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