Disconnect all GTK signals referencing a wx object which is being destructed
[wxWidgets.git] / src / gtk / window.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/window.cpp
3 // Purpose: wxWindowGTK implementation
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling, Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #ifdef __VMS
14 #define XWarpPointer XWARPPOINTER
15 #endif
16
17 #include "wx/window.h"
18
19 #ifndef WX_PRECOMP
20 #include "wx/log.h"
21 #include "wx/app.h"
22 #include "wx/toplevel.h"
23 #include "wx/dcclient.h"
24 #include "wx/menu.h"
25 #include "wx/settings.h"
26 #include "wx/msgdlg.h"
27 #include "wx/math.h"
28 #endif
29
30 #include "wx/dnd.h"
31 #include "wx/tooltip.h"
32 #include "wx/caret.h"
33 #include "wx/fontutil.h"
34 #include "wx/sysopt.h"
35 #ifdef __WXGTK3__
36 #include "wx/gtk/dc.h"
37 #endif
38
39 #include <ctype.h>
40
41 #include <gtk/gtk.h>
42 #include "wx/gtk/private.h"
43 #include "wx/gtk/private/gtk2-compat.h"
44 #include "wx/gtk/private/event.h"
45 #include "wx/gtk/private/win_gtk.h"
46 using namespace wxGTKImpl;
47
48 #ifdef GDK_WINDOWING_X11
49 #include <gdk/gdkx.h>
50 #include "wx/x11/private/wrapxkb.h"
51 #else
52 typedef guint KeySym;
53 #endif
54
55 #include <gdk/gdkkeysyms.h>
56 #ifdef __WXGTK3__
57 #include <gdk/gdkkeysyms-compat.h>
58 #endif
59
60 // gdk_window_set_composited() is only supported since 2.12
61 #define wxGTK_VERSION_REQUIRED_FOR_COMPOSITING 2,12,0
62 #define wxGTK_HAS_COMPOSITING_SUPPORT GTK_CHECK_VERSION(2,12,0)
63
64 //-----------------------------------------------------------------------------
65 // documentation on internals
66 //-----------------------------------------------------------------------------
67
68 /*
69 I have been asked several times about writing some documentation about
70 the GTK port of wxWidgets, especially its internal structures. Obviously,
71 you cannot understand wxGTK without knowing a little about the GTK, but
72 some more information about what the wxWindow, which is the base class
73 for all other window classes, does seems required as well.
74
75 I)
76
77 What does wxWindow do? It contains the common interface for the following
78 jobs of its descendants:
79
80 1) Define the rudimentary behaviour common to all window classes, such as
81 resizing, intercepting user input (so as to make it possible to use these
82 events for special purposes in a derived class), window names etc.
83
84 2) Provide the possibility to contain and manage children, if the derived
85 class is allowed to contain children, which holds true for those window
86 classes which do not display a native GTK widget. To name them, these
87 classes are wxPanel, wxScrolledWindow, wxDialog, wxFrame. The MDI frame-
88 work classes are a special case and are handled a bit differently from
89 the rest. The same holds true for the wxNotebook class.
90
91 3) Provide the possibility to draw into a client area of a window. This,
92 too, only holds true for classes that do not display a native GTK widget
93 as above.
94
95 4) Provide the entire mechanism for scrolling widgets. This actual inter-
96 face for this is usually in wxScrolledWindow, but the GTK implementation
97 is in this class.
98
99 5) A multitude of helper or extra methods for special purposes, such as
100 Drag'n'Drop, managing validators etc.
101
102 6) Display a border (sunken, raised, simple or none).
103
104 Normally one might expect, that one wxWidgets window would always correspond
105 to one GTK widget. Under GTK, there is no such all-round widget that has all
106 the functionality. Moreover, the GTK defines a client area as a different
107 widget from the actual widget you are handling. Last but not least some
108 special classes (e.g. wxFrame) handle different categories of widgets and
109 still have the possibility to draw something in the client area.
110 It was therefore required to write a special purpose GTK widget, that would
111 represent a client area in the sense of wxWidgets capable to do the jobs
112 2), 3) and 4). I have written this class and it resides in win_gtk.c of
113 this directory.
114
115 All windows must have a widget, with which they interact with other under-
116 lying GTK widgets. It is this widget, e.g. that has to be resized etc and
117 the wxWindow class has a member variable called m_widget which holds a
118 pointer to this widget. When the window class represents a GTK native widget,
119 this is (in most cases) the only GTK widget the class manages. E.g. the
120 wxStaticText class handles only a GtkLabel widget a pointer to which you
121 can find in m_widget (defined in wxWindow)
122
123 When the class has a client area for drawing into and for containing children
124 it has to handle the client area widget (of the type wxPizza, defined in
125 win_gtk.cpp), but there could be any number of widgets, handled by a class.
126 The common rule for all windows is only, that the widget that interacts with
127 the rest of GTK must be referenced in m_widget and all other widgets must be
128 children of this widget on the GTK level. The top-most widget, which also
129 represents the client area, must be in the m_wxwindow field and must be of
130 the type wxPizza.
131
132 As I said, the window classes that display a GTK native widget only have
133 one widget, so in the case of e.g. the wxButton class m_widget holds a
134 pointer to a GtkButton widget. But windows with client areas (for drawing
135 and children) have a m_widget field that is a pointer to a GtkScrolled-
136 Window and a m_wxwindow field that is pointer to a wxPizza and this
137 one is (in the GTK sense) a child of the GtkScrolledWindow.
138
139 If the m_wxwindow field is set, then all input to this widget is inter-
140 cepted and sent to the wxWidgets class. If not, all input to the widget
141 that gets pointed to by m_widget gets intercepted and sent to the class.
142
143 II)
144
145 The design of scrolling in wxWidgets is markedly different from that offered
146 by the GTK itself and therefore we cannot simply take it as it is. In GTK,
147 clicking on a scrollbar belonging to scrolled window will inevitably move
148 the window. In wxWidgets, the scrollbar will only emit an event, send this
149 to (normally) a wxScrolledWindow and that class will call ScrollWindow()
150 which actually moves the window and its sub-windows. Note that wxPizza
151 memorizes how much it has been scrolled but that wxWidgets forgets this
152 so that the two coordinates systems have to be kept in synch. This is done
153 in various places using the pizza->m_scroll_x and pizza->m_scroll_y values.
154
155 III)
156
157 Singularly the most broken code in GTK is the code that is supposed to
158 inform subwindows (child windows) about new positions. Very often, duplicate
159 events are sent without changes in size or position, equally often no
160 events are sent at all (All this is due to a bug in the GtkContainer code
161 which got fixed in GTK 1.2.6). For that reason, wxGTK completely ignores
162 GTK's own system and it simply waits for size events for toplevel windows
163 and then iterates down the respective size events to all window. This has
164 the disadvantage that windows might get size events before the GTK widget
165 actually has the reported size. This doesn't normally pose any problem, but
166 the OpenGL drawing routines rely on correct behaviour. Therefore, I have
167 added the m_nativeSizeEvents flag, which is true only for the OpenGL canvas,
168 i.e. the wxGLCanvas will emit a size event, when (and not before) the X11
169 window that is used for OpenGL output really has that size (as reported by
170 GTK).
171
172 IV)
173
174 If someone at some point of time feels the immense desire to have a look at,
175 change or attempt to optimise the Refresh() logic, this person will need an
176 intimate understanding of what "draw" and "expose" events are and what
177 they are used for, in particular when used in connection with GTK's
178 own windowless widgets. Beware.
179
180 V)
181
182 Cursors, too, have been a constant source of pleasure. The main difficulty
183 is that a GdkWindow inherits a cursor if the programmer sets a new cursor
184 for the parent. To prevent this from doing too much harm, SetCursor calls
185 GTKUpdateCursor, which will recursively re-set the cursors of all child windows.
186 Also don't forget that cursors (like much else) are connected to GdkWindows,
187 not GtkWidgets and that the "window" field of a GtkWidget might very well
188 point to the GdkWindow of the parent widget (-> "window-less widget") and
189 that the two obviously have very different meanings.
190 */
191
192 //-----------------------------------------------------------------------------
193 // data
194 //-----------------------------------------------------------------------------
195
196 // Don't allow event propagation during drag
197 bool g_blockEventsOnDrag;
198 // Don't allow mouse event propagation during scroll
199 bool g_blockEventsOnScroll;
200 extern wxCursor g_globalCursor;
201
202 // mouse capture state: the window which has it and if the mouse is currently
203 // inside it
204 static wxWindowGTK *g_captureWindow = NULL;
205 static bool g_captureWindowHasMouse = false;
206
207 // The window that currently has focus:
208 static wxWindowGTK *gs_currentFocus = NULL;
209 // The window that is scheduled to get focus in the next event loop iteration
210 // or NULL if there's no pending focus change:
211 static wxWindowGTK *gs_pendingFocus = NULL;
212
213 // the window that has deferred focus-out event pending, if any (see
214 // GTKAddDeferredFocusOut() for details)
215 static wxWindowGTK *gs_deferredFocusOut = NULL;
216
217 // global variables because GTK+ DnD want to have the
218 // mouse event that caused it
219 GdkEvent *g_lastMouseEvent = NULL;
220 int g_lastButtonNumber = 0;
221
222 //-----------------------------------------------------------------------------
223 // debug
224 //-----------------------------------------------------------------------------
225
226 // the trace mask used for the focus debugging messages
227 #define TRACE_FOCUS wxT("focus")
228
229 // A handy function to run from under gdb to show information about the given
230 // GtkWidget. Right now it only shows its type, we could enhance it to show
231 // more information later but this is already pretty useful.
232 const char* wxDumpGtkWidget(GtkWidget* w)
233 {
234 static wxString s;
235 s.Printf("GtkWidget %p, type \"%s\"", w, G_OBJECT_TYPE_NAME(w));
236
237 return s.c_str();
238 }
239
240 //-----------------------------------------------------------------------------
241 // "expose_event"/"draw" from m_wxwindow
242 //-----------------------------------------------------------------------------
243
244 extern "C" {
245 #ifdef __WXGTK3__
246 static gboolean draw(GtkWidget*, cairo_t* cr, wxWindow* win)
247 {
248 if (gtk_cairo_should_draw_window(cr, win->GTKGetDrawingWindow()))
249 win->GTKSendPaintEvents(cr);
250
251 return false;
252 }
253 #else // !__WXGTK3__
254 static gboolean expose_event(GtkWidget*, GdkEventExpose* gdk_event, wxWindow* win)
255 {
256 if (gdk_event->window == win->GTKGetDrawingWindow())
257 win->GTKSendPaintEvents(gdk_event->region);
258
259 return false;
260 }
261 #endif // !__WXGTK3__
262 }
263
264 #ifndef __WXUNIVERSAL__
265 //-----------------------------------------------------------------------------
266 // "expose_event"/"draw" from m_wxwindow->parent, for drawing border
267 //-----------------------------------------------------------------------------
268
269 extern "C" {
270 static gboolean
271 #ifdef __WXGTK3__
272 draw_border(GtkWidget*, cairo_t* cr, wxWindow* win)
273 #else
274 draw_border(GtkWidget* widget, GdkEventExpose* gdk_event, wxWindow* win)
275 #endif
276 {
277 #ifdef __WXGTK3__
278 if (!gtk_cairo_should_draw_window(cr, gtk_widget_get_parent_window(win->m_wxwindow)))
279 #else
280 if (gdk_event->window != gtk_widget_get_parent_window(win->m_wxwindow))
281 #endif
282 return false;
283
284 if (!win->IsShown())
285 return false;
286
287 GtkAllocation alloc;
288 gtk_widget_get_allocation(win->m_wxwindow, &alloc);
289 const int x = alloc.x;
290 const int y = alloc.y;
291 const int w = alloc.width;
292 const int h = alloc.height;
293
294 if (w <= 0 || h <= 0)
295 return false;
296
297 if (win->HasFlag(wxBORDER_SIMPLE))
298 {
299 #ifdef __WXGTK3__
300 GtkStyleContext* sc = gtk_widget_get_style_context(win->m_wxwindow);
301 GdkRGBA c;
302 gtk_style_context_get_border_color(sc, GTK_STATE_FLAG_NORMAL, &c);
303 gdk_cairo_set_source_rgba(cr, &c);
304 cairo_set_line_width(cr, 1);
305 cairo_rectangle(cr, x + 0.5, y + 0.5, w - 1, h - 1);
306 cairo_stroke(cr);
307 #else
308 gdk_draw_rectangle(gdk_event->window,
309 gtk_widget_get_style(widget)->black_gc, false, x, y, w - 1, h - 1);
310 #endif
311 }
312 else if (win->HasFlag(wxBORDER_RAISED | wxBORDER_SUNKEN | wxBORDER_THEME))
313 {
314 #ifdef __WXGTK3__
315 //TODO: wxBORDER_RAISED/wxBORDER_SUNKEN
316 GtkStyleContext* sc;
317 if (win->HasFlag(wxHSCROLL | wxVSCROLL))
318 sc = gtk_widget_get_style_context(wxGTKPrivate::GetTreeWidget());
319 else
320 sc = gtk_widget_get_style_context(wxGTKPrivate::GetEntryWidget());
321
322 gtk_render_frame(sc, cr, x, y, w, h);
323 #else // !__WXGTK3__
324 GtkShadowType shadow = GTK_SHADOW_IN;
325 if (win->HasFlag(wxBORDER_RAISED))
326 shadow = GTK_SHADOW_OUT;
327
328 GtkStyle* style;
329 const char* detail;
330 if (win->HasFlag(wxHSCROLL | wxVSCROLL))
331 {
332 style = gtk_widget_get_style(wxGTKPrivate::GetTreeWidget());
333 detail = "viewport";
334 }
335 else
336 {
337 style = gtk_widget_get_style(wxGTKPrivate::GetEntryWidget());
338 detail = "entry";
339 }
340
341 // clip rect is required to avoid painting background
342 // over upper left (w,h) of parent window
343 GdkRectangle clipRect = { x, y, w, h };
344 gtk_paint_shadow(
345 style, gdk_event->window, GTK_STATE_NORMAL,
346 shadow, &clipRect, widget, detail, x, y, w, h);
347 #endif // !__WXGTK3__
348 }
349 return false;
350 }
351 }
352
353 //-----------------------------------------------------------------------------
354 // "parent_set" from m_wxwindow
355 //-----------------------------------------------------------------------------
356
357 extern "C" {
358 static void
359 parent_set(GtkWidget* widget, GtkWidget* old_parent, wxWindow* win)
360 {
361 if (old_parent)
362 {
363 g_signal_handlers_disconnect_by_func(
364 old_parent, (void*)draw_border, win);
365 }
366 GtkWidget* parent = gtk_widget_get_parent(widget);
367 if (parent)
368 {
369 #ifdef __WXGTK3__
370 g_signal_connect_after(parent, "draw", G_CALLBACK(draw_border), win);
371 #else
372 g_signal_connect_after(parent, "expose_event", G_CALLBACK(draw_border), win);
373 #endif
374 }
375 }
376 }
377 #endif // !__WXUNIVERSAL__
378
379 //-----------------------------------------------------------------------------
380 // "key_press_event" from any window
381 //-----------------------------------------------------------------------------
382
383 // set WXTRACE to this to see the key event codes on the console
384 #define TRACE_KEYS wxT("keyevent")
385
386 // translates an X key symbol to WXK_XXX value
387 //
388 // if isChar is true it means that the value returned will be used for EVT_CHAR
389 // event and then we choose the logical WXK_XXX, i.e. '/' for GDK_KP_Divide,
390 // for example, while if it is false it means that the value is going to be
391 // used for KEY_DOWN/UP events and then we translate GDK_KP_Divide to
392 // WXK_NUMPAD_DIVIDE
393 static long wxTranslateKeySymToWXKey(KeySym keysym, bool isChar)
394 {
395 long key_code;
396
397 switch ( keysym )
398 {
399 // Shift, Control and Alt don't generate the CHAR events at all
400 case GDK_Shift_L:
401 case GDK_Shift_R:
402 key_code = isChar ? 0 : WXK_SHIFT;
403 break;
404 case GDK_Control_L:
405 case GDK_Control_R:
406 key_code = isChar ? 0 : WXK_CONTROL;
407 break;
408 case GDK_Meta_L:
409 case GDK_Meta_R:
410 case GDK_Alt_L:
411 case GDK_Alt_R:
412 case GDK_Super_L:
413 case GDK_Super_R:
414 key_code = isChar ? 0 : WXK_ALT;
415 break;
416
417 // neither do the toggle modifies
418 case GDK_Scroll_Lock:
419 key_code = isChar ? 0 : WXK_SCROLL;
420 break;
421
422 case GDK_Caps_Lock:
423 key_code = isChar ? 0 : WXK_CAPITAL;
424 break;
425
426 case GDK_Num_Lock:
427 key_code = isChar ? 0 : WXK_NUMLOCK;
428 break;
429
430
431 // various other special keys
432 case GDK_Menu:
433 key_code = WXK_MENU;
434 break;
435
436 case GDK_Help:
437 key_code = WXK_HELP;
438 break;
439
440 case GDK_BackSpace:
441 key_code = WXK_BACK;
442 break;
443
444 case GDK_ISO_Left_Tab:
445 case GDK_Tab:
446 key_code = WXK_TAB;
447 break;
448
449 case GDK_Linefeed:
450 case GDK_Return:
451 key_code = WXK_RETURN;
452 break;
453
454 case GDK_Clear:
455 key_code = WXK_CLEAR;
456 break;
457
458 case GDK_Pause:
459 key_code = WXK_PAUSE;
460 break;
461
462 case GDK_Select:
463 key_code = WXK_SELECT;
464 break;
465
466 case GDK_Print:
467 key_code = WXK_PRINT;
468 break;
469
470 case GDK_Execute:
471 key_code = WXK_EXECUTE;
472 break;
473
474 case GDK_Escape:
475 key_code = WXK_ESCAPE;
476 break;
477
478 // cursor and other extended keyboard keys
479 case GDK_Delete:
480 key_code = WXK_DELETE;
481 break;
482
483 case GDK_Home:
484 key_code = WXK_HOME;
485 break;
486
487 case GDK_Left:
488 key_code = WXK_LEFT;
489 break;
490
491 case GDK_Up:
492 key_code = WXK_UP;
493 break;
494
495 case GDK_Right:
496 key_code = WXK_RIGHT;
497 break;
498
499 case GDK_Down:
500 key_code = WXK_DOWN;
501 break;
502
503 case GDK_Prior: // == GDK_Page_Up
504 key_code = WXK_PAGEUP;
505 break;
506
507 case GDK_Next: // == GDK_Page_Down
508 key_code = WXK_PAGEDOWN;
509 break;
510
511 case GDK_End:
512 key_code = WXK_END;
513 break;
514
515 case GDK_Begin:
516 key_code = WXK_HOME;
517 break;
518
519 case GDK_Insert:
520 key_code = WXK_INSERT;
521 break;
522
523
524 // numpad keys
525 case GDK_KP_0:
526 case GDK_KP_1:
527 case GDK_KP_2:
528 case GDK_KP_3:
529 case GDK_KP_4:
530 case GDK_KP_5:
531 case GDK_KP_6:
532 case GDK_KP_7:
533 case GDK_KP_8:
534 case GDK_KP_9:
535 key_code = (isChar ? '0' : int(WXK_NUMPAD0)) + keysym - GDK_KP_0;
536 break;
537
538 case GDK_KP_Space:
539 key_code = isChar ? ' ' : int(WXK_NUMPAD_SPACE);
540 break;
541
542 case GDK_KP_Tab:
543 key_code = isChar ? WXK_TAB : WXK_NUMPAD_TAB;
544 break;
545
546 case GDK_KP_Enter:
547 key_code = isChar ? WXK_RETURN : WXK_NUMPAD_ENTER;
548 break;
549
550 case GDK_KP_F1:
551 key_code = isChar ? WXK_F1 : WXK_NUMPAD_F1;
552 break;
553
554 case GDK_KP_F2:
555 key_code = isChar ? WXK_F2 : WXK_NUMPAD_F2;
556 break;
557
558 case GDK_KP_F3:
559 key_code = isChar ? WXK_F3 : WXK_NUMPAD_F3;
560 break;
561
562 case GDK_KP_F4:
563 key_code = isChar ? WXK_F4 : WXK_NUMPAD_F4;
564 break;
565
566 case GDK_KP_Home:
567 key_code = isChar ? WXK_HOME : WXK_NUMPAD_HOME;
568 break;
569
570 case GDK_KP_Left:
571 key_code = isChar ? WXK_LEFT : WXK_NUMPAD_LEFT;
572 break;
573
574 case GDK_KP_Up:
575 key_code = isChar ? WXK_UP : WXK_NUMPAD_UP;
576 break;
577
578 case GDK_KP_Right:
579 key_code = isChar ? WXK_RIGHT : WXK_NUMPAD_RIGHT;
580 break;
581
582 case GDK_KP_Down:
583 key_code = isChar ? WXK_DOWN : WXK_NUMPAD_DOWN;
584 break;
585
586 case GDK_KP_Prior: // == GDK_KP_Page_Up
587 key_code = isChar ? WXK_PAGEUP : WXK_NUMPAD_PAGEUP;
588 break;
589
590 case GDK_KP_Next: // == GDK_KP_Page_Down
591 key_code = isChar ? WXK_PAGEDOWN : WXK_NUMPAD_PAGEDOWN;
592 break;
593
594 case GDK_KP_End:
595 key_code = isChar ? WXK_END : WXK_NUMPAD_END;
596 break;
597
598 case GDK_KP_Begin:
599 key_code = isChar ? WXK_HOME : WXK_NUMPAD_BEGIN;
600 break;
601
602 case GDK_KP_Insert:
603 key_code = isChar ? WXK_INSERT : WXK_NUMPAD_INSERT;
604 break;
605
606 case GDK_KP_Delete:
607 key_code = isChar ? WXK_DELETE : WXK_NUMPAD_DELETE;
608 break;
609
610 case GDK_KP_Equal:
611 key_code = isChar ? '=' : int(WXK_NUMPAD_EQUAL);
612 break;
613
614 case GDK_KP_Multiply:
615 key_code = isChar ? '*' : int(WXK_NUMPAD_MULTIPLY);
616 break;
617
618 case GDK_KP_Add:
619 key_code = isChar ? '+' : int(WXK_NUMPAD_ADD);
620 break;
621
622 case GDK_KP_Separator:
623 // FIXME: what is this?
624 key_code = isChar ? '.' : int(WXK_NUMPAD_SEPARATOR);
625 break;
626
627 case GDK_KP_Subtract:
628 key_code = isChar ? '-' : int(WXK_NUMPAD_SUBTRACT);
629 break;
630
631 case GDK_KP_Decimal:
632 key_code = isChar ? '.' : int(WXK_NUMPAD_DECIMAL);
633 break;
634
635 case GDK_KP_Divide:
636 key_code = isChar ? '/' : int(WXK_NUMPAD_DIVIDE);
637 break;
638
639
640 // function keys
641 case GDK_F1:
642 case GDK_F2:
643 case GDK_F3:
644 case GDK_F4:
645 case GDK_F5:
646 case GDK_F6:
647 case GDK_F7:
648 case GDK_F8:
649 case GDK_F9:
650 case GDK_F10:
651 case GDK_F11:
652 case GDK_F12:
653 key_code = WXK_F1 + keysym - GDK_F1;
654 break;
655
656 default:
657 key_code = 0;
658 }
659
660 return key_code;
661 }
662
663 static inline bool wxIsAsciiKeysym(KeySym ks)
664 {
665 return ks < 256;
666 }
667
668 static void wxFillOtherKeyEventFields(wxKeyEvent& event,
669 wxWindowGTK *win,
670 GdkEventKey *gdk_event)
671 {
672 event.SetTimestamp( gdk_event->time );
673 event.SetId(win->GetId());
674
675 event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0;
676 event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0;
677 event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0;
678 event.m_metaDown = (gdk_event->state & GDK_META_MASK) != 0;
679
680 // At least with current Linux systems, MOD5 corresponds to AltGr key and
681 // we represent it, for consistency with Windows, which really allows to
682 // use Ctrl+Alt as a replacement for AltGr if this key is not present, as a
683 // combination of these two modifiers.
684 if ( gdk_event->state & GDK_MOD5_MASK )
685 {
686 event.m_controlDown =
687 event.m_altDown = true;
688 }
689
690 // Normally we take the state of modifiers directly from the low level GDK
691 // event but unfortunately GDK uses a different convention from MSW for the
692 // key events corresponding to the modifier keys themselves: in it, when
693 // e.g. Shift key is pressed, GDK_SHIFT_MASK is not set while it is set
694 // when Shift is released. Under MSW the situation is exactly reversed and
695 // the modifier corresponding to the key is set when it is pressed and
696 // unset when it is released. To ensure consistent behaviour between
697 // platforms (and because it seems to make slightly more sense, although
698 // arguably both behaviours are reasonable) we follow MSW here.
699 //
700 // Final notice: we set the flags to the desired value instead of just
701 // inverting them because they are not set correctly (i.e. in the same way
702 // as for the real events generated by the user) for wxUIActionSimulator-
703 // produced events and it seems better to keep that class code the same
704 // among all platforms and fix the discrepancy here instead of adding
705 // wxGTK-specific code to wxUIActionSimulator.
706 const bool isPress = gdk_event->type == GDK_KEY_PRESS;
707 switch ( gdk_event->keyval )
708 {
709 case GDK_Shift_L:
710 case GDK_Shift_R:
711 event.m_shiftDown = isPress;
712 break;
713
714 case GDK_Control_L:
715 case GDK_Control_R:
716 event.m_controlDown = isPress;
717 break;
718
719 case GDK_Alt_L:
720 case GDK_Alt_R:
721 event.m_altDown = isPress;
722 break;
723
724 case GDK_Meta_L:
725 case GDK_Meta_R:
726 case GDK_Super_L:
727 case GDK_Super_R:
728 event.m_metaDown = isPress;
729 break;
730 }
731
732 event.m_rawCode = (wxUint32) gdk_event->keyval;
733 event.m_rawFlags = gdk_event->hardware_keycode;
734
735 event.SetEventObject( win );
736 }
737
738
739 static bool
740 wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
741 wxWindowGTK *win,
742 GdkEventKey *gdk_event)
743 {
744 // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string
745 // but only event->keyval which is quite useless to us, so remember
746 // the last character from GDK_KEY_PRESS and reuse it as last resort
747 //
748 // NB: should be MT-safe as we're always called from the main thread only
749 static struct
750 {
751 KeySym keysym;
752 long keycode;
753 } s_lastKeyPress = { 0, 0 };
754
755 KeySym keysym = gdk_event->keyval;
756
757 wxLogTrace(TRACE_KEYS, wxT("Key %s event: keysym = %ld"),
758 event.GetEventType() == wxEVT_KEY_UP ? wxT("release")
759 : wxT("press"),
760 keysym);
761
762 long key_code = wxTranslateKeySymToWXKey(keysym, false /* !isChar */);
763
764 if ( !key_code )
765 {
766 // do we have the translation or is it a plain ASCII character?
767 if ( (gdk_event->length == 1) || wxIsAsciiKeysym(keysym) )
768 {
769 // we should use keysym if it is ASCII as X does some translations
770 // like "I pressed while Control is down" => "Ctrl-I" == "TAB"
771 // which we don't want here (but which we do use for OnChar())
772 if ( !wxIsAsciiKeysym(keysym) )
773 {
774 keysym = (KeySym)gdk_event->string[0];
775 }
776
777 #ifdef GDK_WINDOWING_X11
778 // we want to always get the same key code when the same key is
779 // pressed regardless of the state of the modifiers, i.e. on a
780 // standard US keyboard pressing '5' or '%' ('5' key with
781 // Shift) should result in the same key code in OnKeyDown():
782 // '5' (although OnChar() will get either '5' or '%').
783 //
784 // to do it we first translate keysym to keycode (== scan code)
785 // and then back but always using the lower register
786 Display *dpy = (Display *)wxGetDisplay();
787 KeyCode keycode = XKeysymToKeycode(dpy, keysym);
788
789 wxLogTrace(TRACE_KEYS, wxT("\t-> keycode %d"), keycode);
790
791 #ifdef HAVE_X11_XKBLIB_H
792 KeySym keysymNormalized = XkbKeycodeToKeysym(dpy, keycode, 0, 0);
793 #else
794 KeySym keysymNormalized = XKeycodeToKeysym(dpy, keycode, 0);
795 #endif
796
797 // use the normalized, i.e. lower register, keysym if we've
798 // got one
799 key_code = keysymNormalized ? keysymNormalized : keysym;
800 #else
801 key_code = keysym;
802 #endif
803
804 // as explained above, we want to have lower register key codes
805 // normally but for the letter keys we want to have the upper ones
806 //
807 // NB: don't use XConvertCase() here, we want to do it for letters
808 // only
809 key_code = toupper(key_code);
810 }
811 else // non ASCII key, what to do?
812 {
813 // by default, ignore it
814 key_code = 0;
815
816 // but if we have cached information from the last KEY_PRESS
817 if ( gdk_event->type == GDK_KEY_RELEASE )
818 {
819 // then reuse it
820 if ( keysym == s_lastKeyPress.keysym )
821 {
822 key_code = s_lastKeyPress.keycode;
823 }
824 }
825 }
826
827 if ( gdk_event->type == GDK_KEY_PRESS )
828 {
829 // remember it to be reused for KEY_UP event later
830 s_lastKeyPress.keysym = keysym;
831 s_lastKeyPress.keycode = key_code;
832 }
833 }
834
835 wxLogTrace(TRACE_KEYS, wxT("\t-> wxKeyCode %ld"), key_code);
836
837 // sending unknown key events doesn't really make sense
838 if ( !key_code )
839 return false;
840
841 event.m_keyCode = key_code;
842
843 #if wxUSE_UNICODE
844 event.m_uniChar = gdk_keyval_to_unicode(key_code ? key_code : keysym);
845 if ( !event.m_uniChar && event.m_keyCode <= WXK_DELETE )
846 {
847 // Set Unicode key code to the ASCII equivalent for compatibility. E.g.
848 // let RETURN generate the key event with both key and Unicode key
849 // codes of 13.
850 event.m_uniChar = event.m_keyCode;
851 }
852 #endif // wxUSE_UNICODE
853
854 // now fill all the other fields
855 wxFillOtherKeyEventFields(event, win, gdk_event);
856
857 return true;
858 }
859
860
861 struct wxGtkIMData
862 {
863 GtkIMContext *context;
864 GdkEventKey *lastKeyEvent;
865
866 wxGtkIMData()
867 {
868 context = gtk_im_multicontext_new();
869 lastKeyEvent = NULL;
870 }
871 ~wxGtkIMData()
872 {
873 g_object_unref (context);
874 }
875 };
876
877 namespace
878 {
879
880 // Send wxEVT_CHAR_HOOK event to the parent of the window and return true only
881 // if it was processed (and not skipped).
882 bool SendCharHookEvent(const wxKeyEvent& event, wxWindow *win)
883 {
884 // wxEVT_CHAR_HOOK must be sent to allow the parent windows (e.g. a dialog
885 // which typically closes when Esc key is pressed in any of its controls)
886 // to handle key events in all of its children unless the mouse is captured
887 // in which case we consider that the keyboard should be "captured" too.
888 if ( !g_captureWindow )
889 {
890 wxKeyEvent eventCharHook(wxEVT_CHAR_HOOK, event);
891 if ( win->HandleWindowEvent(eventCharHook)
892 && !event.IsNextEventAllowed() )
893 return true;
894 }
895
896 return false;
897 }
898
899 // Adjust wxEVT_CHAR event key code fields. This function takes care of two
900 // conventions:
901 // (a) Ctrl-letter key presses generate key codes in range 1..26
902 // (b) Unicode key codes are same as key codes for the codes in 1..255 range
903 void AdjustCharEventKeyCodes(wxKeyEvent& event)
904 {
905 const int code = event.m_keyCode;
906
907 // Check for (a) above.
908 if ( event.ControlDown() )
909 {
910 // We intentionally don't use isupper/lower() here, we really need
911 // ASCII letters only as it doesn't make sense to translate any other
912 // ones into this range which has only 26 slots.
913 if ( code >= 'a' && code <= 'z' )
914 event.m_keyCode = code - 'a' + 1;
915 else if ( code >= 'A' && code <= 'Z' )
916 event.m_keyCode = code - 'A' + 1;
917
918 #if wxUSE_UNICODE
919 // Adjust the Unicode equivalent in the same way too.
920 if ( event.m_keyCode != code )
921 event.m_uniChar = event.m_keyCode;
922 #endif // wxUSE_UNICODE
923 }
924
925 #if wxUSE_UNICODE
926 // Check for (b) from above.
927 //
928 // FIXME: Should we do it for key codes up to 255?
929 if ( !event.m_uniChar && code < WXK_DELETE )
930 event.m_uniChar = code;
931 #endif // wxUSE_UNICODE
932 }
933
934 } // anonymous namespace
935
936 // If a widget does not handle a key or mouse event, GTK+ sends it up the
937 // parent chain until it is handled. These events are not supposed to propagate
938 // in wxWidgets, so this code avoids handling them in any parent wxWindow,
939 // while still allowing the event to propagate so things like native keyboard
940 // navigation will work.
941 #define wxPROCESS_EVENT_ONCE(EventType, event) \
942 static EventType eventPrev; \
943 if (memcmp(&eventPrev, event, sizeof(EventType)) == 0) \
944 return false; \
945 eventPrev = *event
946
947 extern "C" {
948 static gboolean
949 gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget),
950 GdkEventKey *gdk_event,
951 wxWindow *win )
952 {
953 if (g_blockEventsOnDrag)
954 return FALSE;
955
956 wxPROCESS_EVENT_ONCE(GdkEventKey, gdk_event);
957
958 wxKeyEvent event( wxEVT_KEY_DOWN );
959 bool ret = false;
960 bool return_after_IM = false;
961
962 if( wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
963 {
964 // Send the CHAR_HOOK event first
965 if ( SendCharHookEvent(event, win) )
966 {
967 // Don't do anything at all with this event any more.
968 return TRUE;
969 }
970
971 // Next check for accelerators.
972 #if wxUSE_ACCEL
973 wxWindowGTK *ancestor = win;
974 while (ancestor)
975 {
976 int command = ancestor->GetAcceleratorTable()->GetCommand( event );
977 if (command != -1)
978 {
979 wxCommandEvent menu_event( wxEVT_COMMAND_MENU_SELECTED, command );
980 ret = ancestor->HandleWindowEvent( menu_event );
981
982 if ( !ret )
983 {
984 // if the accelerator wasn't handled as menu event, try
985 // it as button click (for compatibility with other
986 // platforms):
987 wxCommandEvent button_event( wxEVT_COMMAND_BUTTON_CLICKED, command );
988 ret = ancestor->HandleWindowEvent( button_event );
989 }
990
991 break;
992 }
993 if (ancestor->IsTopLevel())
994 break;
995 ancestor = ancestor->GetParent();
996 }
997 #endif // wxUSE_ACCEL
998
999 // If not an accelerator, then emit KEY_DOWN event
1000 if ( !ret )
1001 ret = win->HandleWindowEvent( event );
1002 }
1003 else
1004 {
1005 // Return after IM processing as we cannot do
1006 // anything with it anyhow.
1007 return_after_IM = true;
1008 }
1009
1010 if (!ret && win->m_imData)
1011 {
1012 win->m_imData->lastKeyEvent = gdk_event;
1013
1014 // We should let GTK+ IM filter key event first. According to GTK+ 2.0 API
1015 // docs, if IM filter returns true, no further processing should be done.
1016 // we should send the key_down event anyway.
1017 bool intercepted_by_IM =
1018 gtk_im_context_filter_keypress(win->m_imData->context, gdk_event) != 0;
1019 win->m_imData->lastKeyEvent = NULL;
1020 if (intercepted_by_IM)
1021 {
1022 wxLogTrace(TRACE_KEYS, wxT("Key event intercepted by IM"));
1023 return TRUE;
1024 }
1025 }
1026
1027 if (return_after_IM)
1028 return FALSE;
1029
1030 // Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
1031 // will only be sent if it is not in an accelerator table.
1032 if (!ret)
1033 {
1034 KeySym keysym = gdk_event->keyval;
1035 // Find key code for EVT_CHAR and EVT_CHAR_HOOK events
1036 long key_code = wxTranslateKeySymToWXKey(keysym, true /* isChar */);
1037 if ( !key_code )
1038 {
1039 if ( wxIsAsciiKeysym(keysym) )
1040 {
1041 // ASCII key
1042 key_code = (unsigned char)keysym;
1043 }
1044 // gdk_event->string is actually deprecated
1045 else if ( gdk_event->length == 1 )
1046 {
1047 key_code = (unsigned char)gdk_event->string[0];
1048 }
1049 }
1050
1051 if ( key_code )
1052 {
1053 wxKeyEvent eventChar(wxEVT_CHAR, event);
1054
1055 wxLogTrace(TRACE_KEYS, wxT("Char event: %ld"), key_code);
1056
1057 eventChar.m_keyCode = key_code;
1058 #if wxUSE_UNICODE
1059 eventChar.m_uniChar = gdk_keyval_to_unicode(key_code);
1060 #endif // wxUSE_UNICODE
1061
1062 AdjustCharEventKeyCodes(eventChar);
1063
1064 ret = win->HandleWindowEvent(eventChar);
1065 }
1066 }
1067
1068 return ret;
1069 }
1070 }
1071
1072 extern "C" {
1073 static void
1074 gtk_wxwindow_commit_cb (GtkIMContext * WXUNUSED(context),
1075 const gchar *str,
1076 wxWindow *window)
1077 {
1078 wxKeyEvent event( wxEVT_CHAR );
1079
1080 // take modifiers, cursor position, timestamp etc. from the last
1081 // key_press_event that was fed into Input Method:
1082 if (window->m_imData->lastKeyEvent)
1083 {
1084 wxFillOtherKeyEventFields(event,
1085 window, window->m_imData->lastKeyEvent);
1086 }
1087 else
1088 {
1089 event.SetEventObject( window );
1090 }
1091
1092 const wxString data(wxGTK_CONV_BACK_SYS(str));
1093 if( data.empty() )
1094 return;
1095
1096 for( wxString::const_iterator pstr = data.begin(); pstr != data.end(); ++pstr )
1097 {
1098 #if wxUSE_UNICODE
1099 event.m_uniChar = *pstr;
1100 // Backward compatible for ISO-8859-1
1101 event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0;
1102 wxLogTrace(TRACE_KEYS, wxT("IM sent character '%c'"), event.m_uniChar);
1103 #else
1104 event.m_keyCode = (char)*pstr;
1105 #endif // wxUSE_UNICODE
1106
1107 AdjustCharEventKeyCodes(event);
1108
1109 window->HandleWindowEvent(event);
1110 }
1111 }
1112 }
1113
1114
1115 //-----------------------------------------------------------------------------
1116 // "key_release_event" from any window
1117 //-----------------------------------------------------------------------------
1118
1119 extern "C" {
1120 static gboolean
1121 gtk_window_key_release_callback( GtkWidget * WXUNUSED(widget),
1122 GdkEventKey *gdk_event,
1123 wxWindowGTK *win )
1124 {
1125 if (g_blockEventsOnDrag)
1126 return FALSE;
1127
1128 wxPROCESS_EVENT_ONCE(GdkEventKey, gdk_event);
1129
1130 wxKeyEvent event( wxEVT_KEY_UP );
1131 if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
1132 {
1133 // unknown key pressed, ignore (the event would be useless anyhow)
1134 return FALSE;
1135 }
1136
1137 return win->GTKProcessEvent(event);
1138 }
1139 }
1140
1141 // ============================================================================
1142 // the mouse events
1143 // ============================================================================
1144
1145 // ----------------------------------------------------------------------------
1146 // mouse event processing helpers
1147 // ----------------------------------------------------------------------------
1148
1149 static void AdjustEventButtonState(wxMouseEvent& event)
1150 {
1151 // GDK reports the old state of the button for a button press event, but
1152 // for compatibility with MSW and common sense we want m_leftDown be TRUE
1153 // for a LEFT_DOWN event, not FALSE, so we will invert
1154 // left/right/middleDown for the corresponding click events
1155
1156 if ((event.GetEventType() == wxEVT_LEFT_DOWN) ||
1157 (event.GetEventType() == wxEVT_LEFT_DCLICK) ||
1158 (event.GetEventType() == wxEVT_LEFT_UP))
1159 {
1160 event.m_leftDown = !event.m_leftDown;
1161 return;
1162 }
1163
1164 if ((event.GetEventType() == wxEVT_MIDDLE_DOWN) ||
1165 (event.GetEventType() == wxEVT_MIDDLE_DCLICK) ||
1166 (event.GetEventType() == wxEVT_MIDDLE_UP))
1167 {
1168 event.m_middleDown = !event.m_middleDown;
1169 return;
1170 }
1171
1172 if ((event.GetEventType() == wxEVT_RIGHT_DOWN) ||
1173 (event.GetEventType() == wxEVT_RIGHT_DCLICK) ||
1174 (event.GetEventType() == wxEVT_RIGHT_UP))
1175 {
1176 event.m_rightDown = !event.m_rightDown;
1177 return;
1178 }
1179
1180 if ((event.GetEventType() == wxEVT_AUX1_DOWN) ||
1181 (event.GetEventType() == wxEVT_AUX1_DCLICK))
1182 {
1183 event.m_aux1Down = true;
1184 return;
1185 }
1186
1187 if ((event.GetEventType() == wxEVT_AUX2_DOWN) ||
1188 (event.GetEventType() == wxEVT_AUX2_DCLICK))
1189 {
1190 event.m_aux2Down = true;
1191 return;
1192 }
1193 }
1194
1195 // find the window to send the mouse event to
1196 static
1197 wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
1198 {
1199 wxCoord xx = x;
1200 wxCoord yy = y;
1201
1202 if (win->m_wxwindow)
1203 {
1204 wxPizza* pizza = WX_PIZZA(win->m_wxwindow);
1205 xx += pizza->m_scroll_x;
1206 yy += pizza->m_scroll_y;
1207 }
1208
1209 wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
1210 while (node)
1211 {
1212 wxWindow* child = static_cast<wxWindow*>(node->GetData());
1213
1214 node = node->GetNext();
1215 if (!child->IsShown())
1216 continue;
1217
1218 if (child->GTKIsTransparentForMouse())
1219 {
1220 // wxStaticBox is transparent in the box itself
1221 int xx1 = child->m_x;
1222 int yy1 = child->m_y;
1223 int xx2 = child->m_x + child->m_width;
1224 int yy2 = child->m_y + child->m_height;
1225
1226 // left
1227 if (((xx >= xx1) && (xx <= xx1+10) && (yy >= yy1) && (yy <= yy2)) ||
1228 // right
1229 ((xx >= xx2-10) && (xx <= xx2) && (yy >= yy1) && (yy <= yy2)) ||
1230 // top
1231 ((xx >= xx1) && (xx <= xx2) && (yy >= yy1) && (yy <= yy1+10)) ||
1232 // bottom
1233 ((xx >= xx1) && (xx <= xx2) && (yy >= yy2-1) && (yy <= yy2)))
1234 {
1235 win = child;
1236 x -= child->m_x;
1237 y -= child->m_y;
1238 break;
1239 }
1240
1241 }
1242 else
1243 {
1244 if ((child->m_wxwindow == NULL) &&
1245 win->IsClientAreaChild(child) &&
1246 (child->m_x <= xx) &&
1247 (child->m_y <= yy) &&
1248 (child->m_x+child->m_width >= xx) &&
1249 (child->m_y+child->m_height >= yy))
1250 {
1251 win = child;
1252 x -= child->m_x;
1253 y -= child->m_y;
1254 break;
1255 }
1256 }
1257 }
1258
1259 return win;
1260 }
1261
1262 // ----------------------------------------------------------------------------
1263 // common event handlers helpers
1264 // ----------------------------------------------------------------------------
1265
1266 bool wxWindowGTK::GTKProcessEvent(wxEvent& event) const
1267 {
1268 // nothing special at this level
1269 return HandleWindowEvent(event);
1270 }
1271
1272 bool wxWindowGTK::GTKShouldIgnoreEvent() const
1273 {
1274 return g_blockEventsOnDrag;
1275 }
1276
1277 int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny *event) const
1278 {
1279 if (g_blockEventsOnDrag)
1280 return TRUE;
1281 if (g_blockEventsOnScroll)
1282 return TRUE;
1283
1284 if (!GTKIsOwnWindow(event->window))
1285 return FALSE;
1286
1287 return -1;
1288 }
1289
1290 // overloads for all GDK event types we use here: we need to have this as
1291 // GdkEventXXX can't be implicitly cast to GdkEventAny even if it, in fact,
1292 // derives from it in the sense that the structs have the same layout
1293 #define wxDEFINE_COMMON_PROLOGUE_OVERLOAD(T) \
1294 static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win) \
1295 { \
1296 return win->GTKCallbackCommonPrologue((GdkEventAny *)event); \
1297 }
1298
1299 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventButton)
1300 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion)
1301 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing)
1302
1303 #undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD
1304
1305 #define wxCOMMON_CALLBACK_PROLOGUE(event, win) \
1306 const int rc = wxGtkCallbackCommonPrologue(event, win); \
1307 if ( rc != -1 ) \
1308 return rc
1309
1310 // all event handlers must have C linkage as they're called from GTK+ C code
1311 extern "C"
1312 {
1313
1314 //-----------------------------------------------------------------------------
1315 // "button_press_event"
1316 //-----------------------------------------------------------------------------
1317
1318 static gboolean
1319 gtk_window_button_press_callback( GtkWidget* WXUNUSED_IN_GTK3(widget),
1320 GdkEventButton *gdk_event,
1321 wxWindowGTK *win )
1322 {
1323 wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
1324
1325 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1326
1327 g_lastButtonNumber = gdk_event->button;
1328
1329 wxEventType event_type;
1330 wxEventType down;
1331 wxEventType dclick;
1332 switch (gdk_event->button)
1333 {
1334 case 1:
1335 down = wxEVT_LEFT_DOWN;
1336 dclick = wxEVT_LEFT_DCLICK;
1337 break;
1338 case 2:
1339 down = wxEVT_MIDDLE_DOWN;
1340 dclick = wxEVT_MIDDLE_DCLICK;
1341 break;
1342 case 3:
1343 down = wxEVT_RIGHT_DOWN;
1344 dclick = wxEVT_RIGHT_DCLICK;
1345 break;
1346 case 8:
1347 down = wxEVT_AUX1_DOWN;
1348 dclick = wxEVT_AUX1_DCLICK;
1349 break;
1350 case 9:
1351 down = wxEVT_AUX2_DOWN;
1352 dclick = wxEVT_AUX2_DCLICK;
1353 break;
1354 default:
1355 return false;
1356 }
1357 switch (gdk_event->type)
1358 {
1359 case GDK_BUTTON_PRESS:
1360 event_type = down;
1361 // GDK sends surplus button down events
1362 // before a double click event. We
1363 // need to filter these out.
1364 if (win->m_wxwindow)
1365 {
1366 GdkEvent* peek_event = gdk_event_peek();
1367 if (peek_event)
1368 {
1369 const GdkEventType peek_event_type = peek_event->type;
1370 gdk_event_free(peek_event);
1371 if (peek_event_type == GDK_2BUTTON_PRESS ||
1372 peek_event_type == GDK_3BUTTON_PRESS)
1373 {
1374 return true;
1375 }
1376 }
1377 }
1378 break;
1379 case GDK_2BUTTON_PRESS:
1380 event_type = dclick;
1381 #ifndef __WXGTK3__
1382 if (gdk_event->button >= 1 && gdk_event->button <= 3)
1383 {
1384 // Reset GDK internal timestamp variables in order to disable GDK
1385 // triple click events. GDK will then next time believe no button has
1386 // been clicked just before, and send a normal button click event.
1387 GdkDisplay* display = gtk_widget_get_display(widget);
1388 display->button_click_time[1] = 0;
1389 display->button_click_time[0] = 0;
1390 }
1391 #endif // !__WXGTK3__
1392 break;
1393 // we shouldn't get triple clicks at all for GTK2 because we
1394 // suppress them artificially using the code above but we still
1395 // should map them to something for GTK3 and not just ignore them
1396 // as this would lose clicks
1397 case GDK_3BUTTON_PRESS:
1398 event_type = down;
1399 break;
1400 default:
1401 return false;
1402 }
1403
1404 g_lastMouseEvent = (GdkEvent*) gdk_event;
1405
1406 wxMouseEvent event( event_type );
1407 InitMouseEvent( win, event, gdk_event );
1408
1409 AdjustEventButtonState(event);
1410
1411 // find the correct window to send the event to: it may be a different one
1412 // from the one which got it at GTK+ level because some controls don't have
1413 // their own X window and thus cannot get any events.
1414 if ( !g_captureWindow )
1415 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1416
1417 // reset the event object and id in case win changed.
1418 event.SetEventObject( win );
1419 event.SetId( win->GetId() );
1420
1421 bool ret = win->GTKProcessEvent( event );
1422 g_lastMouseEvent = NULL;
1423 if ( ret )
1424 return TRUE;
1425
1426 if ((event_type == wxEVT_LEFT_DOWN) && !win->IsOfStandardClass() &&
1427 (gs_currentFocus != win) /* && win->IsFocusable() */)
1428 {
1429 win->SetFocus();
1430 }
1431
1432 if (event_type == wxEVT_RIGHT_DOWN)
1433 {
1434 // generate a "context menu" event: this is similar to right mouse
1435 // click under many GUIs except that it is generated differently
1436 // (right up under MSW, ctrl-click under Mac, right down here) and
1437 //
1438 // (a) it's a command event and so is propagated to the parent
1439 // (b) under some ports it can be generated from kbd too
1440 // (c) it uses screen coords (because of (a))
1441 wxContextMenuEvent evtCtx(
1442 wxEVT_CONTEXT_MENU,
1443 win->GetId(),
1444 win->ClientToScreen(event.GetPosition()));
1445 evtCtx.SetEventObject(win);
1446 return win->GTKProcessEvent(evtCtx);
1447 }
1448
1449 return FALSE;
1450 }
1451
1452 //-----------------------------------------------------------------------------
1453 // "button_release_event"
1454 //-----------------------------------------------------------------------------
1455
1456 static gboolean
1457 gtk_window_button_release_callback( GtkWidget *WXUNUSED(widget),
1458 GdkEventButton *gdk_event,
1459 wxWindowGTK *win )
1460 {
1461 wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
1462
1463 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1464
1465 g_lastButtonNumber = 0;
1466
1467 wxEventType event_type = wxEVT_NULL;
1468
1469 switch (gdk_event->button)
1470 {
1471 case 1:
1472 event_type = wxEVT_LEFT_UP;
1473 break;
1474
1475 case 2:
1476 event_type = wxEVT_MIDDLE_UP;
1477 break;
1478
1479 case 3:
1480 event_type = wxEVT_RIGHT_UP;
1481 break;
1482
1483 case 8:
1484 event_type = wxEVT_AUX1_UP;
1485 break;
1486
1487 case 9:
1488 event_type = wxEVT_AUX2_UP;
1489 break;
1490
1491 default:
1492 // unknown button, don't process
1493 return FALSE;
1494 }
1495
1496 g_lastMouseEvent = (GdkEvent*) gdk_event;
1497
1498 wxMouseEvent event( event_type );
1499 InitMouseEvent( win, event, gdk_event );
1500
1501 AdjustEventButtonState(event);
1502
1503 if ( !g_captureWindow )
1504 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1505
1506 // reset the event object and id in case win changed.
1507 event.SetEventObject( win );
1508 event.SetId( win->GetId() );
1509
1510 bool ret = win->GTKProcessEvent(event);
1511
1512 g_lastMouseEvent = NULL;
1513
1514 return ret;
1515 }
1516
1517 //-----------------------------------------------------------------------------
1518 // "motion_notify_event"
1519 //-----------------------------------------------------------------------------
1520
1521 static gboolean
1522 gtk_window_motion_notify_callback( GtkWidget * WXUNUSED(widget),
1523 GdkEventMotion *gdk_event,
1524 wxWindowGTK *win )
1525 {
1526 wxPROCESS_EVENT_ONCE(GdkEventMotion, gdk_event);
1527
1528 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1529
1530 if (gdk_event->is_hint)
1531 {
1532 int x = 0;
1533 int y = 0;
1534 GdkModifierType state;
1535 gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
1536 gdk_event->x = x;
1537 gdk_event->y = y;
1538 }
1539
1540 g_lastMouseEvent = (GdkEvent*) gdk_event;
1541
1542 wxMouseEvent event( wxEVT_MOTION );
1543 InitMouseEvent(win, event, gdk_event);
1544
1545 if ( g_captureWindow )
1546 {
1547 // synthesise a mouse enter or leave event if needed
1548 GdkWindow *winUnderMouse = gdk_window_at_pointer(NULL, NULL);
1549 // This seems to be necessary and actually been added to
1550 // GDK itself in version 2.0.X
1551 gdk_flush();
1552
1553 bool hasMouse = winUnderMouse == gdk_event->window;
1554 if ( hasMouse != g_captureWindowHasMouse )
1555 {
1556 // the mouse changed window
1557 g_captureWindowHasMouse = hasMouse;
1558
1559 wxMouseEvent eventM(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW
1560 : wxEVT_LEAVE_WINDOW);
1561 InitMouseEvent(win, eventM, gdk_event);
1562 eventM.SetEventObject(win);
1563 win->GTKProcessEvent(eventM);
1564 }
1565 }
1566 else // no capture
1567 {
1568 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1569
1570 // reset the event object and id in case win changed.
1571 event.SetEventObject( win );
1572 event.SetId( win->GetId() );
1573 }
1574
1575 if ( !g_captureWindow )
1576 {
1577 wxSetCursorEvent cevent( event.m_x, event.m_y );
1578 if (win->GTKProcessEvent( cevent ))
1579 {
1580 win->SetCursor( cevent.GetCursor() );
1581 }
1582 }
1583
1584 bool ret = win->GTKProcessEvent(event);
1585
1586 g_lastMouseEvent = NULL;
1587
1588 return ret;
1589 }
1590
1591 //-----------------------------------------------------------------------------
1592 // "scroll_event" (mouse wheel event)
1593 //-----------------------------------------------------------------------------
1594
1595 static gboolean
1596 window_scroll_event_hscrollbar(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
1597 {
1598 if (gdk_event->direction != GDK_SCROLL_LEFT &&
1599 gdk_event->direction != GDK_SCROLL_RIGHT)
1600 {
1601 return false;
1602 }
1603
1604 GtkRange *range = win->m_scrollBar[wxWindow::ScrollDir_Horz];
1605
1606 if (range && gtk_widget_get_visible(GTK_WIDGET(range)))
1607 {
1608 GtkAdjustment* adj = gtk_range_get_adjustment(range);
1609 double delta = gtk_adjustment_get_step_increment(adj) * 3;
1610 if (gdk_event->direction == GDK_SCROLL_LEFT)
1611 delta = -delta;
1612
1613 gtk_range_set_value(range, gtk_adjustment_get_value(adj) + delta);
1614
1615 return TRUE;
1616 }
1617
1618 return FALSE;
1619 }
1620
1621 static gboolean
1622 window_scroll_event(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
1623 {
1624 wxMouseEvent event(wxEVT_MOUSEWHEEL);
1625 InitMouseEvent(win, event, gdk_event);
1626
1627 // FIXME: Get these values from GTK or GDK
1628 event.m_linesPerAction = 3;
1629 event.m_wheelDelta = 120;
1630
1631 // Determine the scroll direction.
1632 switch (gdk_event->direction)
1633 {
1634 case GDK_SCROLL_UP:
1635 case GDK_SCROLL_RIGHT:
1636 event.m_wheelRotation = 120;
1637 break;
1638
1639 case GDK_SCROLL_DOWN:
1640 case GDK_SCROLL_LEFT:
1641 event.m_wheelRotation = -120;
1642 break;
1643
1644 default:
1645 return false; // Unknown/unhandled direction
1646 }
1647
1648 // And the scroll axis.
1649 switch (gdk_event->direction)
1650 {
1651 case GDK_SCROLL_UP:
1652 case GDK_SCROLL_DOWN:
1653 event.m_wheelAxis = wxMOUSE_WHEEL_VERTICAL;
1654 break;
1655
1656 case GDK_SCROLL_LEFT:
1657 case GDK_SCROLL_RIGHT:
1658 event.m_wheelAxis = wxMOUSE_WHEEL_HORIZONTAL;
1659 break;
1660 }
1661
1662 if (win->GTKProcessEvent(event))
1663 return TRUE;
1664
1665 GtkRange *range = win->m_scrollBar[wxWindow::ScrollDir_Vert];
1666
1667 if (range && gtk_widget_get_visible(GTK_WIDGET(range)))
1668 {
1669 GtkAdjustment* adj = gtk_range_get_adjustment(range);
1670 double delta = gtk_adjustment_get_step_increment(adj) * 3;
1671 if (gdk_event->direction == GDK_SCROLL_UP)
1672 delta = -delta;
1673
1674 gtk_range_set_value(range, gtk_adjustment_get_value(adj) + delta);
1675
1676 return TRUE;
1677 }
1678
1679 return FALSE;
1680 }
1681
1682 //-----------------------------------------------------------------------------
1683 // "popup-menu"
1684 //-----------------------------------------------------------------------------
1685
1686 static gboolean wxgtk_window_popup_menu_callback(GtkWidget*, wxWindowGTK* win)
1687 {
1688 wxContextMenuEvent event(wxEVT_CONTEXT_MENU, win->GetId(), wxPoint(-1, -1));
1689 event.SetEventObject(win);
1690 return win->GTKProcessEvent(event);
1691 }
1692
1693 //-----------------------------------------------------------------------------
1694 // "focus_in_event"
1695 //-----------------------------------------------------------------------------
1696
1697 static gboolean
1698 gtk_window_focus_in_callback( GtkWidget * WXUNUSED(widget),
1699 GdkEventFocus *WXUNUSED(event),
1700 wxWindowGTK *win )
1701 {
1702 return win->GTKHandleFocusIn();
1703 }
1704
1705 //-----------------------------------------------------------------------------
1706 // "focus_out_event"
1707 //-----------------------------------------------------------------------------
1708
1709 static gboolean
1710 gtk_window_focus_out_callback( GtkWidget * WXUNUSED(widget),
1711 GdkEventFocus * WXUNUSED(gdk_event),
1712 wxWindowGTK *win )
1713 {
1714 return win->GTKHandleFocusOut();
1715 }
1716
1717 //-----------------------------------------------------------------------------
1718 // "focus"
1719 //-----------------------------------------------------------------------------
1720
1721 static gboolean
1722 wx_window_focus_callback(GtkWidget *widget,
1723 GtkDirectionType WXUNUSED(direction),
1724 wxWindowGTK *win)
1725 {
1726 // the default handler for focus signal in GtkScrolledWindow sets
1727 // focus to the window itself even if it doesn't accept focus, i.e. has no
1728 // GTK_CAN_FOCUS in its style -- work around this by forcibly preventing
1729 // the signal from reaching gtk_scrolled_window_focus() if we don't have
1730 // any children which might accept focus (we know we don't accept the focus
1731 // ourselves as this signal is only connected in this case)
1732 if ( win->GetChildren().empty() )
1733 g_signal_stop_emission_by_name(widget, "focus");
1734
1735 // we didn't change the focus
1736 return FALSE;
1737 }
1738
1739 //-----------------------------------------------------------------------------
1740 // "enter_notify_event"
1741 //-----------------------------------------------------------------------------
1742
1743 static gboolean
1744 gtk_window_enter_callback( GtkWidget *widget,
1745 GdkEventCrossing *gdk_event,
1746 wxWindowGTK *win )
1747 {
1748 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1749
1750 // Event was emitted after a grab
1751 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
1752
1753 int x = 0;
1754 int y = 0;
1755 GdkModifierType state = (GdkModifierType)0;
1756
1757 gdk_window_get_pointer(gtk_widget_get_window(widget), &x, &y, &state);
1758
1759 wxMouseEvent event( wxEVT_ENTER_WINDOW );
1760 InitMouseEvent(win, event, gdk_event);
1761 wxPoint pt = win->GetClientAreaOrigin();
1762 event.m_x = x + pt.x;
1763 event.m_y = y + pt.y;
1764
1765 if ( !g_captureWindow )
1766 {
1767 wxSetCursorEvent cevent( event.m_x, event.m_y );
1768 if (win->GTKProcessEvent( cevent ))
1769 {
1770 win->SetCursor( cevent.GetCursor() );
1771 }
1772 }
1773
1774 return win->GTKProcessEvent(event);
1775 }
1776
1777 //-----------------------------------------------------------------------------
1778 // "leave_notify_event"
1779 //-----------------------------------------------------------------------------
1780
1781 static gboolean
1782 gtk_window_leave_callback( GtkWidget *widget,
1783 GdkEventCrossing *gdk_event,
1784 wxWindowGTK *win )
1785 {
1786 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1787
1788 // Event was emitted after an ungrab
1789 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
1790
1791 wxMouseEvent event( wxEVT_LEAVE_WINDOW );
1792
1793 int x = 0;
1794 int y = 0;
1795 GdkModifierType state = (GdkModifierType)0;
1796
1797 gdk_window_get_pointer(gtk_widget_get_window(widget), &x, &y, &state);
1798
1799 InitMouseEvent(win, event, gdk_event);
1800
1801 return win->GTKProcessEvent(event);
1802 }
1803
1804 //-----------------------------------------------------------------------------
1805 // "value_changed" from scrollbar
1806 //-----------------------------------------------------------------------------
1807
1808 static void
1809 gtk_scrollbar_value_changed(GtkRange* range, wxWindow* win)
1810 {
1811 wxEventType eventType = win->GTKGetScrollEventType(range);
1812 if (eventType != wxEVT_NULL)
1813 {
1814 // Convert scroll event type to scrollwin event type
1815 eventType += wxEVT_SCROLLWIN_TOP - wxEVT_SCROLL_TOP;
1816
1817 // find the scrollbar which generated the event
1818 wxWindowGTK::ScrollDir dir = win->ScrollDirFromRange(range);
1819
1820 // generate the corresponding wx event
1821 const int orient = wxWindow::OrientFromScrollDir(dir);
1822 wxScrollWinEvent event(eventType, win->GetScrollPos(orient), orient);
1823 event.SetEventObject(win);
1824
1825 win->GTKProcessEvent(event);
1826 }
1827 }
1828
1829 //-----------------------------------------------------------------------------
1830 // "button_press_event" from scrollbar
1831 //-----------------------------------------------------------------------------
1832
1833 static gboolean
1834 gtk_scrollbar_button_press_event(GtkRange*, GdkEventButton*, wxWindow* win)
1835 {
1836 g_blockEventsOnScroll = true;
1837 win->m_mouseButtonDown = true;
1838
1839 return false;
1840 }
1841
1842 //-----------------------------------------------------------------------------
1843 // "event_after" from scrollbar
1844 //-----------------------------------------------------------------------------
1845
1846 static void
1847 gtk_scrollbar_event_after(GtkRange* range, GdkEvent* event, wxWindow* win)
1848 {
1849 if (event->type == GDK_BUTTON_RELEASE)
1850 {
1851 g_signal_handlers_block_by_func(range, (void*)gtk_scrollbar_event_after, win);
1852
1853 const int orient = wxWindow::OrientFromScrollDir(
1854 win->ScrollDirFromRange(range));
1855 wxScrollWinEvent evt(wxEVT_SCROLLWIN_THUMBRELEASE,
1856 win->GetScrollPos(orient), orient);
1857 evt.SetEventObject(win);
1858 win->GTKProcessEvent(evt);
1859 }
1860 }
1861
1862 //-----------------------------------------------------------------------------
1863 // "button_release_event" from scrollbar
1864 //-----------------------------------------------------------------------------
1865
1866 static gboolean
1867 gtk_scrollbar_button_release_event(GtkRange* range, GdkEventButton*, wxWindow* win)
1868 {
1869 g_blockEventsOnScroll = false;
1870 win->m_mouseButtonDown = false;
1871 // If thumb tracking
1872 if (win->m_isScrolling)
1873 {
1874 win->m_isScrolling = false;
1875 // Hook up handler to send thumb release event after this emission is finished.
1876 // To allow setting scroll position from event handler, sending event must
1877 // be deferred until after the GtkRange handler for this signal has run
1878 g_signal_handlers_unblock_by_func(range, (void*)gtk_scrollbar_event_after, win);
1879 }
1880
1881 return false;
1882 }
1883
1884 //-----------------------------------------------------------------------------
1885 // "realize" from m_widget
1886 //-----------------------------------------------------------------------------
1887
1888 static void
1889 gtk_window_realized_callback(GtkWidget* WXUNUSED(widget), wxWindowGTK* win)
1890 {
1891 win->GTKHandleRealized();
1892 }
1893
1894 //-----------------------------------------------------------------------------
1895 // "size_allocate" from m_wxwindow or m_widget
1896 //-----------------------------------------------------------------------------
1897
1898 static void
1899 size_allocate(GtkWidget*, GtkAllocation* alloc, wxWindow* win)
1900 {
1901 int w = alloc->width;
1902 int h = alloc->height;
1903 if (win->m_wxwindow)
1904 {
1905 GtkBorder border;
1906 WX_PIZZA(win->m_wxwindow)->get_border(border);
1907 w -= border.left + border.right;
1908 h -= border.top + border.bottom;
1909 if (w < 0) w = 0;
1910 if (h < 0) h = 0;
1911 }
1912 if (win->m_oldClientWidth != w || win->m_oldClientHeight != h)
1913 {
1914 win->m_oldClientWidth = w;
1915 win->m_oldClientHeight = h;
1916 // this callback can be connected to m_wxwindow,
1917 // so always get size from m_widget->allocation
1918 GtkAllocation a;
1919 gtk_widget_get_allocation(win->m_widget, &a);
1920 win->m_width = a.width;
1921 win->m_height = a.height;
1922 if (!win->m_nativeSizeEvent)
1923 {
1924 wxSizeEvent event(win->GetSize(), win->GetId());
1925 event.SetEventObject(win);
1926 win->GTKProcessEvent(event);
1927 }
1928 }
1929 }
1930
1931 //-----------------------------------------------------------------------------
1932 // "grab_broken"
1933 //-----------------------------------------------------------------------------
1934
1935 #if GTK_CHECK_VERSION(2, 8, 0)
1936 static gboolean
1937 gtk_window_grab_broken( GtkWidget*,
1938 GdkEventGrabBroken *event,
1939 wxWindow *win )
1940 {
1941 // Mouse capture has been lost involuntarily, notify the application
1942 if(!event->keyboard && wxWindow::GetCapture() == win)
1943 {
1944 wxMouseCaptureLostEvent evt( win->GetId() );
1945 evt.SetEventObject( win );
1946 win->HandleWindowEvent( evt );
1947 }
1948 return false;
1949 }
1950 #endif
1951
1952 //-----------------------------------------------------------------------------
1953 // "style_set"/"style_updated"
1954 //-----------------------------------------------------------------------------
1955
1956 #ifdef __WXGTK3__
1957 static void style_updated(GtkWidget*, wxWindow* win)
1958 #else
1959 static void style_updated(GtkWidget*, GtkStyle*, wxWindow* win)
1960 #endif
1961 {
1962 if (win->IsTopLevel())
1963 {
1964 wxSysColourChangedEvent event;
1965 event.SetEventObject(win);
1966 win->GTKProcessEvent(event);
1967 }
1968 else
1969 {
1970 // Border width could change, which will change client size.
1971 // Make sure size event occurs for this
1972 win->m_oldClientWidth = 0;
1973 }
1974 }
1975
1976 //-----------------------------------------------------------------------------
1977 // "unrealize"
1978 //-----------------------------------------------------------------------------
1979
1980 static void unrealize(GtkWidget*, wxWindow* win)
1981 {
1982 win->GTKHandleUnrealize();
1983 }
1984
1985 } // extern "C"
1986
1987 void wxWindowGTK::GTKHandleRealized()
1988 {
1989 if (IsFrozen())
1990 DoFreeze();
1991
1992 if (m_imData)
1993 {
1994 gtk_im_context_set_client_window
1995 (
1996 m_imData->context,
1997 m_wxwindow ? GTKGetDrawingWindow()
1998 : gtk_widget_get_window(m_widget)
1999 );
2000 }
2001
2002 // Use composited window if background is transparent, if supported.
2003 if (m_backgroundStyle == wxBG_STYLE_TRANSPARENT)
2004 {
2005 #if wxGTK_HAS_COMPOSITING_SUPPORT
2006 if (IsTransparentBackgroundSupported())
2007 {
2008 GdkWindow* const window = GTKGetDrawingWindow();
2009 if (window)
2010 gdk_window_set_composited(window, true);
2011 }
2012 else
2013 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
2014 {
2015 // We revert to erase mode if transparency is not supported
2016 m_backgroundStyle = wxBG_STYLE_ERASE;
2017 }
2018 }
2019
2020
2021 // We cannot set colours and fonts before the widget
2022 // been realized, so we do this directly after realization
2023 // or otherwise in idle time
2024
2025 if (m_needsStyleChange)
2026 {
2027 SetBackgroundStyle(GetBackgroundStyle());
2028 m_needsStyleChange = false;
2029 }
2030
2031 wxWindowCreateEvent event(static_cast<wxWindow*>(this));
2032 event.SetEventObject( this );
2033 GTKProcessEvent( event );
2034
2035 GTKUpdateCursor(true, false);
2036
2037 if (m_wxwindow &&
2038 (IsTopLevel() || HasFlag(wxBORDER_RAISED | wxBORDER_SUNKEN | wxBORDER_THEME)))
2039 {
2040 // attaching to style changed signal after realization avoids initial
2041 // changes we don't care about
2042 const gchar *detailed_signal =
2043 #ifdef __WXGTK3__
2044 "style_updated";
2045 #else
2046 "style_set";
2047 #endif
2048 g_signal_connect(m_wxwindow,
2049 detailed_signal,
2050 G_CALLBACK(style_updated), this);
2051 }
2052 }
2053
2054 void wxWindowGTK::GTKHandleUnrealize()
2055 {
2056 // unrealizing a frozen window seems to have some lingering effect
2057 // preventing updates to the affected area
2058 if (IsFrozen())
2059 DoThaw();
2060
2061 if (m_wxwindow)
2062 {
2063 if (m_imData)
2064 gtk_im_context_set_client_window(m_imData->context, NULL);
2065
2066 g_signal_handlers_disconnect_by_func(
2067 m_wxwindow, (void*)style_updated, this);
2068 }
2069 }
2070
2071 // ----------------------------------------------------------------------------
2072 // this wxWindowBase function is implemented here (in platform-specific file)
2073 // because it is static and so couldn't be made virtual
2074 // ----------------------------------------------------------------------------
2075
2076 wxWindow *wxWindowBase::DoFindFocus()
2077 {
2078 // For compatibility with wxMSW, pretend that showing a popup menu doesn't
2079 // change the focus and that it remains on the window showing it, even
2080 // though the real focus does change in GTK.
2081 extern wxMenu *wxCurrentPopupMenu;
2082 if ( wxCurrentPopupMenu )
2083 return wxCurrentPopupMenu->GetInvokingWindow();
2084
2085 wxWindowGTK *focus = gs_pendingFocus ? gs_pendingFocus : gs_currentFocus;
2086 // the cast is necessary when we compile in wxUniversal mode
2087 return static_cast<wxWindow*>(focus);
2088 }
2089
2090 void wxWindowGTK::AddChildGTK(wxWindowGTK* child)
2091 {
2092 wxASSERT_MSG(m_wxwindow, "Cannot add a child to a window without a client area");
2093
2094 // the window might have been scrolled already, we
2095 // have to adapt the position
2096 wxPizza* pizza = WX_PIZZA(m_wxwindow);
2097 child->m_x += pizza->m_scroll_x;
2098 child->m_y += pizza->m_scroll_y;
2099
2100 pizza->put(child->m_widget,
2101 child->m_x, child->m_y, child->m_width, child->m_height);
2102 }
2103
2104 //-----------------------------------------------------------------------------
2105 // global functions
2106 //-----------------------------------------------------------------------------
2107
2108 wxWindow *wxGetActiveWindow()
2109 {
2110 return wxWindow::FindFocus();
2111 }
2112
2113
2114 // Under Unix this is implemented using X11 functions in utilsx11.cpp but we
2115 // need to have this function under Windows too, so provide at least a stub.
2116 #ifndef GDK_WINDOWING_X11
2117 bool wxGetKeyState(wxKeyCode WXUNUSED(key))
2118 {
2119 wxFAIL_MSG(wxS("Not implemented under Windows"));
2120 return false;
2121 }
2122 #endif // __WINDOWS__
2123
2124 static void GetMouseState(int& x, int& y, GdkModifierType& mask)
2125 {
2126 wxWindow* tlw = NULL;
2127 if (!wxTopLevelWindows.empty())
2128 tlw = wxTopLevelWindows.front();
2129 GdkDisplay* display;
2130 if (tlw && tlw->m_widget)
2131 display = gtk_widget_get_display(tlw->m_widget);
2132 else
2133 display = gdk_display_get_default();
2134 gdk_display_get_pointer(display, NULL, &x, &y, &mask);
2135 }
2136
2137 wxMouseState wxGetMouseState()
2138 {
2139 wxMouseState ms;
2140
2141 gint x;
2142 gint y;
2143 GdkModifierType mask;
2144
2145 GetMouseState(x, y, mask);
2146
2147 ms.SetX(x);
2148 ms.SetY(y);
2149 ms.SetLeftDown((mask & GDK_BUTTON1_MASK) != 0);
2150 ms.SetMiddleDown((mask & GDK_BUTTON2_MASK) != 0);
2151 ms.SetRightDown((mask & GDK_BUTTON3_MASK) != 0);
2152 // see the comment in InitMouseEvent()
2153 ms.SetAux1Down((mask & GDK_BUTTON4_MASK) != 0);
2154 ms.SetAux2Down((mask & GDK_BUTTON5_MASK) != 0);
2155
2156 ms.SetControlDown((mask & GDK_CONTROL_MASK) != 0);
2157 ms.SetShiftDown((mask & GDK_SHIFT_MASK) != 0);
2158 ms.SetAltDown((mask & GDK_MOD1_MASK) != 0);
2159 ms.SetMetaDown((mask & GDK_META_MASK) != 0);
2160
2161 return ms;
2162 }
2163
2164 //-----------------------------------------------------------------------------
2165 // wxWindowGTK
2166 //-----------------------------------------------------------------------------
2167
2168 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2169 // method
2170 #ifdef __WXUNIVERSAL__
2171 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK, wxWindowBase)
2172 #endif // __WXUNIVERSAL__
2173
2174 void wxWindowGTK::Init()
2175 {
2176 // GTK specific
2177 m_widget = NULL;
2178 m_wxwindow = NULL;
2179 m_focusWidget = NULL;
2180
2181 // position/size
2182 m_x = 0;
2183 m_y = 0;
2184 m_width = 0;
2185 m_height = 0;
2186
2187 m_showOnIdle = false;
2188
2189 m_noExpose = false;
2190 m_nativeSizeEvent = false;
2191 #ifdef __WXGTK3__
2192 m_paintContext = NULL;
2193 #endif
2194
2195 m_isScrolling = false;
2196 m_mouseButtonDown = false;
2197
2198 // initialize scrolling stuff
2199 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2200 {
2201 m_scrollBar[dir] = NULL;
2202 m_scrollPos[dir] = 0;
2203 }
2204
2205 m_oldClientWidth =
2206 m_oldClientHeight = 0;
2207
2208 m_clipPaintRegion = false;
2209
2210 m_needsStyleChange = false;
2211
2212 m_cursor = *wxSTANDARD_CURSOR;
2213
2214 m_imData = NULL;
2215 m_dirtyTabOrder = false;
2216 }
2217
2218 wxWindowGTK::wxWindowGTK()
2219 {
2220 Init();
2221 }
2222
2223 wxWindowGTK::wxWindowGTK( wxWindow *parent,
2224 wxWindowID id,
2225 const wxPoint &pos,
2226 const wxSize &size,
2227 long style,
2228 const wxString &name )
2229 {
2230 Init();
2231
2232 Create( parent, id, pos, size, style, name );
2233 }
2234
2235 void wxWindowGTK::GTKCreateScrolledWindowWith(GtkWidget* view)
2236 {
2237 wxASSERT_MSG( HasFlag(wxHSCROLL) || HasFlag(wxVSCROLL),
2238 wxS("Must not be called if scrolling is not needed.") );
2239
2240 m_widget = gtk_scrolled_window_new( NULL, NULL );
2241
2242 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
2243
2244 // There is a conflict with default bindings at GTK+
2245 // level between scrolled windows and notebooks both of which want to use
2246 // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
2247 // direction and notebooks for changing pages -- we decide that if we don't
2248 // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
2249 // means we can get working keyboard navigation in notebooks
2250 if ( !HasFlag(wxHSCROLL) )
2251 {
2252 GtkBindingSet *
2253 bindings = gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget));
2254 if ( bindings )
2255 {
2256 gtk_binding_entry_remove(bindings, GDK_Page_Up, GDK_CONTROL_MASK);
2257 gtk_binding_entry_remove(bindings, GDK_Page_Down, GDK_CONTROL_MASK);
2258 }
2259 }
2260
2261 if (HasFlag(wxALWAYS_SHOW_SB))
2262 {
2263 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS );
2264 }
2265 else
2266 {
2267 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
2268 }
2269
2270 m_scrollBar[ScrollDir_Horz] = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(scrolledWindow));
2271 m_scrollBar[ScrollDir_Vert] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(scrolledWindow));
2272 if (GetLayoutDirection() == wxLayout_RightToLeft)
2273 gtk_range_set_inverted( m_scrollBar[ScrollDir_Horz], TRUE );
2274
2275 gtk_container_add( GTK_CONTAINER(m_widget), view );
2276
2277 // connect various scroll-related events
2278 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2279 {
2280 // these handlers block mouse events to any window during scrolling
2281 // such as motion events and prevent GTK and wxWidgets from fighting
2282 // over where the slider should be
2283 g_signal_connect(m_scrollBar[dir], "button_press_event",
2284 G_CALLBACK(gtk_scrollbar_button_press_event), this);
2285 g_signal_connect(m_scrollBar[dir], "button_release_event",
2286 G_CALLBACK(gtk_scrollbar_button_release_event), this);
2287
2288 gulong handler_id = g_signal_connect(m_scrollBar[dir], "event_after",
2289 G_CALLBACK(gtk_scrollbar_event_after), this);
2290 g_signal_handler_block(m_scrollBar[dir], handler_id);
2291
2292 // these handlers get notified when scrollbar slider moves
2293 g_signal_connect_after(m_scrollBar[dir], "value_changed",
2294 G_CALLBACK(gtk_scrollbar_value_changed), this);
2295 }
2296
2297 gtk_widget_show( view );
2298 }
2299
2300 bool wxWindowGTK::Create( wxWindow *parent,
2301 wxWindowID id,
2302 const wxPoint &pos,
2303 const wxSize &size,
2304 long style,
2305 const wxString &name )
2306 {
2307 // Get default border
2308 wxBorder border = GetBorder(style);
2309
2310 style &= ~wxBORDER_MASK;
2311 style |= border;
2312
2313 if (!PreCreation( parent, pos, size ) ||
2314 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
2315 {
2316 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2317 return false;
2318 }
2319
2320 // We should accept the native look
2321 #if 0
2322 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
2323 scroll_class->scrollbar_spacing = 0;
2324 #endif
2325
2326
2327 m_wxwindow = wxPizza::New(m_windowStyle);
2328 #ifndef __WXUNIVERSAL__
2329 if (HasFlag(wxPizza::BORDER_STYLES))
2330 {
2331 g_signal_connect(m_wxwindow, "parent_set",
2332 G_CALLBACK(parent_set), this);
2333 }
2334 #endif
2335 if (!HasFlag(wxHSCROLL) && !HasFlag(wxVSCROLL))
2336 m_widget = m_wxwindow;
2337 else
2338 GTKCreateScrolledWindowWith(m_wxwindow);
2339 g_object_ref(m_widget);
2340
2341 if (m_parent)
2342 m_parent->DoAddChild( this );
2343
2344 m_focusWidget = m_wxwindow;
2345
2346 SetCanFocus(AcceptsFocus());
2347
2348 PostCreation();
2349
2350 return true;
2351 }
2352
2353 void wxWindowGTK::GTKDisconnect(void* instance)
2354 {
2355 g_signal_handlers_disconnect_matched(instance,
2356 GSignalMatchType(G_SIGNAL_MATCH_DATA), 0, 0, NULL, NULL, this);
2357 }
2358
2359 wxWindowGTK::~wxWindowGTK()
2360 {
2361 SendDestroyEvent();
2362
2363 if (gs_currentFocus == this)
2364 gs_currentFocus = NULL;
2365 if (gs_pendingFocus == this)
2366 gs_pendingFocus = NULL;
2367
2368 if ( gs_deferredFocusOut == this )
2369 gs_deferredFocusOut = NULL;
2370
2371 if (m_widget)
2372 GTKDisconnect(m_widget);
2373 if (m_wxwindow && m_wxwindow != m_widget)
2374 GTKDisconnect(m_wxwindow);
2375
2376 // destroy children before destroying this window itself
2377 DestroyChildren();
2378
2379 if (m_widget)
2380 Show( false );
2381
2382 // delete before the widgets to avoid a crash on solaris
2383 delete m_imData;
2384 m_imData = NULL;
2385
2386 // avoid problem with GTK+ 2.18 where a frozen window causes the whole
2387 // TLW to be frozen, and if the window is then destroyed, nothing ever
2388 // gets painted again
2389 while (IsFrozen())
2390 Thaw();
2391
2392 if (m_widget)
2393 {
2394 // Note that gtk_widget_destroy() does not destroy the widget, it just
2395 // emits the "destroy" signal. The widget is not actually destroyed
2396 // until its reference count drops to zero.
2397 gtk_widget_destroy(m_widget);
2398 // Release our reference, should be the last one
2399 g_object_unref(m_widget);
2400 m_widget = NULL;
2401 }
2402 m_wxwindow = NULL;
2403 }
2404
2405 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
2406 {
2407 if ( GTKNeedsParent() )
2408 {
2409 wxCHECK_MSG( parent, false, wxT("Must have non-NULL parent") );
2410 }
2411
2412 // Use either the given size, or the default if -1 is given.
2413 // See wxWindowBase for these functions.
2414 m_width = WidthDefault(size.x) ;
2415 m_height = HeightDefault(size.y);
2416
2417 if (pos != wxDefaultPosition)
2418 {
2419 m_x = pos.x;
2420 m_y = pos.y;
2421 }
2422
2423 return true;
2424 }
2425
2426 void wxWindowGTK::PostCreation()
2427 {
2428 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2429
2430 #if wxGTK_HAS_COMPOSITING_SUPPORT
2431 // Set RGBA visual as soon as possible to minimize the possibility that
2432 // somebody uses the wrong one.
2433 if ( m_backgroundStyle == wxBG_STYLE_TRANSPARENT &&
2434 IsTransparentBackgroundSupported() )
2435 {
2436 GdkScreen *screen = gtk_widget_get_screen (m_widget);
2437 #ifdef __WXGTK3__
2438 gtk_widget_set_visual(m_widget, gdk_screen_get_rgba_visual(screen));
2439 #else
2440 GdkColormap *rgba_colormap = gdk_screen_get_rgba_colormap (screen);
2441
2442 if (rgba_colormap)
2443 gtk_widget_set_colormap(m_widget, rgba_colormap);
2444 #endif
2445 }
2446 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
2447
2448 if (m_wxwindow)
2449 {
2450 if (!m_noExpose)
2451 {
2452 // these get reported to wxWidgets -> wxPaintEvent
2453 #ifdef __WXGTK3__
2454 g_signal_connect(m_wxwindow, "draw", G_CALLBACK(draw), this);
2455 #else
2456 g_signal_connect(m_wxwindow, "expose_event", G_CALLBACK(expose_event), this);
2457 #endif
2458
2459 if (GetLayoutDirection() == wxLayout_LeftToRight)
2460 gtk_widget_set_redraw_on_allocate(m_wxwindow, HasFlag(wxFULL_REPAINT_ON_RESIZE));
2461 }
2462
2463 // Create input method handler
2464 m_imData = new wxGtkIMData;
2465
2466 // Cannot handle drawing preedited text yet
2467 gtk_im_context_set_use_preedit( m_imData->context, FALSE );
2468
2469 g_signal_connect (m_imData->context, "commit",
2470 G_CALLBACK (gtk_wxwindow_commit_cb), this);
2471 }
2472
2473 // focus handling
2474
2475 if (!GTK_IS_WINDOW(m_widget))
2476 {
2477 if (m_focusWidget == NULL)
2478 m_focusWidget = m_widget;
2479
2480 if (m_wxwindow)
2481 {
2482 g_signal_connect (m_focusWidget, "focus_in_event",
2483 G_CALLBACK (gtk_window_focus_in_callback), this);
2484 g_signal_connect (m_focusWidget, "focus_out_event",
2485 G_CALLBACK (gtk_window_focus_out_callback), this);
2486 }
2487 else
2488 {
2489 g_signal_connect_after (m_focusWidget, "focus_in_event",
2490 G_CALLBACK (gtk_window_focus_in_callback), this);
2491 g_signal_connect_after (m_focusWidget, "focus_out_event",
2492 G_CALLBACK (gtk_window_focus_out_callback), this);
2493 }
2494 }
2495
2496 if ( !AcceptsFocusFromKeyboard() )
2497 {
2498 SetCanFocus(false);
2499
2500 g_signal_connect(m_widget, "focus",
2501 G_CALLBACK(wx_window_focus_callback), this);
2502 }
2503
2504 // connect to the various key and mouse handlers
2505
2506 GtkWidget *connect_widget = GetConnectWidget();
2507
2508 ConnectWidget( connect_widget );
2509
2510 // We cannot set colours, fonts and cursors before the widget has been
2511 // realized, so we do this directly after realization -- unless the widget
2512 // was in fact realized already.
2513 if ( gtk_widget_get_realized(connect_widget) )
2514 {
2515 GTKHandleRealized();
2516 }
2517 else
2518 {
2519 g_signal_connect (connect_widget, "realize",
2520 G_CALLBACK (gtk_window_realized_callback), this);
2521 }
2522 g_signal_connect(connect_widget, "unrealize", G_CALLBACK(unrealize), this);
2523
2524 if (!IsTopLevel())
2525 {
2526 g_signal_connect(m_wxwindow ? m_wxwindow : m_widget, "size_allocate",
2527 G_CALLBACK(size_allocate), this);
2528 }
2529
2530 #if GTK_CHECK_VERSION(2, 8, 0)
2531 #ifndef __WXGTK3__
2532 if ( gtk_check_version(2,8,0) == NULL )
2533 #endif
2534 {
2535 // Make sure we can notify the app when mouse capture is lost
2536 if ( m_wxwindow )
2537 {
2538 g_signal_connect (m_wxwindow, "grab_broken_event",
2539 G_CALLBACK (gtk_window_grab_broken), this);
2540 }
2541
2542 if ( connect_widget != m_wxwindow )
2543 {
2544 g_signal_connect (connect_widget, "grab_broken_event",
2545 G_CALLBACK (gtk_window_grab_broken), this);
2546 }
2547 }
2548 #endif // GTK+ >= 2.8
2549
2550 if (!WX_IS_PIZZA(gtk_widget_get_parent(m_widget)) && !GTK_IS_WINDOW(m_widget))
2551 gtk_widget_set_size_request(m_widget, m_width, m_height);
2552
2553 InheritAttributes();
2554
2555 SetLayoutDirection(wxLayout_Default);
2556
2557 // unless the window was created initially hidden (i.e. Hide() had been
2558 // called before Create()), we should show it at GTK+ level as well
2559 if ( IsShown() )
2560 gtk_widget_show( m_widget );
2561 }
2562
2563 unsigned long
2564 wxWindowGTK::GTKConnectWidget(const char *signal, wxGTKCallback callback)
2565 {
2566 return g_signal_connect(m_widget, signal, callback, this);
2567 }
2568
2569 void wxWindowGTK::ConnectWidget( GtkWidget *widget )
2570 {
2571 g_signal_connect (widget, "key_press_event",
2572 G_CALLBACK (gtk_window_key_press_callback), this);
2573 g_signal_connect (widget, "key_release_event",
2574 G_CALLBACK (gtk_window_key_release_callback), this);
2575 g_signal_connect (widget, "button_press_event",
2576 G_CALLBACK (gtk_window_button_press_callback), this);
2577 g_signal_connect (widget, "button_release_event",
2578 G_CALLBACK (gtk_window_button_release_callback), this);
2579 g_signal_connect (widget, "motion_notify_event",
2580 G_CALLBACK (gtk_window_motion_notify_callback), this);
2581
2582 g_signal_connect (widget, "scroll_event",
2583 G_CALLBACK (window_scroll_event), this);
2584 if (m_scrollBar[ScrollDir_Horz])
2585 g_signal_connect (m_scrollBar[ScrollDir_Horz], "scroll_event",
2586 G_CALLBACK (window_scroll_event_hscrollbar), this);
2587 if (m_scrollBar[ScrollDir_Vert])
2588 g_signal_connect (m_scrollBar[ScrollDir_Vert], "scroll_event",
2589 G_CALLBACK (window_scroll_event), this);
2590
2591 g_signal_connect (widget, "popup_menu",
2592 G_CALLBACK (wxgtk_window_popup_menu_callback), this);
2593 g_signal_connect (widget, "enter_notify_event",
2594 G_CALLBACK (gtk_window_enter_callback), this);
2595 g_signal_connect (widget, "leave_notify_event",
2596 G_CALLBACK (gtk_window_leave_callback), this);
2597 }
2598
2599 static GSList* gs_queueResizeList;
2600
2601 extern "C" {
2602 static gboolean queue_resize(void*)
2603 {
2604 gdk_threads_enter();
2605 for (GSList* p = gs_queueResizeList; p; p = p->next)
2606 {
2607 if (p->data)
2608 {
2609 gtk_widget_queue_resize(GTK_WIDGET(p->data));
2610 g_object_remove_weak_pointer(G_OBJECT(p->data), &p->data);
2611 }
2612 }
2613 g_slist_free(gs_queueResizeList);
2614 gs_queueResizeList = NULL;
2615 gdk_threads_leave();
2616 return false;
2617 }
2618 }
2619
2620 void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
2621 {
2622 gtk_widget_set_size_request(m_widget, width, height);
2623 GtkWidget* parent = gtk_widget_get_parent(m_widget);
2624 if (WX_IS_PIZZA(parent))
2625 WX_PIZZA(parent)->move(m_widget, x, y, width, height);
2626
2627 // With GTK3, gtk_widget_queue_resize() is ignored while a size-allocate
2628 // is in progress. This situation is common in wxWidgets, since
2629 // size-allocate can generate wxSizeEvent and size event handlers often
2630 // call SetSize(), directly or indirectly. Work around this by deferring
2631 // the queue-resize until after size-allocate processing is finished.
2632 if (g_slist_find(gs_queueResizeList, m_widget) == NULL)
2633 {
2634 if (gs_queueResizeList == NULL)
2635 g_idle_add_full(GTK_PRIORITY_RESIZE, queue_resize, NULL, NULL);
2636 gs_queueResizeList = g_slist_prepend(gs_queueResizeList, m_widget);
2637 g_object_add_weak_pointer(G_OBJECT(m_widget), &gs_queueResizeList->data);
2638 }
2639 }
2640
2641 void wxWindowGTK::ConstrainSize()
2642 {
2643 #ifdef __WXGPE__
2644 // GPE's window manager doesn't like size hints at all, esp. when the user
2645 // has to use the virtual keyboard, so don't constrain size there
2646 if (!IsTopLevel())
2647 #endif
2648 {
2649 const wxSize minSize = GetMinSize();
2650 const wxSize maxSize = GetMaxSize();
2651 if (minSize.x > 0 && m_width < minSize.x) m_width = minSize.x;
2652 if (minSize.y > 0 && m_height < minSize.y) m_height = minSize.y;
2653 if (maxSize.x > 0 && m_width > maxSize.x) m_width = maxSize.x;
2654 if (maxSize.y > 0 && m_height > maxSize.y) m_height = maxSize.y;
2655 }
2656 }
2657
2658 void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
2659 {
2660 wxCHECK_RET(m_widget, "invalid window");
2661
2662 int scrollX = 0, scrollY = 0;
2663 GtkWidget* parent = gtk_widget_get_parent(m_widget);
2664 if (WX_IS_PIZZA(parent))
2665 {
2666 wxPizza* pizza = WX_PIZZA(parent);
2667 scrollX = pizza->m_scroll_x;
2668 scrollY = pizza->m_scroll_y;
2669 }
2670 if (x != -1 || (sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2671 x += scrollX;
2672 else
2673 x = m_x;
2674 if (y != -1 || (sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2675 y += scrollY;
2676 else
2677 y = m_y;
2678
2679 // calculate the best size if we should auto size the window
2680 if ( ((sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1) ||
2681 ((sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1) )
2682 {
2683 const wxSize sizeBest = GetBestSize();
2684 if ( (sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1 )
2685 width = sizeBest.x;
2686 if ( (sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1 )
2687 height = sizeBest.y;
2688 }
2689
2690 if (width == -1)
2691 width = m_width;
2692 if (height == -1)
2693 height = m_height;
2694
2695 const bool sizeChange = m_width != width || m_height != height;
2696 if (sizeChange || m_x != x || m_y != y)
2697 {
2698 m_x = x;
2699 m_y = y;
2700 m_width = width;
2701 m_height = height;
2702
2703 /* the default button has a border around it */
2704 if (gtk_widget_get_can_default(m_widget))
2705 {
2706 GtkBorder *default_border = NULL;
2707 gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
2708 if (default_border)
2709 {
2710 x -= default_border->left;
2711 y -= default_border->top;
2712 width += default_border->left + default_border->right;
2713 height += default_border->top + default_border->bottom;
2714 gtk_border_free( default_border );
2715 }
2716 }
2717
2718 DoMoveWindow(x, y, width, height);
2719 }
2720
2721 if ((sizeChange && !m_nativeSizeEvent) || (sizeFlags & wxSIZE_FORCE_EVENT))
2722 {
2723 // update these variables to keep size_allocate handler
2724 // from sending another size event for this change
2725 GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
2726
2727 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
2728 event.SetEventObject( this );
2729 HandleWindowEvent( event );
2730 }
2731 }
2732
2733 bool wxWindowGTK::GTKShowFromOnIdle()
2734 {
2735 if (IsShown() && m_showOnIdle && !gtk_widget_get_visible (m_widget))
2736 {
2737 GtkAllocation alloc;
2738 alloc.x = m_x;
2739 alloc.y = m_y;
2740 alloc.width = m_width;
2741 alloc.height = m_height;
2742 gtk_widget_size_allocate( m_widget, &alloc );
2743 gtk_widget_show( m_widget );
2744 wxShowEvent eventShow(GetId(), true);
2745 eventShow.SetEventObject(this);
2746 HandleWindowEvent(eventShow);
2747 m_showOnIdle = false;
2748 return true;
2749 }
2750
2751 return false;
2752 }
2753
2754 void wxWindowGTK::OnInternalIdle()
2755 {
2756 if ( gs_deferredFocusOut )
2757 GTKHandleDeferredFocusOut();
2758
2759 // Check if we have to show window now
2760 if (GTKShowFromOnIdle()) return;
2761
2762 if ( m_dirtyTabOrder )
2763 {
2764 m_dirtyTabOrder = false;
2765 RealizeTabOrder();
2766 }
2767
2768 wxWindowBase::OnInternalIdle();
2769 }
2770
2771 void wxWindowGTK::DoGetSize( int *width, int *height ) const
2772 {
2773 if (width) (*width) = m_width;
2774 if (height) (*height) = m_height;
2775 }
2776
2777 void wxWindowGTK::DoSetClientSize( int width, int height )
2778 {
2779 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2780
2781 const wxSize size = GetSize();
2782 const wxSize clientSize = GetClientSize();
2783 SetSize(width + (size.x - clientSize.x), height + (size.y - clientSize.y));
2784 }
2785
2786 void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
2787 {
2788 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2789
2790 int w = m_width;
2791 int h = m_height;
2792
2793 if ( m_wxwindow )
2794 {
2795 // if window is scrollable, account for scrollbars
2796 if ( GTK_IS_SCROLLED_WINDOW(m_widget) )
2797 {
2798 GtkPolicyType policy[ScrollDir_Max];
2799 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(m_widget),
2800 &policy[ScrollDir_Horz],
2801 &policy[ScrollDir_Vert]);
2802 const int scrollbar_spacing =
2803 GTK_SCROLLED_WINDOW_GET_CLASS(m_widget)->scrollbar_spacing;
2804
2805 for ( int i = 0; i < ScrollDir_Max; i++ )
2806 {
2807 // don't account for the scrollbars we don't have
2808 GtkRange * const range = m_scrollBar[i];
2809 if ( !range )
2810 continue;
2811
2812 // nor for the ones we have but don't current show
2813 switch ( policy[i] )
2814 {
2815 case GTK_POLICY_NEVER:
2816 // never shown so doesn't take any place
2817 continue;
2818
2819 case GTK_POLICY_ALWAYS:
2820 // no checks necessary
2821 break;
2822
2823 case GTK_POLICY_AUTOMATIC:
2824 // may be shown or not, check
2825 GtkAdjustment *adj = gtk_range_get_adjustment(range);
2826 if (gtk_adjustment_get_upper(adj) <= gtk_adjustment_get_page_size(adj))
2827 continue;
2828 }
2829
2830 GtkRequisition req;
2831 #ifdef __WXGTK3__
2832 GtkWidget* widget = GTK_WIDGET(range);
2833 if (i == ScrollDir_Horz)
2834 {
2835 if (height)
2836 {
2837 gtk_widget_get_preferred_height(widget, NULL, &req.height);
2838 h -= req.height + scrollbar_spacing;
2839 }
2840 }
2841 else
2842 {
2843 if (width)
2844 {
2845 gtk_widget_get_preferred_width(widget, NULL, &req.width);
2846 w -= req.width + scrollbar_spacing;
2847 }
2848 }
2849 #else // !__WXGTK3__
2850 gtk_widget_size_request(GTK_WIDGET(range), &req);
2851 if (i == ScrollDir_Horz)
2852 h -= req.height + scrollbar_spacing;
2853 else
2854 w -= req.width + scrollbar_spacing;
2855 #endif // !__WXGTK3__
2856 }
2857 }
2858
2859 const wxSize sizeBorders = DoGetBorderSize();
2860 w -= sizeBorders.x;
2861 h -= sizeBorders.y;
2862
2863 if (w < 0)
2864 w = 0;
2865 if (h < 0)
2866 h = 0;
2867 }
2868
2869 if (width) *width = w;
2870 if (height) *height = h;
2871 }
2872
2873 wxSize wxWindowGTK::DoGetBorderSize() const
2874 {
2875 if ( !m_wxwindow )
2876 return wxWindowBase::DoGetBorderSize();
2877
2878 GtkBorder border;
2879 WX_PIZZA(m_wxwindow)->get_border(border);
2880 return wxSize(border.left + border.right, border.top + border.bottom);
2881 }
2882
2883 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
2884 {
2885 int dx = 0;
2886 int dy = 0;
2887 GtkWidget* parent = NULL;
2888 if (m_widget)
2889 parent = gtk_widget_get_parent(m_widget);
2890 if (WX_IS_PIZZA(parent))
2891 {
2892 wxPizza* pizza = WX_PIZZA(parent);
2893 dx = pizza->m_scroll_x;
2894 dy = pizza->m_scroll_y;
2895 }
2896 if (x) (*x) = m_x - dx;
2897 if (y) (*y) = m_y - dy;
2898 }
2899
2900 void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
2901 {
2902 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2903
2904 if (gtk_widget_get_window(m_widget) == NULL) return;
2905
2906 GdkWindow *source = NULL;
2907 if (m_wxwindow)
2908 source = gtk_widget_get_window(m_wxwindow);
2909 else
2910 source = gtk_widget_get_window(m_widget);
2911
2912 int org_x = 0;
2913 int org_y = 0;
2914 gdk_window_get_origin( source, &org_x, &org_y );
2915
2916 if (!m_wxwindow)
2917 {
2918 if (!gtk_widget_get_has_window(m_widget))
2919 {
2920 GtkAllocation a;
2921 gtk_widget_get_allocation(m_widget, &a);
2922 org_x += a.x;
2923 org_y += a.y;
2924 }
2925 }
2926
2927
2928 if (x)
2929 {
2930 if (GetLayoutDirection() == wxLayout_RightToLeft)
2931 *x = (GetClientSize().x - *x) + org_x;
2932 else
2933 *x += org_x;
2934 }
2935
2936 if (y) *y += org_y;
2937 }
2938
2939 void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
2940 {
2941 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2942
2943 if (!gtk_widget_get_realized(m_widget)) return;
2944
2945 GdkWindow *source = NULL;
2946 if (m_wxwindow)
2947 source = gtk_widget_get_window(m_wxwindow);
2948 else
2949 source = gtk_widget_get_window(m_widget);
2950
2951 int org_x = 0;
2952 int org_y = 0;
2953 gdk_window_get_origin( source, &org_x, &org_y );
2954
2955 if (!m_wxwindow)
2956 {
2957 if (!gtk_widget_get_has_window(m_widget))
2958 {
2959 GtkAllocation a;
2960 gtk_widget_get_allocation(m_widget, &a);
2961 org_x += a.x;
2962 org_y += a.y;
2963 }
2964 }
2965
2966 if (x)
2967 {
2968 if (GetLayoutDirection() == wxLayout_RightToLeft)
2969 *x = (GetClientSize().x - *x) - org_x;
2970 else
2971 *x -= org_x;
2972 }
2973 if (y) *y -= org_y;
2974 }
2975
2976 bool wxWindowGTK::Show( bool show )
2977 {
2978 if ( !wxWindowBase::Show(show) )
2979 {
2980 // nothing to do
2981 return false;
2982 }
2983
2984 // notice that we may call Hide() before the window is created and this is
2985 // actually useful to create it hidden initially -- but we can't call
2986 // Show() before it is created
2987 if ( !m_widget )
2988 {
2989 wxASSERT_MSG( !show, "can't show invalid window" );
2990 return true;
2991 }
2992
2993 if ( show )
2994 {
2995 if ( m_showOnIdle )
2996 {
2997 // defer until later
2998 return true;
2999 }
3000
3001 gtk_widget_show(m_widget);
3002 }
3003 else // hide
3004 {
3005 gtk_widget_hide(m_widget);
3006 }
3007
3008 wxShowEvent eventShow(GetId(), show);
3009 eventShow.SetEventObject(this);
3010 HandleWindowEvent(eventShow);
3011
3012 return true;
3013 }
3014
3015 void wxWindowGTK::DoEnable( bool enable )
3016 {
3017 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3018
3019 gtk_widget_set_sensitive( m_widget, enable );
3020 if (m_wxwindow && (m_wxwindow != m_widget))
3021 gtk_widget_set_sensitive( m_wxwindow, enable );
3022 }
3023
3024 int wxWindowGTK::GetCharHeight() const
3025 {
3026 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
3027
3028 wxFont font = GetFont();
3029 wxCHECK_MSG( font.IsOk(), 12, wxT("invalid font") );
3030
3031 PangoContext* context = gtk_widget_get_pango_context(m_widget);
3032
3033 if (!context)
3034 return 0;
3035
3036 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3037 PangoLayout *layout = pango_layout_new(context);
3038 pango_layout_set_font_description(layout, desc);
3039 pango_layout_set_text(layout, "H", 1);
3040 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3041
3042 PangoRectangle rect;
3043 pango_layout_line_get_extents(line, NULL, &rect);
3044
3045 g_object_unref (layout);
3046
3047 return (int) PANGO_PIXELS(rect.height);
3048 }
3049
3050 int wxWindowGTK::GetCharWidth() const
3051 {
3052 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
3053
3054 wxFont font = GetFont();
3055 wxCHECK_MSG( font.IsOk(), 8, wxT("invalid font") );
3056
3057 PangoContext* context = gtk_widget_get_pango_context(m_widget);
3058
3059 if (!context)
3060 return 0;
3061
3062 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3063 PangoLayout *layout = pango_layout_new(context);
3064 pango_layout_set_font_description(layout, desc);
3065 pango_layout_set_text(layout, "g", 1);
3066 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3067
3068 PangoRectangle rect;
3069 pango_layout_line_get_extents(line, NULL, &rect);
3070
3071 g_object_unref (layout);
3072
3073 return (int) PANGO_PIXELS(rect.width);
3074 }
3075
3076 void wxWindowGTK::DoGetTextExtent( const wxString& string,
3077 int *x,
3078 int *y,
3079 int *descent,
3080 int *externalLeading,
3081 const wxFont *theFont ) const
3082 {
3083 wxFont fontToUse = theFont ? *theFont : GetFont();
3084
3085 wxCHECK_RET( fontToUse.IsOk(), wxT("invalid font") );
3086
3087 if (string.empty())
3088 {
3089 if (x) (*x) = 0;
3090 if (y) (*y) = 0;
3091 return;
3092 }
3093
3094 PangoContext *context = NULL;
3095 if (m_widget)
3096 context = gtk_widget_get_pango_context( m_widget );
3097
3098 if (!context)
3099 {
3100 if (x) (*x) = 0;
3101 if (y) (*y) = 0;
3102 return;
3103 }
3104
3105 PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
3106 PangoLayout *layout = pango_layout_new(context);
3107 pango_layout_set_font_description(layout, desc);
3108 {
3109 const wxCharBuffer data = wxGTK_CONV( string );
3110 if ( data )
3111 pango_layout_set_text(layout, data, strlen(data));
3112 }
3113
3114 PangoRectangle rect;
3115 pango_layout_get_extents(layout, NULL, &rect);
3116
3117 if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width);
3118 if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height);
3119 if (descent)
3120 {
3121 PangoLayoutIter *iter = pango_layout_get_iter(layout);
3122 int baseline = pango_layout_iter_get_baseline(iter);
3123 pango_layout_iter_free(iter);
3124 *descent = *y - PANGO_PIXELS(baseline);
3125 }
3126 if (externalLeading) (*externalLeading) = 0; // ??
3127
3128 g_object_unref (layout);
3129 }
3130
3131 void wxWindowGTK::GTKDisableFocusOutEvent()
3132 {
3133 g_signal_handlers_block_by_func( m_focusWidget,
3134 (gpointer) gtk_window_focus_out_callback, this);
3135 }
3136
3137 void wxWindowGTK::GTKEnableFocusOutEvent()
3138 {
3139 g_signal_handlers_unblock_by_func( m_focusWidget,
3140 (gpointer) gtk_window_focus_out_callback, this);
3141 }
3142
3143 bool wxWindowGTK::GTKHandleFocusIn()
3144 {
3145 // Disable default focus handling for custom windows since the default GTK+
3146 // handler issues a repaint
3147 const bool retval = m_wxwindow ? true : false;
3148
3149
3150 // NB: if there's still unprocessed deferred focus-out event (see
3151 // GTKHandleFocusOut() for explanation), we need to process it first so
3152 // that the order of focus events -- focus-out first, then focus-in
3153 // elsewhere -- is preserved
3154 if ( gs_deferredFocusOut )
3155 {
3156 if ( GTKNeedsToFilterSameWindowFocus() &&
3157 gs_deferredFocusOut == this )
3158 {
3159 // GTK+ focus changed from this wxWindow back to itself, so don't
3160 // emit any events at all
3161 wxLogTrace(TRACE_FOCUS,
3162 "filtered out spurious focus change within %s(%p, %s)",
3163 GetClassInfo()->GetClassName(), this, GetLabel());
3164 gs_deferredFocusOut = NULL;
3165 return retval;
3166 }
3167
3168 // otherwise we need to send focus-out first
3169 wxASSERT_MSG ( gs_deferredFocusOut != this,
3170 "GTKHandleFocusIn(GTKFocus_Normal) called even though focus changed back to itself - derived class should handle this" );
3171 GTKHandleDeferredFocusOut();
3172 }
3173
3174
3175 wxLogTrace(TRACE_FOCUS,
3176 "handling focus_in event for %s(%p, %s)",
3177 GetClassInfo()->GetClassName(), this, GetLabel());
3178
3179 if (m_imData)
3180 gtk_im_context_focus_in(m_imData->context);
3181
3182 gs_currentFocus = this;
3183 gs_pendingFocus = NULL;
3184
3185 #if wxUSE_CARET
3186 // caret needs to be informed about focus change
3187 wxCaret *caret = GetCaret();
3188 if ( caret )
3189 {
3190 caret->OnSetFocus();
3191 }
3192 #endif // wxUSE_CARET
3193
3194 // Notify the parent keeping track of focus for the kbd navigation
3195 // purposes that we got it.
3196 wxChildFocusEvent eventChildFocus(static_cast<wxWindow*>(this));
3197 GTKProcessEvent(eventChildFocus);
3198
3199 wxFocusEvent eventFocus(wxEVT_SET_FOCUS, GetId());
3200 eventFocus.SetEventObject(this);
3201 GTKProcessEvent(eventFocus);
3202
3203 return retval;
3204 }
3205
3206 bool wxWindowGTK::GTKHandleFocusOut()
3207 {
3208 // Disable default focus handling for custom windows since the default GTK+
3209 // handler issues a repaint
3210 const bool retval = m_wxwindow ? true : false;
3211
3212
3213 // NB: If a control is composed of several GtkWidgets and when focus
3214 // changes from one of them to another within the same wxWindow, we get
3215 // a focus-out event followed by focus-in for another GtkWidget owned
3216 // by the same wx control. We don't want to generate two spurious
3217 // wxEVT_SET_FOCUS events in this case, so we defer sending wx events
3218 // from GTKHandleFocusOut() until we know for sure it's not coming back
3219 // (i.e. in GTKHandleFocusIn() or at idle time).
3220 if ( GTKNeedsToFilterSameWindowFocus() )
3221 {
3222 wxASSERT_MSG( gs_deferredFocusOut == NULL,
3223 "deferred focus out event already pending" );
3224 wxLogTrace(TRACE_FOCUS,
3225 "deferring focus_out event for %s(%p, %s)",
3226 GetClassInfo()->GetClassName(), this, GetLabel());
3227 gs_deferredFocusOut = this;
3228 return retval;
3229 }
3230
3231 GTKHandleFocusOutNoDeferring();
3232
3233 return retval;
3234 }
3235
3236 void wxWindowGTK::GTKHandleFocusOutNoDeferring()
3237 {
3238 wxLogTrace(TRACE_FOCUS,
3239 "handling focus_out event for %s(%p, %s)",
3240 GetClassInfo()->GetClassName(), this, GetLabel());
3241
3242 if (m_imData)
3243 gtk_im_context_focus_out(m_imData->context);
3244
3245 if ( gs_currentFocus != this )
3246 {
3247 // Something is terribly wrong, gs_currentFocus is out of sync with the
3248 // real focus. We will reset it to NULL anyway, because after this
3249 // focus-out event is handled, one of the following with happen:
3250 //
3251 // * either focus will go out of the app altogether, in which case
3252 // gs_currentFocus _should_ be NULL
3253 //
3254 // * or it goes to another control, in which case focus-in event will
3255 // follow immediately and it will set gs_currentFocus to the right
3256 // value
3257 wxLogDebug("window %s(%p, %s) lost focus even though it didn't have it",
3258 GetClassInfo()->GetClassName(), this, GetLabel());
3259 }
3260 gs_currentFocus = NULL;
3261
3262 #if wxUSE_CARET
3263 // caret needs to be informed about focus change
3264 wxCaret *caret = GetCaret();
3265 if ( caret )
3266 {
3267 caret->OnKillFocus();
3268 }
3269 #endif // wxUSE_CARET
3270
3271 wxFocusEvent event( wxEVT_KILL_FOCUS, GetId() );
3272 event.SetEventObject( this );
3273 event.SetWindow( FindFocus() );
3274 GTKProcessEvent( event );
3275 }
3276
3277 /*static*/
3278 void wxWindowGTK::GTKHandleDeferredFocusOut()
3279 {
3280 // NB: See GTKHandleFocusOut() for explanation. This function is called
3281 // from either GTKHandleFocusIn() or OnInternalIdle() to process
3282 // deferred event.
3283 if ( gs_deferredFocusOut )
3284 {
3285 wxWindowGTK *win = gs_deferredFocusOut;
3286 gs_deferredFocusOut = NULL;
3287
3288 wxLogTrace(TRACE_FOCUS,
3289 "processing deferred focus_out event for %s(%p, %s)",
3290 win->GetClassInfo()->GetClassName(), win, win->GetLabel());
3291
3292 win->GTKHandleFocusOutNoDeferring();
3293 }
3294 }
3295
3296 void wxWindowGTK::SetFocus()
3297 {
3298 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3299
3300 // Setting "physical" focus is not immediate in GTK+ and while
3301 // gtk_widget_is_focus ("determines if the widget is the focus widget
3302 // within its toplevel", i.e. returns true for one widget per TLW, not
3303 // globally) returns true immediately after grabbing focus,
3304 // GTK_WIDGET_HAS_FOCUS (which returns true only for the one widget that
3305 // has focus at the moment) takes effect only after the window is shown
3306 // (if it was hidden at the moment of the call) or at the next event loop
3307 // iteration.
3308 //
3309 // Because we want to FindFocus() call immediately following
3310 // foo->SetFocus() to return foo, we have to keep track of "pending" focus
3311 // ourselves.
3312 gs_pendingFocus = this;
3313
3314 GtkWidget *widget = m_wxwindow ? m_wxwindow : m_focusWidget;
3315
3316 if ( GTK_IS_CONTAINER(widget) &&
3317 !gtk_widget_get_can_focus(widget) )
3318 {
3319 wxLogTrace(TRACE_FOCUS,
3320 wxT("Setting focus to a child of %s(%p, %s)"),
3321 GetClassInfo()->GetClassName(), this, GetLabel().c_str());
3322 gtk_widget_child_focus(widget, GTK_DIR_TAB_FORWARD);
3323 }
3324 else
3325 {
3326 wxLogTrace(TRACE_FOCUS,
3327 wxT("Setting focus to %s(%p, %s)"),
3328 GetClassInfo()->GetClassName(), this, GetLabel().c_str());
3329 gtk_widget_grab_focus(widget);
3330 }
3331 }
3332
3333 void wxWindowGTK::SetCanFocus(bool canFocus)
3334 {
3335 gtk_widget_set_can_focus(m_widget, canFocus);
3336
3337 if ( m_wxwindow && (m_widget != m_wxwindow) )
3338 {
3339 gtk_widget_set_can_focus(m_wxwindow, canFocus);
3340 }
3341 }
3342
3343 bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
3344 {
3345 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3346
3347 wxWindowGTK * const newParent = (wxWindowGTK *)newParentBase;
3348
3349 wxASSERT( GTK_IS_WIDGET(m_widget) );
3350
3351 if ( !wxWindowBase::Reparent(newParent) )
3352 return false;
3353
3354 wxASSERT( GTK_IS_WIDGET(m_widget) );
3355
3356 // Notice that old m_parent pointer might be non-NULL here but the widget
3357 // still not have any parent at GTK level if it's a notebook page that had
3358 // been removed from the notebook so test this at GTK level and not wx one.
3359 if ( GtkWidget *parentGTK = gtk_widget_get_parent(m_widget) )
3360 gtk_container_remove(GTK_CONTAINER(parentGTK), m_widget);
3361
3362 wxASSERT( GTK_IS_WIDGET(m_widget) );
3363
3364 if (newParent)
3365 {
3366 if (gtk_widget_get_visible (newParent->m_widget))
3367 {
3368 m_showOnIdle = true;
3369 gtk_widget_hide( m_widget );
3370 }
3371 /* insert GTK representation */
3372 newParent->AddChildGTK(this);
3373 }
3374
3375 SetLayoutDirection(wxLayout_Default);
3376
3377 return true;
3378 }
3379
3380 void wxWindowGTK::DoAddChild(wxWindowGTK *child)
3381 {
3382 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
3383 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
3384
3385 /* add to list */
3386 AddChild( child );
3387
3388 /* insert GTK representation */
3389 AddChildGTK(child);
3390 }
3391
3392 void wxWindowGTK::AddChild(wxWindowBase *child)
3393 {
3394 wxWindowBase::AddChild(child);
3395 m_dirtyTabOrder = true;
3396 wxTheApp->WakeUpIdle();
3397 }
3398
3399 void wxWindowGTK::RemoveChild(wxWindowBase *child)
3400 {
3401 wxWindowBase::RemoveChild(child);
3402 m_dirtyTabOrder = true;
3403 wxTheApp->WakeUpIdle();
3404 }
3405
3406 /* static */
3407 wxLayoutDirection wxWindowGTK::GTKGetLayout(GtkWidget *widget)
3408 {
3409 return gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL
3410 ? wxLayout_RightToLeft
3411 : wxLayout_LeftToRight;
3412 }
3413
3414 /* static */
3415 void wxWindowGTK::GTKSetLayout(GtkWidget *widget, wxLayoutDirection dir)
3416 {
3417 wxASSERT_MSG( dir != wxLayout_Default, wxT("invalid layout direction") );
3418
3419 gtk_widget_set_direction(widget,
3420 dir == wxLayout_RightToLeft ? GTK_TEXT_DIR_RTL
3421 : GTK_TEXT_DIR_LTR);
3422 }
3423
3424 wxLayoutDirection wxWindowGTK::GetLayoutDirection() const
3425 {
3426 return GTKGetLayout(m_widget);
3427 }
3428
3429 void wxWindowGTK::SetLayoutDirection(wxLayoutDirection dir)
3430 {
3431 if ( dir == wxLayout_Default )
3432 {
3433 const wxWindow *const parent = GetParent();
3434 if ( parent )
3435 {
3436 // inherit layout from parent.
3437 dir = parent->GetLayoutDirection();
3438 }
3439 else // no parent, use global default layout
3440 {
3441 dir = wxTheApp->GetLayoutDirection();
3442 }
3443 }
3444
3445 if ( dir == wxLayout_Default )
3446 return;
3447
3448 GTKSetLayout(m_widget, dir);
3449
3450 if (m_wxwindow && (m_wxwindow != m_widget))
3451 GTKSetLayout(m_wxwindow, dir);
3452 }
3453
3454 wxCoord
3455 wxWindowGTK::AdjustForLayoutDirection(wxCoord x,
3456 wxCoord WXUNUSED(width),
3457 wxCoord WXUNUSED(widthTotal)) const
3458 {
3459 // We now mirror the coordinates of RTL windows in wxPizza
3460 return x;
3461 }
3462
3463 void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, WindowOrder move)
3464 {
3465 wxWindowBase::DoMoveInTabOrder(win, move);
3466 m_dirtyTabOrder = true;
3467 wxTheApp->WakeUpIdle();
3468 }
3469
3470 bool wxWindowGTK::DoNavigateIn(int flags)
3471 {
3472 if ( flags & wxNavigationKeyEvent::WinChange )
3473 {
3474 wxFAIL_MSG( wxT("not implemented") );
3475
3476 return false;
3477 }
3478 else // navigate inside the container
3479 {
3480 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
3481 wxCHECK_MSG( parent, false, wxT("every window must have a TLW parent") );
3482
3483 GtkDirectionType dir;
3484 dir = flags & wxNavigationKeyEvent::IsForward ? GTK_DIR_TAB_FORWARD
3485 : GTK_DIR_TAB_BACKWARD;
3486
3487 gboolean rc;
3488 g_signal_emit_by_name(parent->m_widget, "focus", dir, &rc);
3489
3490 return rc != 0;
3491 }
3492 }
3493
3494 bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3495 {
3496 // none needed by default
3497 return false;
3498 }
3499
3500 void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget* WXUNUSED(w))
3501 {
3502 // nothing to do by default since none is needed
3503 }
3504
3505 void wxWindowGTK::RealizeTabOrder()
3506 {
3507 if (m_wxwindow)
3508 {
3509 if ( !m_children.empty() )
3510 {
3511 // we don't only construct the correct focus chain but also use
3512 // this opportunity to update the mnemonic widgets for the widgets
3513 // that need them
3514
3515 GList *chain = NULL;
3516 wxWindowGTK* mnemonicWindow = NULL;
3517
3518 for ( wxWindowList::const_iterator i = m_children.begin();
3519 i != m_children.end();
3520 ++i )
3521 {
3522 wxWindowGTK *win = *i;
3523
3524 bool focusableFromKeyboard = win->AcceptsFocusFromKeyboard();
3525
3526 if ( mnemonicWindow )
3527 {
3528 if ( focusableFromKeyboard )
3529 {
3530 // wxComboBox et al. needs to focus on on a different
3531 // widget than m_widget, so if the main widget isn't
3532 // focusable try the connect widget
3533 GtkWidget* w = win->m_widget;
3534 if ( !gtk_widget_get_can_focus(w) )
3535 {
3536 w = win->GetConnectWidget();
3537 if ( !gtk_widget_get_can_focus(w) )
3538 w = NULL;
3539 }
3540
3541 if ( w )
3542 {
3543 mnemonicWindow->GTKWidgetDoSetMnemonic(w);
3544 mnemonicWindow = NULL;
3545 }
3546 }
3547 }
3548 else if ( win->GTKWidgetNeedsMnemonic() )
3549 {
3550 mnemonicWindow = win;
3551 }
3552
3553 if ( focusableFromKeyboard )
3554 chain = g_list_prepend(chain, win->m_widget);
3555 }
3556
3557 chain = g_list_reverse(chain);
3558
3559 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain);
3560 g_list_free(chain);
3561 }
3562 else // no children
3563 {
3564 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow));
3565 }
3566 }
3567 }
3568
3569 void wxWindowGTK::Raise()
3570 {
3571 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3572
3573 if (m_wxwindow && gtk_widget_get_window(m_wxwindow))
3574 {
3575 gdk_window_raise(gtk_widget_get_window(m_wxwindow));
3576 }
3577 else if (gtk_widget_get_window(m_widget))
3578 {
3579 gdk_window_raise(gtk_widget_get_window(m_widget));
3580 }
3581 }
3582
3583 void wxWindowGTK::Lower()
3584 {
3585 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3586
3587 if (m_wxwindow && gtk_widget_get_window(m_wxwindow))
3588 {
3589 gdk_window_lower(gtk_widget_get_window(m_wxwindow));
3590 }
3591 else if (gtk_widget_get_window(m_widget))
3592 {
3593 gdk_window_lower(gtk_widget_get_window(m_widget));
3594 }
3595 }
3596
3597 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
3598 {
3599 if ( !wxWindowBase::SetCursor(cursor.IsOk() ? cursor : *wxSTANDARD_CURSOR) )
3600 return false;
3601
3602 GTKUpdateCursor();
3603
3604 return true;
3605 }
3606
3607 void wxWindowGTK::GTKUpdateCursor(bool update_self /*=true*/, bool recurse /*=true*/)
3608 {
3609 if (update_self)
3610 {
3611 wxCursor cursor(g_globalCursor.IsOk() ? g_globalCursor : GetCursor());
3612 if ( cursor.IsOk() )
3613 {
3614 wxArrayGdkWindows windowsThis;
3615 GdkWindow* window = GTKGetWindow(windowsThis);
3616 if (window)
3617 gdk_window_set_cursor( window, cursor.GetCursor() );
3618 else
3619 {
3620 const size_t count = windowsThis.size();
3621 for ( size_t n = 0; n < count; n++ )
3622 {
3623 GdkWindow *win = windowsThis[n];
3624 // It can be zero if the window has not been realized yet.
3625 if ( win )
3626 {
3627 gdk_window_set_cursor(win, cursor.GetCursor());
3628 }
3629 }
3630 }
3631 }
3632 }
3633
3634 if (recurse)
3635 {
3636 for (wxWindowList::iterator it = GetChildren().begin(); it != GetChildren().end(); ++it)
3637 {
3638 (*it)->GTKUpdateCursor( true );
3639 }
3640 }
3641 }
3642
3643 void wxWindowGTK::WarpPointer( int x, int y )
3644 {
3645 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3646
3647 ClientToScreen(&x, &y);
3648 GdkDisplay* display = gtk_widget_get_display(m_widget);
3649 GdkScreen* screen = gtk_widget_get_screen(m_widget);
3650 #ifdef __WXGTK3__
3651 GdkDeviceManager* manager = gdk_display_get_device_manager(display);
3652 gdk_device_warp(gdk_device_manager_get_client_pointer(manager), screen, x, y);
3653 #else
3654 #ifdef GDK_WINDOWING_X11
3655 XWarpPointer(GDK_DISPLAY_XDISPLAY(display),
3656 None,
3657 GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
3658 0, 0, 0, 0, x, y);
3659 #endif
3660 #endif
3661 }
3662
3663 wxWindowGTK::ScrollDir wxWindowGTK::ScrollDirFromRange(GtkRange *range) const
3664 {
3665 // find the scrollbar which generated the event
3666 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
3667 {
3668 if ( range == m_scrollBar[dir] )
3669 return (ScrollDir)dir;
3670 }
3671
3672 wxFAIL_MSG( wxT("event from unknown scrollbar received") );
3673
3674 return ScrollDir_Max;
3675 }
3676
3677 bool wxWindowGTK::DoScrollByUnits(ScrollDir dir, ScrollUnit unit, int units)
3678 {
3679 bool changed = false;
3680 GtkRange* range = m_scrollBar[dir];
3681 if ( range && units )
3682 {
3683 GtkAdjustment* adj = gtk_range_get_adjustment(range);
3684 double inc = unit == ScrollUnit_Line ? gtk_adjustment_get_step_increment(adj)
3685 : gtk_adjustment_get_page_increment(adj);
3686
3687 const int posOld = wxRound(gtk_adjustment_get_value(adj));
3688 gtk_range_set_value(range, posOld + units*inc);
3689
3690 changed = wxRound(gtk_adjustment_get_value(adj)) != posOld;
3691 }
3692
3693 return changed;
3694 }
3695
3696 bool wxWindowGTK::ScrollLines(int lines)
3697 {
3698 return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Line, lines);
3699 }
3700
3701 bool wxWindowGTK::ScrollPages(int pages)
3702 {
3703 return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Page, pages);
3704 }
3705
3706 void wxWindowGTK::Refresh(bool WXUNUSED(eraseBackground),
3707 const wxRect *rect)
3708 {
3709 if (m_wxwindow)
3710 {
3711 if (gtk_widget_get_mapped(m_wxwindow))
3712 {
3713 GdkWindow* window = gtk_widget_get_window(m_wxwindow);
3714 if (rect)
3715 {
3716 GdkRectangle r = { rect->x, rect->y, rect->width, rect->height };
3717 if (GetLayoutDirection() == wxLayout_RightToLeft)
3718 r.x = gdk_window_get_width(window) - r.x - rect->width;
3719 gdk_window_invalidate_rect(window, &r, true);
3720 }
3721 else
3722 gdk_window_invalidate_rect(window, NULL, true);
3723 }
3724 }
3725 else if (m_widget)
3726 {
3727 if (gtk_widget_get_mapped(m_widget))
3728 {
3729 if (rect)
3730 gtk_widget_queue_draw_area(m_widget, rect->x, rect->y, rect->width, rect->height);
3731 else
3732 gtk_widget_queue_draw(m_widget);
3733 }
3734 }
3735 }
3736
3737 void wxWindowGTK::Update()
3738 {
3739 if (m_widget && gtk_widget_get_mapped(m_widget))
3740 {
3741 GdkDisplay* display = gtk_widget_get_display(m_widget);
3742 // Flush everything out to the server, and wait for it to finish.
3743 // This ensures nothing will overwrite the drawing we are about to do.
3744 gdk_display_sync(display);
3745
3746 GdkWindow* window = GTKGetDrawingWindow();
3747 if (window == NULL)
3748 window = gtk_widget_get_window(m_widget);
3749 gdk_window_process_updates(window, true);
3750
3751 // Flush again, but no need to wait for it to finish
3752 gdk_display_flush(display);
3753 }
3754 }
3755
3756 bool wxWindowGTK::DoIsExposed( int x, int y ) const
3757 {
3758 return m_updateRegion.Contains(x, y) != wxOutRegion;
3759 }
3760
3761 bool wxWindowGTK::DoIsExposed( int x, int y, int w, int h ) const
3762 {
3763 if (GetLayoutDirection() == wxLayout_RightToLeft)
3764 return m_updateRegion.Contains(x-w, y, w, h) != wxOutRegion;
3765 else
3766 return m_updateRegion.Contains(x, y, w, h) != wxOutRegion;
3767 }
3768
3769 #ifdef __WXGTK3__
3770 void wxWindowGTK::GTKSendPaintEvents(cairo_t* cr)
3771 #else
3772 void wxWindowGTK::GTKSendPaintEvents(const GdkRegion* region)
3773 #endif
3774 {
3775 #ifdef __WXGTK3__
3776 m_paintContext = cr;
3777 double x1, y1, x2, y2;
3778 cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
3779 m_updateRegion = wxRegion(int(x1), int(y1), int(x2 - x1), int(y2 - y1));
3780 #else // !__WXGTK3__
3781 m_updateRegion = wxRegion(region);
3782 #if wxGTK_HAS_COMPOSITING_SUPPORT
3783 cairo_t* cr = NULL;
3784 #endif
3785 #endif // !__WXGTK3__
3786 // Clip to paint region in wxClientDC
3787 m_clipPaintRegion = true;
3788
3789 m_nativeUpdateRegion = m_updateRegion;
3790
3791 if (GetLayoutDirection() == wxLayout_RightToLeft)
3792 {
3793 // Transform m_updateRegion under RTL
3794 m_updateRegion.Clear();
3795
3796 const int width = gdk_window_get_width(GTKGetDrawingWindow());
3797
3798 wxRegionIterator upd( m_nativeUpdateRegion );
3799 while (upd)
3800 {
3801 wxRect rect;
3802 rect.x = upd.GetX();
3803 rect.y = upd.GetY();
3804 rect.width = upd.GetWidth();
3805 rect.height = upd.GetHeight();
3806
3807 rect.x = width - rect.x - rect.width;
3808 m_updateRegion.Union( rect );
3809
3810 ++upd;
3811 }
3812 }
3813
3814 switch ( GetBackgroundStyle() )
3815 {
3816 case wxBG_STYLE_TRANSPARENT:
3817 #if wxGTK_HAS_COMPOSITING_SUPPORT
3818 if (IsTransparentBackgroundSupported())
3819 {
3820 // Set a transparent background, so that overlaying in parent
3821 // might indeed let see through where this child did not
3822 // explicitly paint.
3823 // NB: it works also for top level windows (but this is the
3824 // windows manager which then does the compositing job)
3825 #ifndef __WXGTK3__
3826 cr = gdk_cairo_create(m_wxwindow->window);
3827 gdk_cairo_region(cr, m_nativeUpdateRegion.GetRegion());
3828 cairo_clip(cr);
3829 #endif
3830 cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
3831 cairo_paint(cr);
3832 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
3833 #ifndef __WXGTK3__
3834 cairo_surface_flush(cairo_get_target(cr));
3835 #endif
3836 }
3837 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
3838 break;
3839
3840 case wxBG_STYLE_ERASE:
3841 {
3842 #ifdef __WXGTK3__
3843 wxGTKCairoDC dc(cr);
3844 #else
3845 wxWindowDC dc( (wxWindow*)this );
3846 dc.SetDeviceClippingRegion( m_updateRegion );
3847
3848 // Work around gtk-qt <= 0.60 bug whereby the window colour
3849 // remains grey
3850 if ( UseBgCol() &&
3851 wxSystemOptions::
3852 GetOptionInt("gtk.window.force-background-colour") )
3853 {
3854 dc.SetBackground(GetBackgroundColour());
3855 dc.Clear();
3856 }
3857 #endif // !__WXGTK3__
3858 wxEraseEvent erase_event( GetId(), &dc );
3859 erase_event.SetEventObject( this );
3860
3861 if ( HandleWindowEvent(erase_event) )
3862 {
3863 // background erased, don't do it again
3864 break;
3865 }
3866 }
3867 // fall through
3868
3869 case wxBG_STYLE_SYSTEM:
3870 if ( GetThemeEnabled() )
3871 {
3872 GdkWindow* gdkWindow = GTKGetDrawingWindow();
3873 const int w = gdk_window_get_width(gdkWindow);
3874 const int h = gdk_window_get_height(gdkWindow);
3875 #ifdef __WXGTK3__
3876 GtkStyleContext* sc = gtk_widget_get_style_context(m_wxwindow);
3877 gtk_render_background(sc, cr, 0, 0, w, h);
3878 #else
3879 // find ancestor from which to steal background
3880 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
3881 if (!parent)
3882 parent = (wxWindow*)this;
3883 GdkRectangle rect;
3884 m_nativeUpdateRegion.GetBox(rect.x, rect.y, rect.width, rect.height);
3885 gtk_paint_flat_box(gtk_widget_get_style(parent->m_widget),
3886 gdkWindow,
3887 gtk_widget_get_state(m_wxwindow),
3888 GTK_SHADOW_NONE,
3889 &rect,
3890 parent->m_widget,
3891 (char *)"base",
3892 0, 0, w, h);
3893 #endif // !__WXGTK3__
3894 }
3895 break;
3896
3897 case wxBG_STYLE_PAINT:
3898 // nothing to do: window will be painted over in EVT_PAINT
3899 break;
3900
3901 default:
3902 wxFAIL_MSG( "unsupported background style" );
3903 }
3904
3905 wxNcPaintEvent nc_paint_event( GetId() );
3906 nc_paint_event.SetEventObject( this );
3907 HandleWindowEvent( nc_paint_event );
3908
3909 wxPaintEvent paint_event( GetId() );
3910 paint_event.SetEventObject( this );
3911 HandleWindowEvent( paint_event );
3912
3913 #if wxGTK_HAS_COMPOSITING_SUPPORT
3914 if (IsTransparentBackgroundSupported())
3915 { // now composite children which need it
3916 // Overlay all our composite children on top of the painted area
3917 wxWindowList::compatibility_iterator node;
3918 for ( node = m_children.GetFirst(); node ; node = node->GetNext() )
3919 {
3920 wxWindow *compositeChild = node->GetData();
3921 if (compositeChild->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT)
3922 {
3923 #ifndef __WXGTK3__
3924 if (cr == NULL)
3925 {
3926 cr = gdk_cairo_create(m_wxwindow->window);
3927 gdk_cairo_region(cr, m_nativeUpdateRegion.GetRegion());
3928 cairo_clip(cr);
3929 }
3930 #endif // !__WXGTK3__
3931 GtkWidget *child = compositeChild->m_wxwindow;
3932 GtkAllocation alloc;
3933 gtk_widget_get_allocation(child, &alloc);
3934
3935 // The source data is the (composited) child
3936 gdk_cairo_set_source_window(
3937 cr, gtk_widget_get_window(child), alloc.x, alloc.y);
3938
3939 cairo_paint(cr);
3940 }
3941 }
3942 #ifndef __WXGTK3__
3943 if (cr)
3944 cairo_destroy(cr);
3945 #endif
3946 }
3947 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
3948
3949 m_clipPaintRegion = false;
3950 #ifdef __WXGTK3__
3951 m_paintContext = NULL;
3952 #endif
3953 m_updateRegion.Clear();
3954 m_nativeUpdateRegion.Clear();
3955 }
3956
3957 void wxWindowGTK::SetDoubleBuffered( bool on )
3958 {
3959 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3960
3961 if ( m_wxwindow )
3962 gtk_widget_set_double_buffered( m_wxwindow, on );
3963 }
3964
3965 bool wxWindowGTK::IsDoubleBuffered() const
3966 {
3967 return gtk_widget_get_double_buffered( m_wxwindow ) != 0;
3968 }
3969
3970 void wxWindowGTK::ClearBackground()
3971 {
3972 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3973 }
3974
3975 #if wxUSE_TOOLTIPS
3976 void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
3977 {
3978 if (m_tooltip != tip)
3979 {
3980 wxWindowBase::DoSetToolTip(tip);
3981
3982 if (m_tooltip)
3983 m_tooltip->GTKSetWindow(static_cast<wxWindow*>(this));
3984 else
3985 GTKApplyToolTip(NULL);
3986 }
3987 }
3988
3989 void wxWindowGTK::GTKApplyToolTip(const char* tip)
3990 {
3991 wxToolTip::GTKApply(GetConnectWidget(), tip);
3992 }
3993 #endif // wxUSE_TOOLTIPS
3994
3995 bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
3996 {
3997 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3998
3999 if (!wxWindowBase::SetBackgroundColour(colour))
4000 return false;
4001
4002 #ifndef __WXGTK3__
4003 if (colour.IsOk())
4004 {
4005 // We need the pixel value e.g. for background clearing.
4006 m_backgroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
4007 }
4008 #endif
4009
4010 // apply style change (forceStyle=true so that new style is applied
4011 // even if the bg colour changed from valid to wxNullColour)
4012 GTKApplyWidgetStyle(true);
4013
4014 return true;
4015 }
4016
4017 bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
4018 {
4019 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
4020
4021 if (!wxWindowBase::SetForegroundColour(colour))
4022 {
4023 return false;
4024 }
4025
4026 #ifndef __WXGTK3__
4027 if (colour.IsOk())
4028 {
4029 // We need the pixel value e.g. for background clearing.
4030 m_foregroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
4031 }
4032 #endif
4033
4034 // apply style change (forceStyle=true so that new style is applied
4035 // even if the bg colour changed from valid to wxNullColour):
4036 GTKApplyWidgetStyle(true);
4037
4038 return true;
4039 }
4040
4041 PangoContext *wxWindowGTK::GTKGetPangoDefaultContext()
4042 {
4043 return gtk_widget_get_pango_context( m_widget );
4044 }
4045
4046 #ifndef __WXGTK3__
4047 GtkRcStyle *wxWindowGTK::GTKCreateWidgetStyle(bool forceStyle)
4048 {
4049 // do we need to apply any changes at all?
4050 if ( !forceStyle &&
4051 !m_font.IsOk() &&
4052 !m_foregroundColour.IsOk() && !m_backgroundColour.IsOk() )
4053 {
4054 return NULL;
4055 }
4056
4057 GtkRcStyle *style = gtk_rc_style_new();
4058
4059 if ( m_font.IsOk() )
4060 {
4061 style->font_desc =
4062 pango_font_description_copy( m_font.GetNativeFontInfo()->description );
4063 }
4064
4065 int flagsNormal = 0,
4066 flagsPrelight = 0,
4067 flagsActive = 0,
4068 flagsInsensitive = 0;
4069
4070 if ( m_foregroundColour.IsOk() )
4071 {
4072 const GdkColor *fg = m_foregroundColour.GetColor();
4073
4074 style->fg[GTK_STATE_NORMAL] =
4075 style->text[GTK_STATE_NORMAL] = *fg;
4076 flagsNormal |= GTK_RC_FG | GTK_RC_TEXT;
4077
4078 style->fg[GTK_STATE_PRELIGHT] =
4079 style->text[GTK_STATE_PRELIGHT] = *fg;
4080 flagsPrelight |= GTK_RC_FG | GTK_RC_TEXT;
4081
4082 style->fg[GTK_STATE_ACTIVE] =
4083 style->text[GTK_STATE_ACTIVE] = *fg;
4084 flagsActive |= GTK_RC_FG | GTK_RC_TEXT;
4085 }
4086
4087 if ( m_backgroundColour.IsOk() )
4088 {
4089 const GdkColor *bg = m_backgroundColour.GetColor();
4090
4091 style->bg[GTK_STATE_NORMAL] =
4092 style->base[GTK_STATE_NORMAL] = *bg;
4093 flagsNormal |= GTK_RC_BG | GTK_RC_BASE;
4094
4095 style->bg[GTK_STATE_PRELIGHT] =
4096 style->base[GTK_STATE_PRELIGHT] = *bg;
4097 flagsPrelight |= GTK_RC_BG | GTK_RC_BASE;
4098
4099 style->bg[GTK_STATE_ACTIVE] =
4100 style->base[GTK_STATE_ACTIVE] = *bg;
4101 flagsActive |= GTK_RC_BG | GTK_RC_BASE;
4102
4103 style->bg[GTK_STATE_INSENSITIVE] =
4104 style->base[GTK_STATE_INSENSITIVE] = *bg;
4105 flagsInsensitive |= GTK_RC_BG | GTK_RC_BASE;
4106 }
4107
4108 style->color_flags[GTK_STATE_NORMAL] = (GtkRcFlags)flagsNormal;
4109 style->color_flags[GTK_STATE_PRELIGHT] = (GtkRcFlags)flagsPrelight;
4110 style->color_flags[GTK_STATE_ACTIVE] = (GtkRcFlags)flagsActive;
4111 style->color_flags[GTK_STATE_INSENSITIVE] = (GtkRcFlags)flagsInsensitive;
4112
4113 return style;
4114 }
4115 #endif // !__WXGTK3__
4116
4117 void wxWindowGTK::GTKApplyWidgetStyle(bool WXUNUSED_IN_GTK3(forceStyle))
4118 {
4119 #ifdef __WXGTK3__
4120 DoApplyWidgetStyle(NULL);
4121 #else
4122 GtkRcStyle *style = GTKCreateWidgetStyle(forceStyle);
4123 if ( style )
4124 {
4125 DoApplyWidgetStyle(style);
4126 g_object_unref(style);
4127 }
4128 #endif
4129
4130 // Style change may affect GTK+'s size calculation:
4131 InvalidateBestSize();
4132 }
4133
4134 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
4135 {
4136 GtkWidget* widget = m_wxwindow ? m_wxwindow : m_widget;
4137
4138 // block the signal temporarily to avoid sending
4139 // wxSysColourChangedEvents when we change the colours ourselves
4140 bool unblock = false;
4141 if (m_wxwindow && IsTopLevel())
4142 {
4143 unblock = true;
4144 g_signal_handlers_block_by_func(
4145 m_wxwindow, (void*)style_updated, this);
4146 }
4147
4148 GTKApplyStyle(widget, style);
4149
4150 if (unblock)
4151 {
4152 g_signal_handlers_unblock_by_func(
4153 m_wxwindow, (void*)style_updated, this);
4154 }
4155 }
4156
4157 void wxWindowGTK::GTKApplyStyle(GtkWidget* widget, GtkRcStyle* WXUNUSED_IN_GTK3(style))
4158 {
4159 #ifdef __WXGTK3__
4160 const PangoFontDescription* pfd = NULL;
4161 if (m_font.IsOk())
4162 pfd = pango_font_description_copy(m_font.GetNativeFontInfo()->description);
4163 gtk_widget_override_font(widget, pfd);
4164 gtk_widget_override_color(widget, GTK_STATE_FLAG_NORMAL, m_foregroundColour);
4165 gtk_widget_override_background_color(widget, GTK_STATE_FLAG_NORMAL, m_backgroundColour);
4166 #else
4167 gtk_widget_modify_style(widget, style);
4168 #endif
4169 }
4170
4171 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
4172 {
4173 if (!wxWindowBase::SetBackgroundStyle(style))
4174 return false;
4175
4176 #ifndef __WXGTK3__
4177 GdkWindow *window;
4178 if ( m_wxwindow )
4179 {
4180 window = GTKGetDrawingWindow();
4181 }
4182 else
4183 {
4184 GtkWidget * const w = GetConnectWidget();
4185 window = w ? gtk_widget_get_window(w) : NULL;
4186 }
4187
4188 bool wantNoBackPixmap = style == wxBG_STYLE_PAINT || style == wxBG_STYLE_TRANSPARENT;
4189
4190 if ( wantNoBackPixmap )
4191 {
4192 if (window)
4193 {
4194 // Make sure GDK/X11 doesn't refresh the window
4195 // automatically.
4196 gdk_window_set_back_pixmap( window, NULL, FALSE );
4197 m_needsStyleChange = false;
4198 }
4199 else // window not realized yet
4200 {
4201 // Do when window is realized
4202 m_needsStyleChange = true;
4203 }
4204
4205 // Don't apply widget style, or we get a grey background
4206 }
4207 else
4208 {
4209 // apply style change (forceStyle=true so that new style is applied
4210 // even if the bg colour changed from valid to wxNullColour):
4211 GTKApplyWidgetStyle(true);
4212 }
4213 #endif // !__WXGTK3__
4214
4215 return true;
4216 }
4217
4218 bool wxWindowGTK::IsTransparentBackgroundSupported(wxString* reason) const
4219 {
4220 #if wxGTK_HAS_COMPOSITING_SUPPORT
4221 #ifndef __WXGTK3__
4222 if (gtk_check_version(wxGTK_VERSION_REQUIRED_FOR_COMPOSITING) != NULL)
4223 {
4224 if (reason)
4225 {
4226 *reason = _("GTK+ installed on this machine is too old to "
4227 "support screen compositing, please install "
4228 "GTK+ 2.12 or later.");
4229 }
4230
4231 return false;
4232 }
4233 #endif // !__WXGTK3__
4234
4235 // NB: We don't check here if the particular kind of widget supports
4236 // transparency, we check only if it would be possible for a generic window
4237
4238 wxCHECK_MSG ( m_widget, false, "Window must be created first" );
4239
4240 if (!gdk_screen_is_composited(gtk_widget_get_screen(m_widget)))
4241 {
4242 if (reason)
4243 {
4244 *reason = _("Compositing not supported by this system, "
4245 "please enable it in your Window Manager.");
4246 }
4247
4248 return false;
4249 }
4250
4251 return true;
4252 #else
4253 if (reason)
4254 {
4255 *reason = _("This program was compiled with a too old version of GTK+, "
4256 "please rebuild with GTK+ 2.12 or newer.");
4257 }
4258
4259 return false;
4260 #endif // wxGTK_HAS_COMPOSITING_SUPPORT/!wxGTK_HAS_COMPOSITING_SUPPORT
4261 }
4262
4263 // ----------------------------------------------------------------------------
4264 // Pop-up menu stuff
4265 // ----------------------------------------------------------------------------
4266
4267 #if wxUSE_MENUS_NATIVE
4268
4269 extern "C" {
4270 static
4271 void wxPopupMenuPositionCallback( GtkMenu *menu,
4272 gint *x, gint *y,
4273 gboolean * WXUNUSED(whatever),
4274 gpointer user_data )
4275 {
4276 // ensure that the menu appears entirely on screen
4277 GtkRequisition req;
4278 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &req);
4279
4280 wxSize sizeScreen = wxGetDisplaySize();
4281 wxPoint *pos = (wxPoint*)user_data;
4282
4283 gint xmax = sizeScreen.x - req.width,
4284 ymax = sizeScreen.y - req.height;
4285
4286 *x = pos->x < xmax ? pos->x : xmax;
4287 *y = pos->y < ymax ? pos->y : ymax;
4288 }
4289 }
4290
4291 bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
4292 {
4293 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
4294
4295 menu->UpdateUI();
4296
4297 wxPoint pos;
4298 gpointer userdata;
4299 GtkMenuPositionFunc posfunc;
4300 if ( x == -1 && y == -1 )
4301 {
4302 // use GTK's default positioning algorithm
4303 userdata = NULL;
4304 posfunc = NULL;
4305 }
4306 else
4307 {
4308 pos = ClientToScreen(wxPoint(x, y));
4309 userdata = &pos;
4310 posfunc = wxPopupMenuPositionCallback;
4311 }
4312
4313 menu->m_popupShown = true;
4314 gtk_menu_popup(
4315 GTK_MENU(menu->m_menu),
4316 NULL, // parent menu shell
4317 NULL, // parent menu item
4318 posfunc, // function to position it
4319 userdata, // client data
4320 0, // button used to activate it
4321 gtk_get_current_event_time()
4322 );
4323
4324 while (menu->m_popupShown)
4325 {
4326 gtk_main_iteration();
4327 }
4328
4329 return true;
4330 }
4331
4332 #endif // wxUSE_MENUS_NATIVE
4333
4334 #if wxUSE_DRAG_AND_DROP
4335
4336 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
4337 {
4338 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4339
4340 GtkWidget *dnd_widget = GetConnectWidget();
4341
4342 if (m_dropTarget) m_dropTarget->GtkUnregisterWidget( dnd_widget );
4343
4344 if (m_dropTarget) delete m_dropTarget;
4345 m_dropTarget = dropTarget;
4346
4347 if (m_dropTarget) m_dropTarget->GtkRegisterWidget( dnd_widget );
4348 }
4349
4350 #endif // wxUSE_DRAG_AND_DROP
4351
4352 GtkWidget* wxWindowGTK::GetConnectWidget()
4353 {
4354 GtkWidget *connect_widget = m_widget;
4355 if (m_wxwindow) connect_widget = m_wxwindow;
4356
4357 return connect_widget;
4358 }
4359
4360 bool wxWindowGTK::GTKIsOwnWindow(GdkWindow *window) const
4361 {
4362 wxArrayGdkWindows windowsThis;
4363 GdkWindow * const winThis = GTKGetWindow(windowsThis);
4364
4365 return winThis ? window == winThis
4366 : windowsThis.Index(window) != wxNOT_FOUND;
4367 }
4368
4369 GdkWindow *wxWindowGTK::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
4370 {
4371 return m_wxwindow ? GTKGetDrawingWindow() : gtk_widget_get_window(m_widget);
4372 }
4373
4374 bool wxWindowGTK::SetFont( const wxFont &font )
4375 {
4376 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
4377
4378 if (!wxWindowBase::SetFont(font))
4379 return false;
4380
4381 // apply style change (forceStyle=true so that new style is applied
4382 // even if the font changed from valid to wxNullFont):
4383 GTKApplyWidgetStyle(true);
4384
4385 return true;
4386 }
4387
4388 void wxWindowGTK::DoCaptureMouse()
4389 {
4390 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4391
4392 GdkWindow *window = NULL;
4393 if (m_wxwindow)
4394 window = GTKGetDrawingWindow();
4395 else
4396 window = gtk_widget_get_window(GetConnectWidget());
4397
4398 wxCHECK_RET( window, wxT("CaptureMouse() failed") );
4399
4400 const wxCursor* cursor = &m_cursor;
4401 if (!cursor->IsOk())
4402 cursor = wxSTANDARD_CURSOR;
4403
4404 gdk_pointer_grab( window, FALSE,
4405 (GdkEventMask)
4406 (GDK_BUTTON_PRESS_MASK |
4407 GDK_BUTTON_RELEASE_MASK |
4408 GDK_POINTER_MOTION_HINT_MASK |
4409 GDK_POINTER_MOTION_MASK),
4410 NULL,
4411 cursor->GetCursor(),
4412 (guint32)GDK_CURRENT_TIME );
4413 g_captureWindow = this;
4414 g_captureWindowHasMouse = true;
4415 }
4416
4417 void wxWindowGTK::DoReleaseMouse()
4418 {
4419 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4420
4421 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
4422
4423 g_captureWindow = NULL;
4424
4425 GdkWindow *window = NULL;
4426 if (m_wxwindow)
4427 window = GTKGetDrawingWindow();
4428 else
4429 window = gtk_widget_get_window(GetConnectWidget());
4430
4431 if (!window)
4432 return;
4433
4434 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
4435 }
4436
4437 void wxWindowGTK::GTKReleaseMouseAndNotify()
4438 {
4439 DoReleaseMouse();
4440 wxMouseCaptureLostEvent evt(GetId());
4441 evt.SetEventObject( this );
4442 HandleWindowEvent( evt );
4443 }
4444
4445 /* static */
4446 wxWindow *wxWindowBase::GetCapture()
4447 {
4448 return (wxWindow *)g_captureWindow;
4449 }
4450
4451 bool wxWindowGTK::IsRetained() const
4452 {
4453 return false;
4454 }
4455
4456 void wxWindowGTK::SetScrollbar(int orient,
4457 int pos,
4458 int thumbVisible,
4459 int range,
4460 bool WXUNUSED(update))
4461 {
4462 const int dir = ScrollDirFromOrient(orient);
4463 GtkRange* const sb = m_scrollBar[dir];
4464 wxCHECK_RET( sb, wxT("this window is not scrollable") );
4465
4466 if (range <= 0)
4467 {
4468 // GtkRange requires upper > lower
4469 range =
4470 thumbVisible = 1;
4471 }
4472
4473 g_signal_handlers_block_by_func(
4474 sb, (void*)gtk_scrollbar_value_changed, this);
4475
4476 gtk_range_set_increments(sb, 1, thumbVisible);
4477 gtk_adjustment_set_page_size(gtk_range_get_adjustment(sb), thumbVisible);
4478 gtk_range_set_range(sb, 0, range);
4479 gtk_range_set_value(sb, pos);
4480 m_scrollPos[dir] = gtk_range_get_value(sb);
4481
4482 g_signal_handlers_unblock_by_func(
4483 sb, (void*)gtk_scrollbar_value_changed, this);
4484 }
4485
4486 void wxWindowGTK::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh))
4487 {
4488 const int dir = ScrollDirFromOrient(orient);
4489 GtkRange * const sb = m_scrollBar[dir];
4490 wxCHECK_RET( sb, wxT("this window is not scrollable") );
4491
4492 // This check is more than an optimization. Without it, the slider
4493 // will not move smoothly while tracking when using wxScrollHelper.
4494 if (GetScrollPos(orient) != pos)
4495 {
4496 g_signal_handlers_block_by_func(
4497 sb, (void*)gtk_scrollbar_value_changed, this);
4498
4499 gtk_range_set_value(sb, pos);
4500 m_scrollPos[dir] = gtk_range_get_value(sb);
4501
4502 g_signal_handlers_unblock_by_func(
4503 sb, (void*)gtk_scrollbar_value_changed, this);
4504 }
4505 }
4506
4507 int wxWindowGTK::GetScrollThumb(int orient) const
4508 {
4509 GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
4510 wxCHECK_MSG( sb, 0, wxT("this window is not scrollable") );
4511
4512 return wxRound(gtk_adjustment_get_page_size(gtk_range_get_adjustment(sb)));
4513 }
4514
4515 int wxWindowGTK::GetScrollPos( int orient ) const
4516 {
4517 GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
4518 wxCHECK_MSG( sb, 0, wxT("this window is not scrollable") );
4519
4520 return wxRound(gtk_range_get_value(sb));
4521 }
4522
4523 int wxWindowGTK::GetScrollRange( int orient ) const
4524 {
4525 GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
4526 wxCHECK_MSG( sb, 0, wxT("this window is not scrollable") );
4527
4528 return wxRound(gtk_adjustment_get_upper(gtk_range_get_adjustment(sb)));
4529 }
4530
4531 // Determine if increment is the same as +/-x, allowing for some small
4532 // difference due to possible inexactness in floating point arithmetic
4533 static inline bool IsScrollIncrement(double increment, double x)
4534 {
4535 wxASSERT(increment > 0);
4536 const double tolerance = 1.0 / 1024;
4537 return fabs(increment - fabs(x)) < tolerance;
4538 }
4539
4540 wxEventType wxWindowGTK::GTKGetScrollEventType(GtkRange* range)
4541 {
4542 wxASSERT(range == m_scrollBar[0] || range == m_scrollBar[1]);
4543
4544 const int barIndex = range == m_scrollBar[1];
4545
4546 const double value = gtk_range_get_value(range);
4547
4548 // save previous position
4549 const double oldPos = m_scrollPos[barIndex];
4550 // update current position
4551 m_scrollPos[barIndex] = value;
4552 // If event should be ignored, or integral position has not changed
4553 if (g_blockEventsOnDrag || wxRound(value) == wxRound(oldPos))
4554 {
4555 return wxEVT_NULL;
4556 }
4557
4558 wxEventType eventType = wxEVT_SCROLL_THUMBTRACK;
4559 if (!m_isScrolling)
4560 {
4561 // Difference from last change event
4562 const double diff = value - oldPos;
4563 const bool isDown = diff > 0;
4564
4565 GtkAdjustment* adj = gtk_range_get_adjustment(range);
4566 if (IsScrollIncrement(gtk_adjustment_get_step_increment(adj), diff))
4567 {
4568 eventType = isDown ? wxEVT_SCROLL_LINEDOWN : wxEVT_SCROLL_LINEUP;
4569 }
4570 else if (IsScrollIncrement(gtk_adjustment_get_page_increment(adj), diff))
4571 {
4572 eventType = isDown ? wxEVT_SCROLL_PAGEDOWN : wxEVT_SCROLL_PAGEUP;
4573 }
4574 else if (m_mouseButtonDown)
4575 {
4576 // Assume track event
4577 m_isScrolling = true;
4578 }
4579 }
4580 return eventType;
4581 }
4582
4583 void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
4584 {
4585 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4586
4587 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4588
4589 // No scrolling requested.
4590 if ((dx == 0) && (dy == 0)) return;
4591
4592 m_clipPaintRegion = true;
4593
4594 WX_PIZZA(m_wxwindow)->scroll(dx, dy);
4595
4596 m_clipPaintRegion = false;
4597
4598 #if wxUSE_CARET
4599 bool restoreCaret = (GetCaret() != NULL && GetCaret()->IsVisible());
4600 if (restoreCaret)
4601 {
4602 wxRect caretRect(GetCaret()->GetPosition(), GetCaret()->GetSize());
4603 if (dx > 0)
4604 caretRect.width += dx;
4605 else
4606 {
4607 caretRect.x += dx; caretRect.width -= dx;
4608 }
4609 if (dy > 0)
4610 caretRect.height += dy;
4611 else
4612 {
4613 caretRect.y += dy; caretRect.height -= dy;
4614 }
4615
4616 RefreshRect(caretRect);
4617 }
4618 #endif // wxUSE_CARET
4619 }
4620
4621 void wxWindowGTK::GTKScrolledWindowSetBorder(GtkWidget* w, int wxstyle)
4622 {
4623 //RN: Note that static controls usually have no border on gtk, so maybe
4624 //it makes sense to treat that as simply no border at the wx level
4625 //as well...
4626 if (!(wxstyle & wxNO_BORDER) && !(wxstyle & wxBORDER_STATIC))
4627 {
4628 GtkShadowType gtkstyle;
4629
4630 if(wxstyle & wxBORDER_RAISED)
4631 gtkstyle = GTK_SHADOW_OUT;
4632 else if ((wxstyle & wxBORDER_SUNKEN) || (wxstyle & wxBORDER_THEME))
4633 gtkstyle = GTK_SHADOW_IN;
4634 #if 0
4635 // Now obsolete
4636 else if (wxstyle & wxBORDER_DOUBLE)
4637 gtkstyle = GTK_SHADOW_ETCHED_IN;
4638 #endif
4639 else //default
4640 gtkstyle = GTK_SHADOW_IN;
4641
4642 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w),
4643 gtkstyle );
4644 }
4645 }
4646
4647 // Find the wxWindow at the current mouse position, also returning the mouse
4648 // position.
4649 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4650 {
4651 pt = wxGetMousePosition();
4652 wxWindow* found = wxFindWindowAtPoint(pt);
4653 return found;
4654 }
4655
4656 // Get the current mouse position.
4657 wxPoint wxGetMousePosition()
4658 {
4659 int x, y;
4660 GdkModifierType unused;
4661 GetMouseState(x, y, unused);
4662 return wxPoint(x, y);
4663 }
4664
4665 GdkWindow* wxWindowGTK::GTKGetDrawingWindow() const
4666 {
4667 GdkWindow* window = NULL;
4668 if (m_wxwindow)
4669 window = gtk_widget_get_window(m_wxwindow);
4670 return window;
4671 }
4672
4673 // ----------------------------------------------------------------------------
4674 // freeze/thaw
4675 // ----------------------------------------------------------------------------
4676
4677 void wxWindowGTK::GTKFreezeWidget(GtkWidget* widget)
4678 {
4679 if (widget && gtk_widget_get_has_window(widget))
4680 {
4681 GdkWindow* window = gtk_widget_get_window(widget);
4682 if (window)
4683 gdk_window_freeze_updates(window);
4684 }
4685 }
4686
4687 void wxWindowGTK::GTKThawWidget(GtkWidget* widget)
4688 {
4689 if (widget && gtk_widget_get_has_window(widget))
4690 {
4691 GdkWindow* window = gtk_widget_get_window(widget);
4692 if (window)
4693 gdk_window_thaw_updates(window);
4694 }
4695 }
4696
4697 void wxWindowGTK::DoFreeze()
4698 {
4699 GtkWidget* widget = m_wxwindow;
4700 if (widget == NULL)
4701 widget = m_widget;
4702 GTKFreezeWidget(widget);
4703 }
4704
4705 void wxWindowGTK::DoThaw()
4706 {
4707 GtkWidget* widget = m_wxwindow;
4708 if (widget == NULL)
4709 widget = m_widget;
4710 GTKThawWidget(widget);
4711 }