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/display.h"
30 #include "wx/dnd.h"
31 #include "wx/tooltip.h"
32 #include "wx/caret.h"
33 #include "wx/fontutil.h"
34 #include "wx/sysopt.h"
35 #ifdef __WXGTK3__
36     #include "wx/gtk/dc.h"
37 #endif
38 
39 #include <ctype.h>
40 
41 #include "wx/gtk/private.h"
42 #include "wx/gtk/private/gtk3-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 #ifdef __WXGTK4__
56 #define wxGTK_HAS_COMPOSITING_SUPPORT 0
57 #else
58 // gdk_window_set_composited() is only supported since 2.12
59 #define wxGTK_HAS_COMPOSITING_SUPPORT (GTK_CHECK_VERSION(2,12,0) && wxUSE_CAIRO)
60 #endif
61 
62 #ifndef PANGO_VERSION_CHECK
63     #define PANGO_VERSION_CHECK(a,b,c) 0
64 #endif
65 
66 //-----------------------------------------------------------------------------
67 // documentation on internals
68 //-----------------------------------------------------------------------------
69 
70 /*
71    I have been asked several times about writing some documentation about
72    the GTK port of wxWidgets, especially its internal structures. Obviously,
73    you cannot understand wxGTK without knowing a little about the GTK, but
74    some more information about what the wxWindow, which is the base class
75    for all other window classes, does seems required as well.
76 
77    I)
78 
79    What does wxWindow do? It contains the common interface for the following
80    jobs of its descendants:
81 
82    1) Define the rudimentary behaviour common to all window classes, such as
83    resizing, intercepting user input (so as to make it possible to use these
84    events for special purposes in a derived class), window names etc.
85 
86    2) Provide the possibility to contain and manage children, if the derived
87    class is allowed to contain children, which holds true for those window
88    classes which do not display a native GTK widget. To name them, these
89    classes are wxPanel, wxScrolledWindow, wxDialog, wxFrame. The MDI frame-
90    work classes are a special case and are handled a bit differently from
91    the rest. The same holds true for the wxNotebook class.
92 
93    3) Provide the possibility to draw into a client area of a window. This,
94    too, only holds true for classes that do not display a native GTK widget
95    as above.
96 
97    4) Provide the entire mechanism for scrolling widgets. This actual inter-
98    face for this is usually in wxScrolledWindow, but the GTK implementation
99    is in this class.
100 
101    5) A multitude of helper or extra methods for special purposes, such as
102    Drag'n'Drop, managing validators etc.
103 
104    6) Display a border (sunken, raised, simple or none).
105 
106    Normally one might expect, that one wxWidgets window would always correspond
107    to one GTK widget. Under GTK, there is no such all-round widget that has all
108    the functionality. Moreover, the GTK defines a client area as a different
109    widget from the actual widget you are handling. Last but not least some
110    special classes (e.g. wxFrame) handle different categories of widgets and
111    still have the possibility to draw something in the client area.
112    It was therefore required to write a special purpose GTK widget, that would
113    represent a client area in the sense of wxWidgets capable to do the jobs
114    2), 3) and 4). I have written this class and it resides in win_gtk.c of
115    this directory.
116 
117    All windows must have a widget, with which they interact with other under-
118    lying GTK widgets. It is this widget, e.g. that has to be resized etc and
119    the wxWindow class has a member variable called m_widget which holds a
120    pointer to this widget. When the window class represents a GTK native widget,
121    this is (in most cases) the only GTK widget the class manages. E.g. the
122    wxStaticText class handles only a GtkLabel widget a pointer to which you
123    can find in m_widget (defined in wxWindow)
124 
125    When the class has a client area for drawing into and for containing children
126    it has to handle the client area widget (of the type wxPizza, defined in
127    win_gtk.cpp), but there could be any number of widgets, handled by a class.
128    The common rule for all windows is only, that the widget that interacts with
129    the rest of GTK must be referenced in m_widget and all other widgets must be
130    children of this widget on the GTK level. The top-most widget, which also
131    represents the client area, must be in the m_wxwindow field and must be of
132    the type wxPizza.
133 
134    As I said, the window classes that display a GTK native widget only have
135    one widget, so in the case of e.g. the wxButton class m_widget holds a
136    pointer to a GtkButton widget. But windows with client areas (for drawing
137    and children) have a m_widget field that is a pointer to a GtkScrolled-
138    Window and a m_wxwindow field that is pointer to a wxPizza and this
139    one is (in the GTK sense) a child of the GtkScrolledWindow.
140 
141    If the m_wxwindow field is set, then all input to this widget is inter-
142    cepted and sent to the wxWidgets class. If not, all input to the widget
143    that gets pointed to by m_widget gets intercepted and sent to the class.
144 
145    II)
146 
147    The design of scrolling in wxWidgets is markedly different from that offered
148    by the GTK itself and therefore we cannot simply take it as it is. In GTK,
149    clicking on a scrollbar belonging to scrolled window will inevitably move
150    the window. In wxWidgets, the scrollbar will only emit an event, send this
151    to (normally) a wxScrolledWindow and that class will call ScrollWindow()
152    which actually moves the window and its sub-windows. Note that wxPizza
153    memorizes how much it has been scrolled but that wxWidgets forgets this
154    so that the two coordinates systems have to be kept in synch. This is done
155    in various places using the pizza->m_scroll_x and pizza->m_scroll_y values.
156 
157    III)
158 
159    Singularly the most broken code in GTK is the code that is supposed to
160    inform subwindows (child windows) about new positions. Very often, duplicate
161    events are sent without changes in size or position, equally often no
162    events are sent at all (All this is due to a bug in the GtkContainer code
163    which got fixed in GTK 1.2.6). For that reason, wxGTK completely ignores
164    GTK's own system and it simply waits for size events for toplevel windows
165    and then iterates down the respective size events to all window. This has
166    the disadvantage that windows might get size events before the GTK widget
167    actually has the reported size. This doesn't normally pose any problem, but
168    the OpenGL drawing routines rely on correct behaviour. Therefore, I have
169    added the m_nativeSizeEvents flag, which is true only for the OpenGL canvas,
170    i.e. the wxGLCanvas will emit a size event, when (and not before) the X11
171    window that is used for OpenGL output really has that size (as reported by
172    GTK).
173 
174    IV)
175 
176    If someone at some point of time feels the immense desire to have a look at,
177    change or attempt to optimise the Refresh() logic, this person will need an
178    intimate understanding of what "draw" and "expose" events are and what
179    they are used for, in particular when used in connection with GTK's
180    own windowless widgets. Beware.
181 
182    V)
183 
184    Cursors, too, have been a constant source of pleasure. The main difficulty
185    is that a GdkWindow inherits a cursor if the programmer sets a new cursor
186    for the parent. To prevent this from doing too much harm, SetCursor calls
187    GTKUpdateCursor, which will recursively re-set the cursors of all child windows.
188    Also don't forget that cursors (like much else) are connected to GdkWindows,
189    not GtkWidgets and that the "window" field of a GtkWidget might very well
190    point to the GdkWindow of the parent widget (-> "window-less widget") and
191    that the two obviously have very different meanings.
192 */
193 
194 //-----------------------------------------------------------------------------
195 // data
196 //-----------------------------------------------------------------------------
197 
198 // Don't allow event propagation during drag
199 bool g_blockEventsOnDrag;
200 // Don't allow mouse event propagation during scroll
201 bool g_blockEventsOnScroll;
202 extern wxCursor g_globalCursor;
203 
204 // mouse capture state: the window which has it and if the mouse is currently
205 // inside it
206 static wxWindowGTK  *g_captureWindow = NULL;
207 static bool g_captureWindowHasMouse = false;
208 
209 // The window that currently has focus:
210 static wxWindowGTK *gs_currentFocus = NULL;
211 // The window that is scheduled to get focus in the next event loop iteration
212 // or NULL if there's no pending focus change:
213 static wxWindowGTK *gs_pendingFocus = NULL;
214 // The window that had focus before we lost it last time:
215 static wxWindowGTK *gs_lastFocus = NULL;
216 
217 // the window that has deferred focus-out event pending, if any (see
218 // GTKAddDeferredFocusOut() for details)
219 static wxWindowGTK *gs_deferredFocusOut = NULL;
220 
221 // global variables because GTK+ DnD want to have the
222 // mouse event that caused it
223 GdkEvent    *g_lastMouseEvent = NULL;
224 int          g_lastButtonNumber = 0;
225 
226 #ifdef __WXGTK3__
227 static GList* gs_sizeRevalidateList;
228 #endif
229 bool g_inSizeAllocate;
230 
231 #if GTK_CHECK_VERSION(3,14,0)
232     #define wxGTK_HAS_GESTURES_SUPPORT
233 #endif
234 
235 #ifdef wxGTK_HAS_GESTURES_SUPPORT
236 
237 #include "wx/hashmap.h"
238 #include "wx/private/extfield.h"
239 
240 namespace
241 {
242 
243 // Per-window data for gestures support.
244 class wxWindowGesturesData
245 {
246 public:
247     // This class has rather unusual "resurrectable" semantics: it is
248     // initialized by the ctor as usual, but may then be uninitialized by
249     // calling Free() and re-initialized again by calling Reinit().
wxWindowGesturesData(wxWindowGTK * win,GtkWidget * widget,int eventsMask)250     wxWindowGesturesData(wxWindowGTK* win, GtkWidget *widget, int eventsMask)
251     {
252         Reinit(win, widget, eventsMask);
253     }
254 
~wxWindowGesturesData()255     ~wxWindowGesturesData()
256     {
257         Free();
258     }
259 
260     void Reinit(wxWindowGTK* win, GtkWidget *widget, int eventsMask);
261     void Free();
262 
263     unsigned int         m_touchCount;
264     unsigned int         m_lastTouchTime;
265     int                  m_gestureState;
266     int                  m_allowedGestures;
267     int                  m_activeGestures;
268     wxPoint              m_lastTouchPoint;
269     GdkEventSequence*    m_touchSequence;
270 
271     GtkGesture* m_vertical_pan_gesture;
272     GtkGesture* m_horizontal_pan_gesture;
273     GtkGesture* m_zoom_gesture;
274     GtkGesture* m_rotate_gesture;
275     GtkGesture* m_long_press_gesture;
276 };
277 
278 WX_DECLARE_HASH_MAP(wxWindow*, wxWindowGesturesData*,
279                     wxPointerHash, wxPointerEqual,
280                     wxWindowGesturesMap);
281 
282 typedef wxExternalField<wxWindow,
283                         wxWindowGesturesData,
284                         wxWindowGesturesMap> wxWindowGestures;
285 
286 } // anonymous namespace
287 
288 // This is true when the gesture has just started (currently used for pan gesture only)
289 static bool gs_gestureStart = false;
290 
291 // Last offset for the pan gesture, this is used to calculate deltas for pan gesture event
292 static double gs_lastOffset = 0;
293 
294 // Last scale provided by GTK
295 static gdouble gs_lastScale = 1.0;
296 
297 // This is used to set the angle when rotate gesture ends.
298 static gdouble gs_lastAngle = 0;
299 
300 // Last Zoom/Rotate gesture point
301 static wxPoint gs_lastGesturePoint;
302 
303 #endif // wxGTK_HAS_GESTURES_SUPPORT
304 
305 //-----------------------------------------------------------------------------
306 // debug
307 //-----------------------------------------------------------------------------
308 
309 // the trace mask used for the focus debugging messages
310 #define TRACE_FOCUS wxT("focus")
311 
312 #if wxUSE_LOG_TRACE
313 // Function used to dump a brief description of a window.
314 static
wxDumpWindow(wxWindowGTK * win)315 wxString wxDumpWindow(wxWindowGTK* win)
316 {
317     if ( !win )
318         return "(no window)";
319 
320     wxString s = wxString::Format("%s(%p",
321                                   win->GetClassInfo()->GetClassName(), win);
322 
323     wxString label = win->GetLabel();
324     if ( !label.empty() )
325         s += wxString::Format(", \"%s\"", label);
326     s += ")";
327 
328     return s;
329 }
330 #endif // wxUSE_LOG_TRACE
331 
332 // A handy function to run from under gdb to show information about the given
333 // GtkWidget. Right now it only shows its type, we could enhance it to show
334 // more information later but this is already pretty useful.
wxDumpGtkWidget(GtkWidget * w)335 const char* wxDumpGtkWidget(GtkWidget* w)
336 {
337     static wxString s;
338     s.Printf("GtkWidget %p, type \"%s\"", w, G_OBJECT_TYPE_NAME(w));
339 
340     return s.c_str();
341 }
342 
343 //-----------------------------------------------------------------------------
344 // global top level GtkWidget/GdkWindow
345 //-----------------------------------------------------------------------------
346 
wxGetTopLevel(GtkWidget ** widget,GdkWindow ** window)347 static bool wxGetTopLevel(GtkWidget** widget, GdkWindow** window)
348 {
349     wxWindowList::const_iterator i = wxTopLevelWindows.begin();
350     for (; i != wxTopLevelWindows.end(); ++i)
351     {
352         const wxWindow* win = *i;
353         if (win->m_widget)
354         {
355             GdkWindow* gdkwin = gtk_widget_get_window(win->m_widget);
356             if (gdkwin)
357             {
358                 if (widget)
359                     *widget = win->m_widget;
360                 if (window)
361                     *window = gdkwin;
362                 return true;
363             }
364         }
365     }
366     return false;
367 }
368 
wxGetTopLevelGTK()369 GtkWidget* wxGetTopLevelGTK()
370 {
371     GtkWidget* widget = NULL;
372     wxGetTopLevel(&widget, NULL);
373     return widget;
374 }
375 
wxGetTopLevelGDK()376 GdkWindow* wxGetTopLevelGDK()
377 {
378     GdkWindow* window;
379     if (!wxGetTopLevel(NULL, &window))
380         window = gdk_get_default_root_window();
381     return window;
382 }
383 
wxGetPangoContext()384 PangoContext* wxGetPangoContext()
385 {
386     PangoContext* context = NULL;
387     GtkWidget* widget;
388     if (wxGetTopLevel(&widget, NULL))
389     {
390         context = gtk_widget_get_pango_context(widget);
391         g_object_ref(context);
392     }
393     else
394     {
395         if ( GdkScreen *screen = gdk_screen_get_default() )
396         {
397             context = gdk_pango_context_get_for_screen(screen);
398         }
399 #if PANGO_VERSION_CHECK(1,22,0)
400         else // No default screen.
401         {
402             // This may happen in console applications which didn't open the
403             // display, use the default font map for them -- it's better than
404             // nothing.
405             if (wx_pango_version_check(1,22,0) == 0)
406             {
407                 context = pango_font_map_create_context(
408                                 pango_cairo_font_map_get_default ());
409             }
410             //else: pango_font_map_create_context() not available
411         }
412 #endif // Pango 1.22+
413     }
414 
415     return context;
416 }
417 
418 //-----------------------------------------------------------------------------
419 // "expose_event"/"draw" from m_wxwindow
420 //-----------------------------------------------------------------------------
421 
422 extern "C" {
423 #ifdef __WXGTK3__
draw(GtkWidget *,cairo_t * cr,wxWindow * win)424 static gboolean draw(GtkWidget*, cairo_t* cr, wxWindow* win)
425 {
426     if (gtk_cairo_should_draw_window(cr, win->GTKGetDrawingWindow()))
427         win->GTKSendPaintEvents(cr);
428 
429     return false;
430 }
431 #else // !__WXGTK3__
432 static gboolean expose_event(GtkWidget*, GdkEventExpose* gdk_event, wxWindow* win)
433 {
434     if (gdk_event->window == win->GTKGetDrawingWindow())
435         win->GTKSendPaintEvents(gdk_event->region);
436 
437     return false;
438 }
439 #endif // !__WXGTK3__
440 }
441 
442 #ifndef __WXUNIVERSAL__
443 //-----------------------------------------------------------------------------
444 // "expose_event"/"draw" from m_wxwindow->parent, for drawing border
445 //-----------------------------------------------------------------------------
446 
447 extern "C" {
448 static gboolean
449 #ifdef __WXGTK3__
draw_border(GtkWidget * widget,cairo_t * cr,wxWindow * win)450 draw_border(GtkWidget* widget, cairo_t* cr, wxWindow* win)
451 #else
452 draw_border(GtkWidget* widget, GdkEventExpose* gdk_event, wxWindow* win)
453 #endif
454 {
455 #ifdef __WXGTK3__
456     if (!gtk_cairo_should_draw_window(cr, gtk_widget_get_parent_window(win->m_wxwindow)))
457 #else
458     if (gdk_event->window != gtk_widget_get_parent_window(win->m_wxwindow))
459 #endif
460         return false;
461 
462     if (!win->IsShown())
463         return false;
464 
465     GtkAllocation alloc;
466     gtk_widget_get_allocation(win->m_wxwindow, &alloc);
467     int x = alloc.x;
468     int y = alloc.y;
469     const int w = alloc.width;
470     const int h = alloc.height;
471 #ifdef __WXGTK3__
472     if (!gtk_widget_get_has_window(widget))
473     {
474         // cairo_t origin is set to widget's origin, need to adjust
475         // coordinates for child when they are not relative to parent
476         gtk_widget_get_allocation(widget, &alloc);
477         x -= alloc.x;
478         y -= alloc.y;
479     }
480 #endif
481 
482     if (w <= 0 || h <= 0)
483         return false;
484 
485     if (win->HasFlag(wxBORDER_SIMPLE))
486     {
487 #ifdef __WXGTK3__
488         GtkStyleContext* sc = gtk_widget_get_style_context(win->m_wxwindow);
489         GdkRGBA* c;
490         gtk_style_context_save(sc);
491         gtk_style_context_set_state(sc, GTK_STATE_FLAG_NORMAL);
492         gtk_style_context_get(sc, GTK_STATE_FLAG_NORMAL, "border-color", &c, NULL);
493         gtk_style_context_restore(sc);
494         gdk_cairo_set_source_rgba(cr, c);
495         gdk_rgba_free(c);
496         cairo_set_line_width(cr, 1);
497         cairo_rectangle(cr, x + 0.5, y + 0.5, w - 1, h - 1);
498         cairo_stroke(cr);
499 #else
500         gdk_draw_rectangle(gdk_event->window,
501             gtk_widget_get_style(widget)->black_gc, false, x, y, w - 1, h - 1);
502 #endif
503     }
504     else if (win->HasFlag(wxBORDER_RAISED | wxBORDER_SUNKEN | wxBORDER_THEME))
505     {
506 #ifdef __WXGTK3__
507         //TODO: wxBORDER_RAISED/wxBORDER_SUNKEN
508         GtkStyleContext* sc;
509         if (win->HasFlag(wxHSCROLL | wxVSCROLL))
510             sc = gtk_widget_get_style_context(wxGTKPrivate::GetTreeWidget());
511         else
512             sc = gtk_widget_get_style_context(wxGTKPrivate::GetEntryWidget());
513 
514         gtk_render_frame(sc, cr, x, y, w, h);
515 #else // !__WXGTK3__
516         GtkShadowType shadow = GTK_SHADOW_IN;
517         if (win->HasFlag(wxBORDER_RAISED))
518             shadow = GTK_SHADOW_OUT;
519 
520         GtkStyle* style;
521         const char* detail;
522         if (win->HasFlag(wxHSCROLL | wxVSCROLL))
523         {
524             style = gtk_widget_get_style(wxGTKPrivate::GetTreeWidget());
525             detail = "viewport";
526         }
527         else
528         {
529             style = gtk_widget_get_style(wxGTKPrivate::GetEntryWidget());
530             detail = "entry";
531         }
532 
533         // clip rect is required to avoid painting background
534         // over upper left (w,h) of parent window
535         GdkRectangle clipRect = { x, y, w, h };
536         gtk_paint_shadow(
537            style, gdk_event->window, GTK_STATE_NORMAL,
538            shadow, &clipRect, widget, detail, x, y, w, h);
539 #endif // !__WXGTK3__
540     }
541     return false;
542 }
543 }
544 
545 //-----------------------------------------------------------------------------
546 // "parent_set" from m_wxwindow
547 //-----------------------------------------------------------------------------
548 
549 extern "C" {
550 static void
parent_set(GtkWidget * widget,GtkWidget * old_parent,wxWindow * win)551 parent_set(GtkWidget* widget, GtkWidget* old_parent, wxWindow* win)
552 {
553     if (old_parent)
554     {
555         g_signal_handlers_disconnect_by_func(
556             old_parent, (void*)draw_border, win);
557     }
558     GtkWidget* parent = gtk_widget_get_parent(widget);
559     if (parent)
560     {
561 #ifdef __WXGTK3__
562         g_signal_connect_after(parent, "draw", G_CALLBACK(draw_border), win);
563 #else
564         g_signal_connect_after(parent, "expose_event", G_CALLBACK(draw_border), win);
565 #endif
566     }
567 }
568 }
569 #endif // !__WXUNIVERSAL__
570 
571 //-----------------------------------------------------------------------------
572 // "key_press_event" from any window
573 //-----------------------------------------------------------------------------
574 
575 // set WXTRACE to this to see the key event codes on the console
576 #define TRACE_KEYS  wxT("keyevent")
577 
578 // translates an X key symbol to WXK_XXX value
579 //
580 // if isChar is true it means that the value returned will be used for EVT_CHAR
581 // event and then we choose the logical WXK_XXX, i.e. '/' for GDK_KP_Divide,
582 // for example, while if it is false it means that the value is going to be
583 // used for KEY_DOWN/UP events and then we translate GDK_KP_Divide to
584 // WXK_NUMPAD_DIVIDE
wxTranslateKeySymToWXKey(KeySym keysym,bool isChar)585 static long wxTranslateKeySymToWXKey(KeySym keysym, bool isChar)
586 {
587     long key_code;
588 
589     switch ( keysym )
590     {
591         // Shift, Control and Alt don't generate the CHAR events at all
592         case GDK_KEY_Shift_L:
593         case GDK_KEY_Shift_R:
594             key_code = isChar ? 0 : WXK_SHIFT;
595             break;
596         case GDK_KEY_Control_L:
597         case GDK_KEY_Control_R:
598             key_code = isChar ? 0 : WXK_CONTROL;
599             break;
600         case GDK_KEY_Meta_L:
601         case GDK_KEY_Meta_R:
602         case GDK_KEY_Alt_L:
603         case GDK_KEY_Alt_R:
604         case GDK_KEY_Super_L:
605         case GDK_KEY_Super_R:
606             key_code = isChar ? 0 : WXK_ALT;
607             break;
608 
609         // neither do the toggle modifies
610         case GDK_KEY_Scroll_Lock:
611             key_code = isChar ? 0 : WXK_SCROLL;
612             break;
613 
614         case GDK_KEY_Caps_Lock:
615             key_code = isChar ? 0 : WXK_CAPITAL;
616             break;
617 
618         case GDK_KEY_Num_Lock:
619             key_code = isChar ? 0 : WXK_NUMLOCK;
620             break;
621 
622 
623         // various other special keys
624         case GDK_KEY_Menu:
625             key_code = WXK_MENU;
626             break;
627 
628         case GDK_KEY_Help:
629             key_code = WXK_HELP;
630             break;
631 
632         case GDK_KEY_BackSpace:
633             key_code = WXK_BACK;
634             break;
635 
636         case GDK_KEY_ISO_Left_Tab:
637         case GDK_KEY_Tab:
638             key_code = WXK_TAB;
639             break;
640 
641         case GDK_KEY_Linefeed:
642         case GDK_KEY_Return:
643             key_code = WXK_RETURN;
644             break;
645 
646         case GDK_KEY_Clear:
647             key_code = WXK_CLEAR;
648             break;
649 
650         case GDK_KEY_Pause:
651             key_code = WXK_PAUSE;
652             break;
653 
654         case GDK_KEY_Select:
655             key_code = WXK_SELECT;
656             break;
657 
658         case GDK_KEY_Print:
659             key_code = WXK_PRINT;
660             break;
661 
662         case GDK_KEY_Execute:
663             key_code = WXK_EXECUTE;
664             break;
665 
666         case GDK_KEY_Escape:
667             key_code = WXK_ESCAPE;
668             break;
669 
670         // cursor and other extended keyboard keys
671         case GDK_KEY_Delete:
672             key_code = WXK_DELETE;
673             break;
674 
675         case GDK_KEY_Home:
676             key_code = WXK_HOME;
677             break;
678 
679         case GDK_KEY_Left:
680             key_code = WXK_LEFT;
681             break;
682 
683         case GDK_KEY_Up:
684             key_code = WXK_UP;
685             break;
686 
687         case GDK_KEY_Right:
688             key_code = WXK_RIGHT;
689             break;
690 
691         case GDK_KEY_Down:
692             key_code = WXK_DOWN;
693             break;
694 
695         case GDK_KEY_Prior:     // == GDK_KEY_Page_Up
696             key_code = WXK_PAGEUP;
697             break;
698 
699         case GDK_KEY_Next:      // == GDK_KEY_Page_Down
700             key_code = WXK_PAGEDOWN;
701             break;
702 
703         case GDK_KEY_End:
704             key_code = WXK_END;
705             break;
706 
707         case GDK_KEY_Begin:
708             key_code = WXK_HOME;
709             break;
710 
711         case GDK_KEY_Insert:
712             key_code = WXK_INSERT;
713             break;
714 
715 
716         // numpad keys
717         case GDK_KEY_KP_0:
718         case GDK_KEY_KP_1:
719         case GDK_KEY_KP_2:
720         case GDK_KEY_KP_3:
721         case GDK_KEY_KP_4:
722         case GDK_KEY_KP_5:
723         case GDK_KEY_KP_6:
724         case GDK_KEY_KP_7:
725         case GDK_KEY_KP_8:
726         case GDK_KEY_KP_9:
727             key_code = (isChar ? '0' : int(WXK_NUMPAD0)) + keysym - GDK_KEY_KP_0;
728             break;
729 
730         case GDK_KEY_KP_Space:
731             key_code = isChar ? ' ' : int(WXK_NUMPAD_SPACE);
732             break;
733 
734         case GDK_KEY_KP_Tab:
735             key_code = isChar ? WXK_TAB : WXK_NUMPAD_TAB;
736             break;
737 
738         case GDK_KEY_KP_Enter:
739             key_code = isChar ? WXK_RETURN : WXK_NUMPAD_ENTER;
740             break;
741 
742         case GDK_KEY_KP_F1:
743             key_code = isChar ? WXK_F1 : WXK_NUMPAD_F1;
744             break;
745 
746         case GDK_KEY_KP_F2:
747             key_code = isChar ? WXK_F2 : WXK_NUMPAD_F2;
748             break;
749 
750         case GDK_KEY_KP_F3:
751             key_code = isChar ? WXK_F3 : WXK_NUMPAD_F3;
752             break;
753 
754         case GDK_KEY_KP_F4:
755             key_code = isChar ? WXK_F4 : WXK_NUMPAD_F4;
756             break;
757 
758         case GDK_KEY_KP_Home:
759             key_code = isChar ? WXK_HOME : WXK_NUMPAD_HOME;
760             break;
761 
762         case GDK_KEY_KP_Left:
763             key_code = isChar ? WXK_LEFT : WXK_NUMPAD_LEFT;
764             break;
765 
766         case GDK_KEY_KP_Up:
767             key_code = isChar ? WXK_UP : WXK_NUMPAD_UP;
768             break;
769 
770         case GDK_KEY_KP_Right:
771             key_code = isChar ? WXK_RIGHT : WXK_NUMPAD_RIGHT;
772             break;
773 
774         case GDK_KEY_KP_Down:
775             key_code = isChar ? WXK_DOWN : WXK_NUMPAD_DOWN;
776             break;
777 
778         case GDK_KEY_KP_Prior: // == GDK_KP_Page_Up
779             key_code = isChar ? WXK_PAGEUP : WXK_NUMPAD_PAGEUP;
780             break;
781 
782         case GDK_KEY_KP_Next: // == GDK_KP_Page_Down
783             key_code = isChar ? WXK_PAGEDOWN : WXK_NUMPAD_PAGEDOWN;
784             break;
785 
786         case GDK_KEY_KP_End:
787             key_code = isChar ? WXK_END : WXK_NUMPAD_END;
788             break;
789 
790         case GDK_KEY_KP_Begin:
791             key_code = isChar ? WXK_HOME : WXK_NUMPAD_BEGIN;
792             break;
793 
794         case GDK_KEY_KP_Insert:
795             key_code = isChar ? WXK_INSERT : WXK_NUMPAD_INSERT;
796             break;
797 
798         case GDK_KEY_KP_Delete:
799             key_code = isChar ? WXK_DELETE : WXK_NUMPAD_DELETE;
800             break;
801 
802         case GDK_KEY_KP_Equal:
803             key_code = isChar ? '=' : int(WXK_NUMPAD_EQUAL);
804             break;
805 
806         case GDK_KEY_KP_Multiply:
807             key_code = isChar ? '*' : int(WXK_NUMPAD_MULTIPLY);
808             break;
809 
810         case GDK_KEY_KP_Add:
811             key_code = isChar ? '+' : int(WXK_NUMPAD_ADD);
812             break;
813 
814         case GDK_KEY_KP_Separator:
815             // FIXME: what is this?
816             key_code = isChar ? '.' : int(WXK_NUMPAD_SEPARATOR);
817             break;
818 
819         case GDK_KEY_KP_Subtract:
820             key_code = isChar ? '-' : int(WXK_NUMPAD_SUBTRACT);
821             break;
822 
823         case GDK_KEY_KP_Decimal:
824             key_code = isChar ? '.' : int(WXK_NUMPAD_DECIMAL);
825             break;
826 
827         case GDK_KEY_KP_Divide:
828             key_code = isChar ? '/' : int(WXK_NUMPAD_DIVIDE);
829             break;
830 
831 
832         // function keys
833         case GDK_KEY_F1:
834         case GDK_KEY_F2:
835         case GDK_KEY_F3:
836         case GDK_KEY_F4:
837         case GDK_KEY_F5:
838         case GDK_KEY_F6:
839         case GDK_KEY_F7:
840         case GDK_KEY_F8:
841         case GDK_KEY_F9:
842         case GDK_KEY_F10:
843         case GDK_KEY_F11:
844         case GDK_KEY_F12:
845             key_code = WXK_F1 + keysym - GDK_KEY_F1;
846             break;
847 #if GTK_CHECK_VERSION(2,18,0)
848         case GDK_KEY_Back:
849             key_code = WXK_BROWSER_BACK;
850             break;
851         case GDK_KEY_Forward:
852             key_code = WXK_BROWSER_FORWARD;
853             break;
854         case GDK_KEY_Refresh:
855             key_code = WXK_BROWSER_REFRESH;
856             break;
857         case GDK_KEY_Stop:
858             key_code = WXK_BROWSER_STOP;
859             break;
860         case GDK_KEY_Search:
861             key_code = WXK_BROWSER_SEARCH;
862             break;
863         case GDK_KEY_Favorites:
864             key_code = WXK_BROWSER_FAVORITES;
865             break;
866         case GDK_KEY_HomePage:
867             key_code = WXK_BROWSER_HOME;
868             break;
869         case GDK_KEY_AudioMute:
870             key_code = WXK_VOLUME_MUTE;
871             break;
872         case GDK_KEY_AudioLowerVolume:
873             key_code = WXK_VOLUME_DOWN;
874             break;
875         case GDK_KEY_AudioRaiseVolume:
876             key_code = WXK_VOLUME_UP;
877             break;
878         case GDK_KEY_AudioNext:
879             key_code = WXK_MEDIA_NEXT_TRACK;
880             break;
881         case GDK_KEY_AudioPrev:
882             key_code = WXK_MEDIA_PREV_TRACK;
883             break;
884         case GDK_KEY_AudioStop:
885             key_code = WXK_MEDIA_STOP;
886             break;
887         case GDK_KEY_AudioPlay:
888             key_code = WXK_MEDIA_PLAY_PAUSE;
889             break;
890         case GDK_KEY_Mail:
891             key_code = WXK_LAUNCH_MAIL;
892             break;
893         case GDK_KEY_LaunchA:
894             key_code = WXK_LAUNCH_APP1;
895             break;
896         case GDK_KEY_LaunchB:
897             key_code = WXK_LAUNCH_APP2;
898             break;
899 #endif // GTK_CHECK_VERSION(2,18,0)
900 
901         default:
902             key_code = 0;
903     }
904 
905     return key_code;
906 }
907 
wxIsAsciiKeysym(KeySym ks)908 static inline bool wxIsAsciiKeysym(KeySym ks)
909 {
910     return ks < 256;
911 }
912 
wxFillOtherKeyEventFields(wxKeyEvent & event,wxWindowGTK * win,GdkEventKey * gdk_event)913 static void wxFillOtherKeyEventFields(wxKeyEvent& event,
914                                       wxWindowGTK *win,
915                                       GdkEventKey *gdk_event)
916 {
917     event.SetTimestamp( gdk_event->time );
918     event.SetId(win->GetId());
919 
920     event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK) != 0;
921     event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK) != 0;
922     event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0;
923     event.m_metaDown = (gdk_event->state & GDK_META_MASK) != 0;
924 
925     // At least with current Linux systems, MOD5 corresponds to AltGr key and
926     // we represent it, for consistency with Windows, which really allows to
927     // use Ctrl+Alt as a replacement for AltGr if this key is not present, as a
928     // combination of these two modifiers.
929     if ( gdk_event->state & GDK_MOD5_MASK )
930     {
931         event.m_controlDown =
932         event.m_altDown = true;
933     }
934 
935     // Normally we take the state of modifiers directly from the low level GDK
936     // event but unfortunately GDK uses a different convention from MSW for the
937     // key events corresponding to the modifier keys themselves: in it, when
938     // e.g. Shift key is pressed, GDK_SHIFT_MASK is not set while it is set
939     // when Shift is released. Under MSW the situation is exactly reversed and
940     // the modifier corresponding to the key is set when it is pressed and
941     // unset when it is released. To ensure consistent behaviour between
942     // platforms (and because it seems to make slightly more sense, although
943     // arguably both behaviours are reasonable) we follow MSW here.
944     //
945     // Final notice: we set the flags to the desired value instead of just
946     // inverting them because they are not set correctly (i.e. in the same way
947     // as for the real events generated by the user) for wxUIActionSimulator-
948     // produced events and it seems better to keep that class code the same
949     // among all platforms and fix the discrepancy here instead of adding
950     // wxGTK-specific code to wxUIActionSimulator.
951     const bool isPress = gdk_event->type == GDK_KEY_PRESS;
952     switch ( gdk_event->keyval )
953     {
954         case GDK_KEY_Shift_L:
955         case GDK_KEY_Shift_R:
956             event.m_shiftDown = isPress;
957             break;
958 
959         case GDK_KEY_Control_L:
960         case GDK_KEY_Control_R:
961             event.m_controlDown = isPress;
962             break;
963 
964         case GDK_KEY_Alt_L:
965         case GDK_KEY_Alt_R:
966             event.m_altDown = isPress;
967             break;
968 
969         case GDK_KEY_Meta_L:
970         case GDK_KEY_Meta_R:
971         case GDK_KEY_Super_L:
972         case GDK_KEY_Super_R:
973             event.m_metaDown = isPress;
974             break;
975     }
976 
977     event.m_rawCode = (wxUint32) gdk_event->keyval;
978     event.m_rawFlags = gdk_event->hardware_keycode;
979 
980     event.SetEventObject( win );
981 }
982 
983 
984 static bool
wxTranslateGTKKeyEventToWx(wxKeyEvent & event,wxWindowGTK * win,GdkEventKey * gdk_event)985 wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
986                            wxWindowGTK *win,
987                            GdkEventKey *gdk_event)
988 {
989     // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string
990     //     but only event->keyval which is quite useless to us, so remember
991     //     the last character from GDK_KEY_PRESS and reuse it as last resort
992     //
993     // NB: should be MT-safe as we're always called from the main thread only
994     static struct
995     {
996         KeySym keysym;
997         long   keycode;
998     } s_lastKeyPress = { 0, 0 };
999 
1000     KeySym keysym = gdk_event->keyval;
1001 
1002     wxLogTrace(TRACE_KEYS, wxT("Key %s event: keysym = %lu"),
1003                event.GetEventType() == wxEVT_KEY_UP ? wxT("release")
1004                                                     : wxT("press"),
1005                static_cast<unsigned long>(keysym));
1006 
1007     long key_code = wxTranslateKeySymToWXKey(keysym, false /* !isChar */);
1008 
1009     if ( !key_code )
1010     {
1011         // do we have the translation or is it a plain ASCII character?
1012         if ( (gdk_event->length == 1) || wxIsAsciiKeysym(keysym) )
1013         {
1014             // we should use keysym if it is ASCII as X does some translations
1015             // like "I pressed while Control is down" => "Ctrl-I" == "TAB"
1016             // which we don't want here (but which we do use for OnChar())
1017             if ( !wxIsAsciiKeysym(keysym) )
1018             {
1019                 keysym = (KeySym)gdk_event->string[0];
1020             }
1021 
1022 #ifdef GDK_WINDOWING_X11
1023 #ifdef __WXGTK3__
1024             if (strcmp("GdkX11Window", g_type_name(G_TYPE_FROM_INSTANCE(gdk_event->window))) == 0)
1025 #else
1026             if (true)
1027 #endif
1028             {
1029                 // we want to always get the same key code when the same key is
1030                 // pressed regardless of the state of the modifiers, i.e. on a
1031                 // standard US keyboard pressing '5' or '%' ('5' key with
1032                 // Shift) should result in the same key code in OnKeyDown():
1033                 // '5' (although OnChar() will get either '5' or '%').
1034                 //
1035                 // to do it we first translate keysym to keycode (== scan code)
1036                 // and then back but always using the lower register
1037                 Display *dpy = (Display *)wxGetDisplay();
1038                 KeyCode keycode = XKeysymToKeycode(dpy, keysym);
1039 
1040                 wxLogTrace(TRACE_KEYS, wxT("\t-> keycode %d"), keycode);
1041 
1042 #ifdef HAVE_X11_XKBLIB_H
1043                 KeySym keysymNormalized = XkbKeycodeToKeysym(dpy, keycode, 0, 0);
1044 #else
1045                 KeySym keysymNormalized = XKeycodeToKeysym(dpy, keycode, 0);
1046 #endif
1047 
1048                 // use the normalized, i.e. lower register, keysym if we've
1049                 // got one
1050                 key_code = keysymNormalized ? keysymNormalized : keysym;
1051             }
1052             else
1053 #endif // GDK_WINDOWING_X11
1054             {
1055                 key_code = keysym;
1056             }
1057 
1058             // as explained above, we want to have lower register key codes
1059             // normally but for the letter keys we want to have the upper ones
1060             //
1061             // NB: don't use XConvertCase() here, we want to do it for letters
1062             // only
1063             key_code = toupper(key_code);
1064         }
1065         else // non ASCII key, what to do?
1066         {
1067             // by default, ignore it
1068             key_code = 0;
1069 
1070             // but if we have cached information from the last KEY_PRESS
1071             if ( gdk_event->type == GDK_KEY_RELEASE )
1072             {
1073                 // then reuse it
1074                 if ( keysym == s_lastKeyPress.keysym )
1075                 {
1076                     key_code = s_lastKeyPress.keycode;
1077                 }
1078             }
1079         }
1080 
1081         if ( gdk_event->type == GDK_KEY_PRESS )
1082         {
1083             // remember it to be reused for KEY_UP event later
1084             s_lastKeyPress.keysym = keysym;
1085             s_lastKeyPress.keycode = key_code;
1086         }
1087     }
1088 
1089     wxLogTrace(TRACE_KEYS, wxT("\t-> wxKeyCode %ld"), key_code);
1090 
1091     event.m_keyCode = key_code;
1092 
1093 #if wxUSE_UNICODE
1094     event.m_uniChar = gdk_keyval_to_unicode(key_code ? key_code : gdk_event->keyval);
1095     if ( !event.m_uniChar && event.m_keyCode <= WXK_DELETE )
1096     {
1097         // Set Unicode key code to the ASCII equivalent for compatibility. E.g.
1098         // let RETURN generate the key event with both key and Unicode key
1099         // codes of 13.
1100         event.m_uniChar = event.m_keyCode;
1101     }
1102 
1103     // sending unknown key events doesn't really make sense
1104     if ( !key_code && !event.m_uniChar )
1105         return false;
1106 #else
1107     if (!key_code)
1108         return false;
1109 #endif // wxUSE_UNICODE
1110 
1111     // now fill all the other fields
1112     wxFillOtherKeyEventFields(event, win, gdk_event);
1113 
1114     return true;
1115 }
1116 
1117 
1118 namespace
1119 {
1120 
1121 // Send wxEVT_CHAR_HOOK event to the parent of the window and return true only
1122 // if it was processed (and not skipped).
SendCharHookEvent(const wxKeyEvent & event,wxWindow * win)1123 bool SendCharHookEvent(const wxKeyEvent& event, wxWindow *win)
1124 {
1125     // wxEVT_CHAR_HOOK must be sent to allow the parent windows (e.g. a dialog
1126     // which typically closes when Esc key is pressed in any of its controls)
1127     // to handle key events in all of its children unless the mouse is captured
1128     // in which case we consider that the keyboard should be "captured" too.
1129     if ( !g_captureWindow )
1130     {
1131         wxKeyEvent eventCharHook(wxEVT_CHAR_HOOK, event);
1132         if ( win->HandleWindowEvent(eventCharHook)
1133                 && !event.IsNextEventAllowed() )
1134             return true;
1135     }
1136 
1137     return false;
1138 }
1139 
1140 // Adjust wxEVT_CHAR event key code fields. This function takes care of two
1141 // conventions:
1142 // (a) Ctrl-letter key presses generate key codes in range 1..26
1143 // (b) Unicode key codes are same as key codes for the codes in 1..255 range
AdjustCharEventKeyCodes(wxKeyEvent & event)1144 void AdjustCharEventKeyCodes(wxKeyEvent& event)
1145 {
1146     const int code = event.m_keyCode;
1147 
1148     // Check for (a) above.
1149     if ( event.ControlDown() )
1150     {
1151         // We intentionally don't use isupper/lower() here, we really need
1152         // ASCII letters only as it doesn't make sense to translate any other
1153         // ones into this range which has only 26 slots.
1154         if ( code >= 'a' && code <= 'z' )
1155             event.m_keyCode = code - 'a' + 1;
1156         else if ( code >= 'A' && code <= 'Z' )
1157             event.m_keyCode = code - 'A' + 1;
1158 
1159 #if wxUSE_UNICODE
1160         // Adjust the Unicode equivalent in the same way too.
1161         if ( event.m_keyCode != code )
1162             event.m_uniChar = event.m_keyCode;
1163 #endif // wxUSE_UNICODE
1164     }
1165 
1166 #if wxUSE_UNICODE
1167     // Check for (b) from above.
1168     //
1169     // FIXME: Should we do it for key codes up to 255?
1170     if ( !event.m_uniChar && code < WXK_DELETE )
1171         event.m_uniChar = code;
1172 #endif // wxUSE_UNICODE
1173 }
1174 
1175 } // anonymous namespace
1176 
1177 // If a widget does not handle a key or mouse event, GTK+ sends it up the
1178 // parent chain until it is handled. These events are not supposed to propagate
1179 // in wxWidgets, so this code avoids handling them in any parent wxWindow,
1180 // while still allowing the event to propagate so things like native keyboard
1181 // navigation will work.
1182 #define wxPROCESS_EVENT_ONCE(EventType, event) \
1183     static EventType eventPrev; \
1184     if (!gs_isNewEvent && memcmp(&eventPrev, event, sizeof(EventType)) == 0) \
1185         return false; \
1186     gs_isNewEvent = false; \
1187     eventPrev = *event
1188 
1189 static bool gs_isNewEvent;
1190 
1191 extern "C" {
1192 static gboolean
gtk_window_key_press_callback(GtkWidget * WXUNUSED (widget),GdkEventKey * gdk_event,wxWindow * win)1193 gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget),
1194                                GdkEventKey *gdk_event,
1195                                wxWindow *win )
1196 {
1197     if (g_blockEventsOnDrag)
1198         return FALSE;
1199 
1200     wxPROCESS_EVENT_ONCE(GdkEventKey, gdk_event);
1201 
1202     wxKeyEvent event( wxEVT_KEY_DOWN );
1203     bool ret = false;
1204     bool return_after_IM = false;
1205 
1206     if( wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
1207     {
1208         // Send the CHAR_HOOK event first
1209         if ( SendCharHookEvent(event, win) )
1210         {
1211             // Don't do anything at all with this event any more.
1212             return TRUE;
1213         }
1214 
1215         // Next check for accelerators.
1216 #if wxUSE_ACCEL
1217         wxWindowGTK *ancestor = win;
1218         while (ancestor)
1219         {
1220             int command = ancestor->GetAcceleratorTable()->GetCommand( event );
1221             if (command != -1)
1222             {
1223                 wxCommandEvent menu_event( wxEVT_MENU, command );
1224                 ret = ancestor->HandleWindowEvent( menu_event );
1225 
1226                 if ( !ret )
1227                 {
1228                     // if the accelerator wasn't handled as menu event, try
1229                     // it as button click (for compatibility with other
1230                     // platforms):
1231                     wxCommandEvent button_event( wxEVT_BUTTON, command );
1232                     ret = ancestor->HandleWindowEvent( button_event );
1233                 }
1234 
1235                 break;
1236             }
1237             if (ancestor->IsTopNavigationDomain(wxWindow::Navigation_Accel))
1238                 break;
1239             ancestor = ancestor->GetParent();
1240         }
1241 #endif // wxUSE_ACCEL
1242 
1243         // If not an accelerator, then emit KEY_DOWN event
1244         if ( !ret )
1245             ret = win->HandleWindowEvent( event );
1246     }
1247     else
1248     {
1249         // Return after IM processing as we cannot do
1250         // anything with it anyhow.
1251         return_after_IM = true;
1252     }
1253 
1254     if ( !ret )
1255     {
1256         // Indicate that IM handling is in process by setting this pointer
1257         // (which will remain valid for all the code called during IM key
1258         // handling).
1259         win->m_imKeyEvent = gdk_event;
1260 
1261         // We should let GTK+ IM filter key event first. According to GTK+ 2.0 API
1262         // docs, if IM filter returns true, no further processing should be done.
1263         // we should send the key_down event anyway.
1264         const int intercepted_by_IM = win->GTKIMFilterKeypress(gdk_event);
1265 
1266         win->m_imKeyEvent = NULL;
1267 
1268         if ( intercepted_by_IM )
1269         {
1270             wxLogTrace(TRACE_KEYS, wxT("Key event intercepted by IM"));
1271             return TRUE;
1272         }
1273     }
1274 
1275     if (return_after_IM)
1276         return FALSE;
1277 
1278     // Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
1279     // will only be sent if it is not in an accelerator table.
1280     if (!ret)
1281     {
1282         KeySym keysym = gdk_event->keyval;
1283         // Find key code for EVT_CHAR and EVT_CHAR_HOOK events
1284         long key_code = wxTranslateKeySymToWXKey(keysym, true /* isChar */);
1285         if ( !key_code )
1286         {
1287             if ( wxIsAsciiKeysym(keysym) )
1288             {
1289                 // ASCII key
1290                 key_code = (unsigned char)keysym;
1291             }
1292             // gdk_event->string is actually deprecated
1293             else if ( gdk_event->length == 1 )
1294             {
1295                 key_code = (unsigned char)gdk_event->string[0];
1296             }
1297         }
1298 
1299         if ( key_code )
1300         {
1301             wxKeyEvent eventChar(wxEVT_CHAR, event);
1302 
1303             wxLogTrace(TRACE_KEYS, wxT("Char event: %ld"), key_code);
1304 
1305             eventChar.m_keyCode = key_code;
1306 #if wxUSE_UNICODE
1307             eventChar.m_uniChar = gdk_keyval_to_unicode(key_code);
1308 #endif // wxUSE_UNICODE
1309 
1310             AdjustCharEventKeyCodes(eventChar);
1311 
1312             ret = win->HandleWindowEvent(eventChar);
1313         }
1314     }
1315 
1316     return ret;
1317 }
1318 }
1319 
GTKIMFilterKeypress(GdkEventKey * event) const1320 int wxWindowGTK::GTKIMFilterKeypress(GdkEventKey* event) const
1321 {
1322     return m_imContext ? gtk_im_context_filter_keypress(m_imContext, event)
1323                        : FALSE;
1324 }
1325 
1326 extern "C" {
1327 static void
gtk_wxwindow_commit_cb(GtkIMContext * WXUNUSED (context),const gchar * str,wxWindow * window)1328 gtk_wxwindow_commit_cb (GtkIMContext * WXUNUSED(context),
1329                         const gchar  *str,
1330                         wxWindow     *window)
1331 {
1332     // Ignore the return value here, it doesn't matter for the "commit" signal.
1333     window->GTKDoInsertTextFromIM(str);
1334 }
1335 }
1336 
GTKDoInsertTextFromIM(const char * str)1337 bool wxWindowGTK::GTKDoInsertTextFromIM(const char* str)
1338 {
1339     wxKeyEvent event( wxEVT_CHAR );
1340 
1341     // take modifiers, cursor position, timestamp etc. from the last
1342     // key_press_event that was fed into Input Method:
1343     if ( m_imKeyEvent )
1344     {
1345         wxFillOtherKeyEventFields(event, this, m_imKeyEvent);
1346     }
1347     else
1348     {
1349         event.SetEventObject(this);
1350     }
1351 
1352     const wxString data(wxGTK_CONV_BACK_SYS(str));
1353     if( data.empty() )
1354         return false;
1355 
1356     bool processed = false;
1357     for( wxString::const_iterator pstr = data.begin(); pstr != data.end(); ++pstr )
1358     {
1359 #if wxUSE_UNICODE
1360         event.m_uniChar = *pstr;
1361         // Backward compatible for ISO-8859-1
1362         event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0;
1363         wxLogTrace(TRACE_KEYS, wxT("IM sent character '%c'"), event.m_uniChar);
1364 #else
1365         event.m_keyCode = (char)*pstr;
1366 #endif  // wxUSE_UNICODE
1367 
1368         AdjustCharEventKeyCodes(event);
1369 
1370         if ( HandleWindowEvent(event) )
1371             processed = true;
1372     }
1373 
1374     return processed;
1375 }
1376 
GTKOnInsertText(const char * text)1377 bool wxWindowGTK::GTKOnInsertText(const char* text)
1378 {
1379     if ( !m_imKeyEvent )
1380     {
1381         // We're not inside IM key handling at all.
1382         return false;
1383     }
1384 
1385     return GTKDoInsertTextFromIM(text);
1386 }
1387 
1388 
1389 //-----------------------------------------------------------------------------
1390 // "key_release_event" from any window
1391 //-----------------------------------------------------------------------------
1392 
1393 extern "C" {
1394 static gboolean
gtk_window_key_release_callback(GtkWidget * WXUNUSED (widget),GdkEventKey * gdk_event,wxWindowGTK * win)1395 gtk_window_key_release_callback( GtkWidget * WXUNUSED(widget),
1396                                  GdkEventKey *gdk_event,
1397                                  wxWindowGTK *win )
1398 {
1399     if (g_blockEventsOnDrag)
1400         return FALSE;
1401 
1402     wxPROCESS_EVENT_ONCE(GdkEventKey, gdk_event);
1403 
1404     wxKeyEvent event( wxEVT_KEY_UP );
1405     if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
1406     {
1407         // unknown key pressed, ignore (the event would be useless anyhow)
1408         return FALSE;
1409     }
1410 
1411     return win->GTKProcessEvent(event);
1412 }
1413 }
1414 
1415 // ============================================================================
1416 // the mouse events
1417 // ============================================================================
1418 
1419 // ----------------------------------------------------------------------------
1420 // mouse event processing helpers
1421 // ----------------------------------------------------------------------------
1422 
AdjustEventButtonState(wxMouseEvent & event)1423 static void AdjustEventButtonState(wxMouseEvent& event)
1424 {
1425     // GDK reports the old state of the button for a button press event, but
1426     // for compatibility with MSW and common sense we want m_leftDown be TRUE
1427     // for a LEFT_DOWN event, not FALSE, so we will invert
1428     // left/right/middleDown for the corresponding click events
1429 
1430     if ((event.GetEventType() == wxEVT_LEFT_DOWN) ||
1431         (event.GetEventType() == wxEVT_LEFT_DCLICK) ||
1432         (event.GetEventType() == wxEVT_LEFT_UP))
1433     {
1434         event.m_leftDown = !event.m_leftDown;
1435         return;
1436     }
1437 
1438     if ((event.GetEventType() == wxEVT_MIDDLE_DOWN) ||
1439         (event.GetEventType() == wxEVT_MIDDLE_DCLICK) ||
1440         (event.GetEventType() == wxEVT_MIDDLE_UP))
1441     {
1442         event.m_middleDown = !event.m_middleDown;
1443         return;
1444     }
1445 
1446     if ((event.GetEventType() == wxEVT_RIGHT_DOWN) ||
1447         (event.GetEventType() == wxEVT_RIGHT_DCLICK) ||
1448         (event.GetEventType() == wxEVT_RIGHT_UP))
1449     {
1450         event.m_rightDown = !event.m_rightDown;
1451         return;
1452     }
1453 
1454     if ((event.GetEventType() == wxEVT_AUX1_DOWN) ||
1455         (event.GetEventType() == wxEVT_AUX1_DCLICK))
1456     {
1457         event.m_aux1Down = true;
1458         return;
1459     }
1460 
1461     if ((event.GetEventType() == wxEVT_AUX2_DOWN) ||
1462         (event.GetEventType() == wxEVT_AUX2_DCLICK))
1463     {
1464         event.m_aux2Down = true;
1465         return;
1466     }
1467 }
1468 
1469 // find the window to send the mouse event to
1470 static
FindWindowForMouseEvent(wxWindowGTK * win,wxCoord & x,wxCoord & y)1471 wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
1472 {
1473     wxCoord xx = x;
1474     wxCoord yy = y;
1475 
1476     if (win->m_wxwindow)
1477     {
1478         wxPizza* pizza = WX_PIZZA(win->m_wxwindow);
1479         xx += pizza->m_scroll_x;
1480         yy += pizza->m_scroll_y;
1481     }
1482 
1483     wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
1484     while (node)
1485     {
1486         wxWindow* child = static_cast<wxWindow*>(node->GetData());
1487 
1488         node = node->GetNext();
1489         if (!child->IsShown())
1490             continue;
1491 
1492         if (child->GTKIsTransparentForMouse())
1493         {
1494             // wxStaticBox is transparent in the box itself
1495             int xx1 = child->m_x;
1496             int yy1 = child->m_y;
1497             int xx2 = child->m_x + child->m_width;
1498             int yy2 = child->m_y + child->m_height;
1499 
1500             // left
1501             if (((xx >= xx1) && (xx <= xx1+10) && (yy >= yy1) && (yy <= yy2)) ||
1502             // right
1503                 ((xx >= xx2-10) && (xx <= xx2) && (yy >= yy1) && (yy <= yy2)) ||
1504             // top
1505                 ((xx >= xx1) && (xx <= xx2) && (yy >= yy1) && (yy <= yy1+10)) ||
1506             // bottom
1507                 ((xx >= xx1) && (xx <= xx2) && (yy >= yy2-1) && (yy <= yy2)))
1508             {
1509                 win = child;
1510                 x -= child->m_x;
1511                 y -= child->m_y;
1512                 break;
1513             }
1514 
1515         }
1516         else
1517         {
1518             if ((child->m_wxwindow == NULL) &&
1519                 win->IsClientAreaChild(child) &&
1520                 (child->m_x <= xx) &&
1521                 (child->m_y <= yy) &&
1522                 (child->m_x+child->m_width  >= xx) &&
1523                 (child->m_y+child->m_height >= yy))
1524             {
1525                 win = child;
1526                 x -= child->m_x;
1527                 y -= child->m_y;
1528                 break;
1529             }
1530         }
1531     }
1532 
1533     return win;
1534 }
1535 
1536 // ----------------------------------------------------------------------------
1537 // common event handlers helpers
1538 // ----------------------------------------------------------------------------
1539 
GTKProcessEvent(wxEvent & event) const1540 bool wxWindowGTK::GTKProcessEvent(wxEvent& event) const
1541 {
1542     // nothing special at this level
1543     return HandleWindowEvent(event);
1544 }
1545 
GTKShouldIgnoreEvent() const1546 bool wxWindowGTK::GTKShouldIgnoreEvent() const
1547 {
1548     return g_blockEventsOnDrag;
1549 }
1550 
GTKCallbackCommonPrologue(GdkEventAny * event) const1551 int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny *event) const
1552 {
1553     if (g_blockEventsOnDrag)
1554         return TRUE;
1555     if (g_blockEventsOnScroll)
1556         return TRUE;
1557 
1558     if (!GTKIsOwnWindow(event->window))
1559         return FALSE;
1560 
1561     return -1;
1562 }
1563 
1564 // overloads for all GDK event types we use here: we need to have this as
1565 // GdkEventXXX can't be implicitly cast to GdkEventAny even if it, in fact,
1566 // derives from it in the sense that the structs have the same layout
1567 #define wxDEFINE_COMMON_PROLOGUE_OVERLOAD(T)                                  \
1568     static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win)        \
1569     {                                                                         \
1570         return win->GTKCallbackCommonPrologue((GdkEventAny *)event);          \
1571     }
1572 
1573 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventButton)
wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion)1574 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion)
1575 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing)
1576 
1577 #undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD
1578 
1579 #define wxCOMMON_CALLBACK_PROLOGUE(event, win)                                \
1580     const int rc = wxGtkCallbackCommonPrologue(event, win);                   \
1581     if ( rc != -1 )                                                           \
1582         return rc
1583 
1584 // all event handlers must have C linkage as they're called from GTK+ C code
1585 extern "C"
1586 {
1587 
1588 //-----------------------------------------------------------------------------
1589 // "button_press_event"
1590 //-----------------------------------------------------------------------------
1591 
1592 static gboolean
1593 gtk_window_button_press_callback( GtkWidget* WXUNUSED_IN_GTK3(widget),
1594                                   GdkEventButton *gdk_event,
1595                                   wxWindowGTK *win )
1596 {
1597     wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
1598 
1599     wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1600 
1601     g_lastButtonNumber = gdk_event->button;
1602 
1603     wxEventType event_type;
1604     wxEventType down;
1605     wxEventType dclick;
1606     switch (gdk_event->button)
1607     {
1608         case 1:
1609             down = wxEVT_LEFT_DOWN;
1610             dclick = wxEVT_LEFT_DCLICK;
1611             break;
1612         case 2:
1613             down = wxEVT_MIDDLE_DOWN;
1614             dclick = wxEVT_MIDDLE_DCLICK;
1615             break;
1616         case 3:
1617             down = wxEVT_RIGHT_DOWN;
1618             dclick = wxEVT_RIGHT_DCLICK;
1619             break;
1620         case 8:
1621             down = wxEVT_AUX1_DOWN;
1622             dclick = wxEVT_AUX1_DCLICK;
1623             break;
1624         case 9:
1625             down = wxEVT_AUX2_DOWN;
1626             dclick = wxEVT_AUX2_DCLICK;
1627             break;
1628         default:
1629             return false;
1630     }
1631     switch (gdk_event->type)
1632     {
1633         case GDK_BUTTON_PRESS:
1634             event_type = down;
1635             // GDK sends surplus button down events
1636             // before a double click event. We
1637             // need to filter these out.
1638             if (win->m_wxwindow)
1639             {
1640                 GdkEvent* peek_event = gdk_event_peek();
1641                 if (peek_event)
1642                 {
1643                     const GdkEventType peek_event_type = peek_event->type;
1644                     gdk_event_free(peek_event);
1645                     if (peek_event_type == GDK_2BUTTON_PRESS ||
1646                         peek_event_type == GDK_3BUTTON_PRESS)
1647                     {
1648                         return true;
1649                     }
1650                 }
1651             }
1652             break;
1653         case GDK_2BUTTON_PRESS:
1654             event_type = dclick;
1655 #ifndef __WXGTK3__
1656             if (gdk_event->button >= 1 && gdk_event->button <= 3)
1657             {
1658                 // Reset GDK internal timestamp variables in order to disable GDK
1659                 // triple click events. GDK will then next time believe no button has
1660                 // been clicked just before, and send a normal button click event.
1661                 GdkDisplay* display = gtk_widget_get_display(widget);
1662                 display->button_click_time[1] = 0;
1663                 display->button_click_time[0] = 0;
1664             }
1665 #endif // !__WXGTK3__
1666             break;
1667         // we shouldn't get triple clicks at all for GTK2 because we
1668         // suppress them artificially using the code above but we still
1669         // should map them to something for GTK3 and not just ignore them
1670         // as this would lose clicks
1671         case GDK_3BUTTON_PRESS:
1672             event_type = down;
1673             break;
1674         default:
1675             return false;
1676     }
1677 
1678     g_lastMouseEvent = (GdkEvent*) gdk_event;
1679 
1680     wxMouseEvent event( event_type );
1681     InitMouseEvent( win, event, gdk_event );
1682 
1683     AdjustEventButtonState(event);
1684 
1685     // find the correct window to send the event to: it may be a different one
1686     // from the one which got it at GTK+ level because some controls don't have
1687     // their own X window and thus cannot get any events.
1688     if ( !g_captureWindow )
1689         win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1690 
1691     // reset the event object and id in case win changed.
1692     event.SetEventObject( win );
1693     event.SetId( win->GetId() );
1694 
1695     bool ret = win->GTKProcessEvent( event );
1696     g_lastMouseEvent = NULL;
1697     if ( ret )
1698         return TRUE;
1699 
1700     if ((event_type == wxEVT_LEFT_DOWN) && !win->IsOfStandardClass() &&
1701         (gs_currentFocus != win) && win->IsFocusable())
1702     {
1703         win->SetFocus();
1704     }
1705 
1706     if (event_type == wxEVT_RIGHT_DOWN)
1707     {
1708         // generate a "context menu" event: this is similar to right mouse
1709         // click under many GUIs except that it is generated differently
1710         // (right up under MSW, ctrl-click under Mac, right down here) and
1711         //
1712         // (a) it's a command event and so is propagated to the parent
1713         // (b) under some ports it can be generated from kbd too
1714         // (c) it uses screen coords (because of (a))
1715         const wxPoint pos = win->ClientToScreen(event.GetPosition());
1716         return win->WXSendContextMenuEvent(pos);
1717     }
1718 
1719     return FALSE;
1720 }
1721 
1722 //-----------------------------------------------------------------------------
1723 // "button_release_event"
1724 //-----------------------------------------------------------------------------
1725 
1726 static gboolean
1727 gtk_window_button_release_callback( GtkWidget *WXUNUSED(widget),
1728                                     GdkEventButton *gdk_event,
1729                                     wxWindowGTK *win )
1730 {
1731     wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
1732 
1733     wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1734 
1735     g_lastButtonNumber = 0;
1736 
1737     wxEventType event_type = wxEVT_NULL;
1738 
1739     switch (gdk_event->button)
1740     {
1741         case 1:
1742             event_type = wxEVT_LEFT_UP;
1743             break;
1744 
1745         case 2:
1746             event_type = wxEVT_MIDDLE_UP;
1747             break;
1748 
1749         case 3:
1750             event_type = wxEVT_RIGHT_UP;
1751             break;
1752 
1753         case 8:
1754             event_type = wxEVT_AUX1_UP;
1755             break;
1756 
1757         case 9:
1758             event_type = wxEVT_AUX2_UP;
1759             break;
1760 
1761         default:
1762             // unknown button, don't process
1763             return FALSE;
1764     }
1765 
1766     g_lastMouseEvent = (GdkEvent*) gdk_event;
1767 
1768     wxMouseEvent event( event_type );
1769     InitMouseEvent( win, event, gdk_event );
1770 
1771     AdjustEventButtonState(event);
1772 
1773     if ( !g_captureWindow )
1774         win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1775 
1776     // reset the event object and id in case win changed.
1777     event.SetEventObject( win );
1778     event.SetId( win->GetId() );
1779 
1780     // We ignore the result of the event processing here as we don't really
1781     // want to prevent the other handlers from running even if we did process
1782     // this event ourselves, there is no real advantage in doing this and it
1783     // could actually be harmful, see #16055.
1784     (void)win->GTKProcessEvent(event);
1785 
1786     g_lastMouseEvent = NULL;
1787 
1788     return FALSE;
1789 }
1790 
1791 //-----------------------------------------------------------------------------
1792 
1793 static void SendSetCursorEvent(wxWindowGTK* win, int x, int y)
1794 {
1795     wxPoint posClient(x, y);
1796     const wxPoint posScreen = win->ClientToScreen(posClient);
1797 
1798     wxWindowGTK* w = win;
1799     for ( ;; )
1800     {
1801         wxSetCursorEvent event(posClient.x, posClient.y);
1802         event.SetId(win->GetId());
1803         event.SetEventObject(win);
1804 
1805         if (w->GTKProcessEvent(event))
1806         {
1807             win->GTKUpdateCursor(false, false, &event.GetCursor());
1808             win->m_needCursorReset = true;
1809             return;
1810         }
1811         // this is how wxMSW works...
1812         if (w->GetCursor().IsOk())
1813             break;
1814 
1815         w = w->GetParent();
1816         if (w == NULL || w->m_widget == NULL || !gtk_widget_get_visible(w->m_widget))
1817             break;
1818         posClient = w->ScreenToClient(posScreen);
1819     }
1820     if (win->m_needCursorReset)
1821         win->GTKUpdateCursor();
1822 }
1823 
1824 //-----------------------------------------------------------------------------
1825 // "motion_notify_event"
1826 //-----------------------------------------------------------------------------
1827 
1828 static gboolean
1829 gtk_window_motion_notify_callback( GtkWidget * WXUNUSED(widget),
1830                                    GdkEventMotion *gdk_event,
1831                                    wxWindowGTK *win )
1832 {
1833     wxPROCESS_EVENT_ONCE(GdkEventMotion, gdk_event);
1834 
1835     wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1836 
1837     g_lastMouseEvent = (GdkEvent*) gdk_event;
1838 
1839     wxMouseEvent event( wxEVT_MOTION );
1840     InitMouseEvent(win, event, gdk_event);
1841 
1842     if ( g_captureWindow )
1843     {
1844         // synthesise a mouse enter or leave event if needed
1845         GdkWindow* winUnderMouse =
1846 #ifdef __WXGTK3__
1847             gdk_device_get_window_at_position(gdk_event->device, NULL, NULL);
1848 #else
1849             gdk_window_at_pointer(NULL, NULL);
1850 #endif
1851         // This seems to be necessary and actually been added to
1852         // GDK itself in version 2.0.X
1853         gdk_flush();
1854 
1855         bool hasMouse = winUnderMouse == gdk_event->window;
1856         if ( hasMouse != g_captureWindowHasMouse )
1857         {
1858             // the mouse changed window
1859             g_captureWindowHasMouse = hasMouse;
1860 
1861             wxMouseEvent eventM(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW
1862                                                         : wxEVT_LEAVE_WINDOW);
1863             InitMouseEvent(win, eventM, gdk_event);
1864             eventM.SetEventObject(win);
1865             win->GTKProcessEvent(eventM);
1866         }
1867     }
1868     else // no capture
1869     {
1870         win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1871 
1872         // reset the event object and id in case win changed.
1873         event.SetEventObject( win );
1874         event.SetId( win->GetId() );
1875     }
1876 
1877     if ( !g_captureWindow )
1878         SendSetCursorEvent(win, event.m_x, event.m_y);
1879 
1880     bool ret = win->GTKProcessEvent(event);
1881 
1882     g_lastMouseEvent = NULL;
1883 
1884     // Request additional motion events. Done at the end to increase the
1885     // chances that lower priority events requested by the handler above, such
1886     // as painting, can be processed before the next motion event occurs.
1887     // Otherwise a long-running handler can cause paint events to be entirely
1888     // blocked while the mouse is moving.
1889     if (gdk_event->is_hint)
1890     {
1891 #ifdef __WXGTK3__
1892         gdk_event_request_motions(gdk_event);
1893 #else
1894         gdk_window_get_pointer(gdk_event->window, NULL, NULL, NULL);
1895 #endif
1896     }
1897 
1898     return ret;
1899 }
1900 
1901 //-----------------------------------------------------------------------------
1902 // "scroll_event" (mouse wheel event)
1903 //-----------------------------------------------------------------------------
1904 
1905 static void AdjustRangeValue(GtkRange* range, double step)
1906 {
1907     if (gtk_widget_get_visible(GTK_WIDGET(range)))
1908     {
1909         GtkAdjustment* adj = gtk_range_get_adjustment(range);
1910         double value = gtk_adjustment_get_value(adj);
1911         value += step * gtk_adjustment_get_step_increment(adj);
1912         gtk_range_set_value(range, value);
1913     }
1914 }
1915 
1916 static gboolean
1917 scroll_event(GtkWidget* widget, GdkEventScroll* gdk_event, wxWindow* win)
1918 {
1919     wxMouseEvent event(wxEVT_MOUSEWHEEL);
1920     InitMouseEvent(win, event, gdk_event);
1921 
1922     event.m_wheelDelta = 120;
1923     event.m_linesPerAction = 3;
1924     event.m_columnsPerAction = 3;
1925 
1926     GtkRange* range_h = win->m_scrollBar[wxWindow::ScrollDir_Horz];
1927     GtkRange* range_v = win->m_scrollBar[wxWindow::ScrollDir_Vert];
1928     const bool is_range_h = (void*)widget == range_h;
1929     const bool is_range_v = (void*)widget == range_v;
1930     GdkScrollDirection direction = gdk_event->direction;
1931     switch (direction)
1932     {
1933         case GDK_SCROLL_UP:
1934             if (is_range_h)
1935                 direction = GDK_SCROLL_LEFT;
1936             break;
1937         case GDK_SCROLL_DOWN:
1938             if (is_range_h)
1939                 direction = GDK_SCROLL_RIGHT;
1940             break;
1941         case GDK_SCROLL_LEFT:
1942             if (is_range_v)
1943                 direction = GDK_SCROLL_UP;
1944             break;
1945         case GDK_SCROLL_RIGHT:
1946             if (is_range_v)
1947                 direction = GDK_SCROLL_DOWN;
1948             break;
1949         default:
1950             break;
1951 #if GTK_CHECK_VERSION(3,4,0)
1952         case GDK_SCROLL_SMOOTH:
1953             double delta_x = gdk_event->delta_x;
1954             double delta_y = gdk_event->delta_y;
1955             if (delta_x == 0)
1956             {
1957                 if (is_range_h)
1958                 {
1959                     delta_x = delta_y;
1960                     delta_y = 0;
1961                 }
1962             }
1963             else if (delta_y == 0)
1964             {
1965                 if (is_range_v)
1966                 {
1967                     delta_y = delta_x;
1968                     delta_x = 0;
1969                 }
1970             }
1971             bool handled = false;
1972             if (delta_x)
1973             {
1974                 event.m_wheelAxis = wxMOUSE_WHEEL_HORIZONTAL;
1975                 event.m_wheelRotation = int(event.m_wheelDelta * delta_x);
1976                 handled = win->GTKProcessEvent(event);
1977                 if (!handled && range_h)
1978                 {
1979                     AdjustRangeValue(range_h, event.m_columnsPerAction * delta_x);
1980                     handled = true;
1981                 }
1982             }
1983             if (delta_y)
1984             {
1985                 event.m_wheelAxis = wxMOUSE_WHEEL_VERTICAL;
1986                 event.m_wheelRotation = int(event.m_wheelDelta * -delta_y);
1987                 handled = win->GTKProcessEvent(event);
1988                 if (!handled && range_v)
1989                 {
1990                     AdjustRangeValue(range_v, event.m_linesPerAction * delta_y);
1991                     handled = true;
1992                 }
1993             }
1994             return handled;
1995 #endif // GTK_CHECK_VERSION(3,4,0)
1996     }
1997     GtkRange *range;
1998     double step;
1999     switch (direction)
2000     {
2001         case GDK_SCROLL_UP:
2002         case GDK_SCROLL_DOWN:
2003             range = range_v;
2004             event.m_wheelAxis = wxMOUSE_WHEEL_VERTICAL;
2005             step = event.m_linesPerAction;
2006             break;
2007         case GDK_SCROLL_LEFT:
2008         case GDK_SCROLL_RIGHT:
2009             range = range_h;
2010             event.m_wheelAxis = wxMOUSE_WHEEL_HORIZONTAL;
2011             step = event.m_columnsPerAction;
2012             break;
2013         default:
2014             return false;
2015     }
2016 
2017     event.m_wheelRotation = event.m_wheelDelta;
2018     if (direction == GDK_SCROLL_DOWN || direction == GDK_SCROLL_LEFT)
2019         event.m_wheelRotation = -event.m_wheelRotation;
2020 
2021     if (!win->GTKProcessEvent(event))
2022     {
2023         if (!range)
2024             return false;
2025 
2026         if (direction == GDK_SCROLL_UP || direction == GDK_SCROLL_LEFT)
2027             step = -step;
2028         AdjustRangeValue(range, step);
2029     }
2030 
2031     return true;
2032 }
2033 
2034 //-----------------------------------------------------------------------------
2035 // "popup-menu"
2036 //-----------------------------------------------------------------------------
2037 
2038 static gboolean wxgtk_window_popup_menu_callback(GtkWidget*, wxWindowGTK* win)
2039 {
2040     wxContextMenuEvent event(wxEVT_CONTEXT_MENU, win->GetId(), wxPoint(-1, -1));
2041     event.SetEventObject(win);
2042     return win->GTKProcessEvent(event);
2043 }
2044 
2045 //-----------------------------------------------------------------------------
2046 // "focus_in_event"
2047 //-----------------------------------------------------------------------------
2048 
2049 static gboolean
2050 gtk_window_focus_in_callback( GtkWidget * WXUNUSED(widget),
2051                               GdkEventFocus *WXUNUSED(event),
2052                               wxWindowGTK *win )
2053 {
2054     return win->GTKHandleFocusIn();
2055 }
2056 
2057 //-----------------------------------------------------------------------------
2058 // "focus_out_event"
2059 //-----------------------------------------------------------------------------
2060 
2061 static gboolean
2062 gtk_window_focus_out_callback( GtkWidget * WXUNUSED(widget),
2063                                GdkEventFocus * WXUNUSED(gdk_event),
2064                                wxWindowGTK *win )
2065 {
2066     return win->GTKHandleFocusOut();
2067 }
2068 
2069 //-----------------------------------------------------------------------------
2070 // "focus"
2071 //-----------------------------------------------------------------------------
2072 
2073 static gboolean
2074 wx_window_focus_callback(GtkWidget *widget,
2075                          GtkDirectionType WXUNUSED(direction),
2076                          wxWindowGTK *win)
2077 {
2078     // the default handler for focus signal in GtkScrolledWindow sets
2079     // focus to the window itself even if it doesn't accept focus, i.e. has no
2080     // GTK_CAN_FOCUS in its style -- work around this by forcibly preventing
2081     // the signal from reaching gtk_scrolled_window_focus() if we don't have
2082     // any children which might accept focus (we know we don't accept the focus
2083     // ourselves as this signal is only connected in this case)
2084     if ( win->GetChildren().empty() )
2085         g_signal_stop_emission_by_name(widget, "focus");
2086 
2087     // we didn't change the focus
2088     return FALSE;
2089 }
2090 
2091 //-----------------------------------------------------------------------------
2092 // "enter_notify_event"
2093 //-----------------------------------------------------------------------------
2094 
2095 static gboolean
2096 gtk_window_enter_callback( GtkWidget*,
2097                            GdkEventCrossing *gdk_event,
2098                            wxWindowGTK *win )
2099 {
2100     wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
2101 
2102     // Event was emitted after a grab
2103     if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
2104 
2105     wxMouseEvent event( wxEVT_ENTER_WINDOW );
2106     InitMouseEvent(win, event, gdk_event);
2107 
2108     if ( !g_captureWindow )
2109         SendSetCursorEvent(win, event.m_x, event.m_y);
2110 
2111     return win->GTKProcessEvent(event);
2112 }
2113 
2114 //-----------------------------------------------------------------------------
2115 // "leave_notify_event"
2116 //-----------------------------------------------------------------------------
2117 
2118 static gboolean
2119 gtk_window_leave_callback( GtkWidget*,
2120                            GdkEventCrossing *gdk_event,
2121                            wxWindowGTK *win )
2122 {
2123     wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
2124 
2125     if (win->m_needCursorReset)
2126         win->GTKUpdateCursor();
2127 
2128     // Event was emitted after an ungrab
2129     if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
2130 
2131     wxMouseEvent event( wxEVT_LEAVE_WINDOW );
2132     InitMouseEvent(win, event, gdk_event);
2133 
2134     return win->GTKProcessEvent(event);
2135 }
2136 
2137 //-----------------------------------------------------------------------------
2138 // "value_changed" from scrollbar
2139 //-----------------------------------------------------------------------------
2140 
2141 static void
2142 gtk_scrollbar_value_changed(GtkRange* range, wxWindow* win)
2143 {
2144     wxEventType eventType = win->GTKGetScrollEventType(range);
2145     if (eventType != wxEVT_NULL)
2146     {
2147         // Convert scroll event type to scrollwin event type
2148         eventType += wxEVT_SCROLLWIN_TOP - wxEVT_SCROLL_TOP;
2149 
2150         // find the scrollbar which generated the event
2151         wxWindowGTK::ScrollDir dir = win->ScrollDirFromRange(range);
2152 
2153         // generate the corresponding wx event
2154         const int orient = wxWindow::OrientFromScrollDir(dir);
2155         wxScrollWinEvent event(eventType, win->GetScrollPos(orient), orient);
2156         event.SetEventObject(win);
2157 
2158         win->GTKProcessEvent(event);
2159     }
2160 }
2161 
2162 //-----------------------------------------------------------------------------
2163 // "button_press_event" from scrollbar
2164 //-----------------------------------------------------------------------------
2165 
2166 static gboolean
2167 gtk_scrollbar_button_press_event(GtkRange*, GdkEventButton*, wxWindow* win)
2168 {
2169     g_blockEventsOnScroll = true;
2170     win->m_mouseButtonDown = true;
2171 
2172     return false;
2173 }
2174 
2175 //-----------------------------------------------------------------------------
2176 // "event_after" from scrollbar
2177 //-----------------------------------------------------------------------------
2178 
2179 static void
2180 gtk_scrollbar_event_after(GtkRange* range, GdkEvent* event, wxWindow* win)
2181 {
2182     if (event->type == GDK_BUTTON_RELEASE)
2183     {
2184         g_signal_handlers_block_by_func(range, (void*)gtk_scrollbar_event_after, win);
2185 
2186         const int orient = wxWindow::OrientFromScrollDir(
2187                                         win->ScrollDirFromRange(range));
2188         wxScrollWinEvent evt(wxEVT_SCROLLWIN_THUMBRELEASE,
2189                                 win->GetScrollPos(orient), orient);
2190         evt.SetEventObject(win);
2191         win->GTKProcessEvent(evt);
2192     }
2193 }
2194 
2195 //-----------------------------------------------------------------------------
2196 // "button_release_event" from scrollbar
2197 //-----------------------------------------------------------------------------
2198 
2199 static gboolean
2200 gtk_scrollbar_button_release_event(GtkRange* range, GdkEventButton*, wxWindow* win)
2201 {
2202     g_blockEventsOnScroll = false;
2203     win->m_mouseButtonDown = false;
2204     // If thumb tracking
2205     if (win->m_isScrolling)
2206     {
2207         win->m_isScrolling = false;
2208         // Hook up handler to send thumb release event after this emission is finished.
2209         // To allow setting scroll position from event handler, sending event must
2210         // be deferred until after the GtkRange handler for this signal has run
2211         g_signal_handlers_unblock_by_func(range, (void*)gtk_scrollbar_event_after, win);
2212     }
2213 
2214     return false;
2215 }
2216 
2217 //-----------------------------------------------------------------------------
2218 // "realize" from m_widget
2219 //-----------------------------------------------------------------------------
2220 
2221 static void
2222 gtk_window_realized_callback(GtkWidget* WXUNUSED(widget), wxWindowGTK* win)
2223 {
2224     win->GTKHandleRealized();
2225 }
2226 
2227 //-----------------------------------------------------------------------------
2228 // "size_allocate" from m_wxwindow or m_widget
2229 //-----------------------------------------------------------------------------
2230 
2231 static void
2232 size_allocate(GtkWidget* WXUNUSED_IN_GTK2(widget), GtkAllocation* alloc, wxWindow* win)
2233 {
2234     int w = alloc->width;
2235     int h = alloc->height;
2236 #if GTK_CHECK_VERSION(3,14,0)
2237     if (wx_is_at_least_gtk3(14))
2238     {
2239         // Prevent under-allocated widgets from drawing outside their allocation
2240         GtkAllocation clip;
2241         gtk_widget_get_clip(widget, &clip);
2242         if (clip.width > w || clip.height > h)
2243         {
2244             GtkStyleContext* sc = gtk_widget_get_style_context(widget);
2245             int outline_offset, outline_width;
2246             gtk_style_context_get(sc, gtk_style_context_get_state(sc),
2247                 "outline-offset", &outline_offset, "outline-width", &outline_width, NULL);
2248             const int outline = outline_offset + outline_width;
2249             GtkAllocation a = *alloc;
2250             if (outline > 0)
2251             {
2252                 // Allow enough room for focus indicator "outline", it's drawn
2253                 // outside of GtkCheckButton allocation with Adwaita theme
2254                 a.x -= outline;
2255                 a.y -= outline;
2256                 a.width += outline + outline;
2257                 a.height += outline + outline;
2258             }
2259             gtk_widget_set_clip(widget, &a);
2260         }
2261     }
2262 #endif
2263     if (win->m_wxwindow)
2264     {
2265         GtkBorder border;
2266         WX_PIZZA(win->m_wxwindow)->get_border(border);
2267         w -= border.left + border.right;
2268         h -= border.top + border.bottom;
2269         if (w < 0) w = 0;
2270         if (h < 0) h = 0;
2271     }
2272     GtkAllocation a;
2273     gtk_widget_get_allocation(win->m_widget, &a);
2274     // update position for widgets in native containers, such as wxToolBar
2275     if (!WX_IS_PIZZA(gtk_widget_get_parent(win->m_widget)))
2276     {
2277         win->m_x = a.x;
2278         win->m_y = a.y;
2279     }
2280     win->m_useCachedClientSize = true;
2281     win->m_isGtkPositionValid = true;
2282     if (win->m_clientWidth != w || win->m_clientHeight != h)
2283     {
2284         win->m_clientWidth  = w;
2285         win->m_clientHeight = h;
2286         // this callback can be connected to m_wxwindow,
2287         // so always get size from m_widget->allocation
2288         win->m_width  = a.width;
2289         win->m_height = a.height;
2290         {
2291             const bool save_inSizeAllocate = g_inSizeAllocate;
2292             g_inSizeAllocate = true;
2293             wxSizeEvent event(win->GetSize(), win->GetId());
2294             event.SetEventObject(win);
2295             win->GTKProcessEvent(event);
2296             g_inSizeAllocate = save_inSizeAllocate;
2297         }
2298     }
2299 }
2300 
2301 //-----------------------------------------------------------------------------
2302 // "grab_broken_event"
2303 //-----------------------------------------------------------------------------
2304 
2305 #if GTK_CHECK_VERSION(2, 8, 0)
2306 static gboolean
2307 gtk_window_grab_broken( GtkWidget*,
2308                         GdkEventGrabBroken *event,
2309                         wxWindow *win )
2310 {
2311     // Mouse capture has been lost involuntarily, notify the application
2312     if(!event->keyboard && wxWindow::GetCapture() == win)
2313     {
2314         wxWindowGTK::GTKHandleCaptureLost();
2315     }
2316     return false;
2317 }
2318 #endif
2319 
2320 //-----------------------------------------------------------------------------
2321 // "unrealize"
2322 //-----------------------------------------------------------------------------
2323 
2324 static void unrealize(GtkWidget*, wxWindow* win)
2325 {
2326     win->GTKHandleUnrealize();
2327 }
2328 
2329 #if GTK_CHECK_VERSION(3,8,0)
2330 //-----------------------------------------------------------------------------
2331 // "layout" from GdkFrameClock
2332 //-----------------------------------------------------------------------------
2333 
2334 static void frame_clock_layout(GdkFrameClock*, wxWindow* win)
2335 {
2336     win->GTKSizeRevalidate();
2337 }
2338 #endif // GTK_CHECK_VERSION(3,8,0)
2339 
2340 } // extern "C"
2341 
GTKHandleRealized()2342 void wxWindowGTK::GTKHandleRealized()
2343 {
2344     GdkWindow* const window = GTKGetDrawingWindow();
2345 
2346     if (m_wxwindow)
2347     {
2348         if (m_imContext == NULL)
2349         {
2350             // Create input method handler
2351             m_imContext = gtk_im_multicontext_new();
2352 
2353             // Cannot handle drawing preedited text yet
2354             gtk_im_context_set_use_preedit(m_imContext, false);
2355 
2356             g_signal_connect(m_imContext,
2357                 "commit", G_CALLBACK(gtk_wxwindow_commit_cb), this);
2358         }
2359         gtk_im_context_set_client_window(m_imContext, window);
2360     }
2361 
2362     // Use composited window if background is transparent, if supported.
2363     if (m_backgroundStyle == wxBG_STYLE_TRANSPARENT)
2364     {
2365 #if wxGTK_HAS_COMPOSITING_SUPPORT
2366         if (IsTransparentBackgroundSupported())
2367         {
2368             wxGCC_WARNING_SUPPRESS(deprecated-declarations)
2369             if (window)
2370                 gdk_window_set_composited(window, true);
2371             wxGCC_WARNING_RESTORE()
2372         }
2373         else
2374 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
2375         {
2376             // We revert to erase mode if transparency is not supported
2377             m_backgroundStyle = wxBG_STYLE_ERASE;
2378         }
2379     }
2380 
2381 #ifndef __WXGTK3__
2382     if (window && (
2383         m_backgroundStyle == wxBG_STYLE_PAINT ||
2384         m_backgroundStyle == wxBG_STYLE_TRANSPARENT))
2385     {
2386         gdk_window_set_back_pixmap(window, NULL, false);
2387     }
2388 #endif
2389 
2390 #if GTK_CHECK_VERSION(3,8,0)
2391     if (IsTopLevel() && gtk_check_version(3,8,0) == NULL)
2392     {
2393         GdkFrameClock* clock = gtk_widget_get_frame_clock(m_widget);
2394         if (clock &&
2395             !g_signal_handler_find(clock, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, this))
2396         {
2397             g_signal_connect(clock, "layout", G_CALLBACK(frame_clock_layout), this);
2398         }
2399     }
2400 #endif
2401 
2402     wxWindowCreateEvent event(static_cast<wxWindow*>(this));
2403     event.SetEventObject( this );
2404     GTKProcessEvent( event );
2405 
2406     GTKUpdateCursor(false, true);
2407 }
2408 
GTKHandleUnrealize()2409 void wxWindowGTK::GTKHandleUnrealize()
2410 {
2411     m_isGtkPositionValid = false;
2412 
2413     if (m_wxwindow)
2414     {
2415         if (m_imContext)
2416             gtk_im_context_set_client_window(m_imContext, NULL);
2417     }
2418 }
2419 
2420 // ----------------------------------------------------------------------------
2421 // this wxWindowBase function is implemented here (in platform-specific file)
2422 // because it is static and so couldn't be made virtual
2423 // ----------------------------------------------------------------------------
2424 
DoFindFocus()2425 wxWindow *wxWindowBase::DoFindFocus()
2426 {
2427 #if wxUSE_MENUS
2428     // For compatibility with wxMSW, pretend that showing a popup menu doesn't
2429     // change the focus and that it remains on the window showing it, even
2430     // though the real focus does change in GTK.
2431     extern wxMenu *wxCurrentPopupMenu;
2432     if ( wxCurrentPopupMenu )
2433         return wxCurrentPopupMenu->GetInvokingWindow();
2434 #endif // wxUSE_MENUS
2435 
2436     wxWindowGTK *focus = gs_pendingFocus ? gs_pendingFocus : gs_currentFocus;
2437     // the cast is necessary when we compile in wxUniversal mode
2438     return static_cast<wxWindow*>(focus);
2439 }
2440 
AddChildGTK(wxWindowGTK * child)2441 void wxWindowGTK::AddChildGTK(wxWindowGTK* child)
2442 {
2443     wxASSERT_MSG(m_wxwindow, "Cannot add a child to a window without a client area");
2444 
2445     // the window might have been scrolled already, we
2446     // have to adapt the position
2447     wxPizza* pizza = WX_PIZZA(m_wxwindow);
2448     child->m_x += pizza->m_scroll_x;
2449     child->m_y += pizza->m_scroll_y;
2450 
2451     pizza->put(child->m_widget,
2452         child->m_x, child->m_y, child->m_width, child->m_height);
2453 }
2454 
2455 //-----------------------------------------------------------------------------
2456 // global functions
2457 //-----------------------------------------------------------------------------
2458 
wxGetActiveWindow()2459 wxWindow *wxGetActiveWindow()
2460 {
2461     return wxWindow::FindFocus();
2462 }
2463 
2464 
2465 // Under Unix this is implemented using X11 functions in utilsx11.cpp but we
2466 // need to have this function under Windows too, so provide at least a stub.
2467 #ifdef GDK_WINDOWING_WIN32
wxGetKeyState(wxKeyCode WXUNUSED (key))2468 bool wxGetKeyState(wxKeyCode WXUNUSED(key))
2469 {
2470     wxFAIL_MSG(wxS("Not implemented under Windows"));
2471     return false;
2472 }
2473 #endif // __WINDOWS__
2474 
wxGetMouseState()2475 wxMouseState wxGetMouseState()
2476 {
2477     wxMouseState ms;
2478 
2479     gint x;
2480     gint y;
2481     GdkModifierType mask;
2482 
2483     GdkWindow* window = wxGetTopLevelGDK();
2484     GdkDisplay* display = gdk_window_get_display(window);
2485 #ifdef __WXGTK3__
2486 #ifdef __WXGTK4__
2487     GdkSeat* seat = gdk_display_get_default_seat(display);
2488     GdkDevice* device = gdk_seat_get_pointer(seat);
2489 #else
2490     wxGCC_WARNING_SUPPRESS(deprecated-declarations)
2491     GdkDeviceManager* manager = gdk_display_get_device_manager(display);
2492     GdkDevice* device = gdk_device_manager_get_client_pointer(manager);
2493     wxGCC_WARNING_RESTORE()
2494 #endif
2495     gdk_device_get_position(device, NULL, &x, &y);
2496     gdk_device_get_state(device, window, NULL, &mask);
2497 #else
2498     gdk_display_get_pointer(display, NULL, &x, &y, &mask);
2499 #endif
2500 
2501     ms.SetX(x);
2502     ms.SetY(y);
2503     ms.SetLeftDown((mask & GDK_BUTTON1_MASK) != 0);
2504     ms.SetMiddleDown((mask & GDK_BUTTON2_MASK) != 0);
2505     ms.SetRightDown((mask & GDK_BUTTON3_MASK) != 0);
2506     // see the comment in InitMouseEvent()
2507     ms.SetAux1Down((mask & GDK_BUTTON4_MASK) != 0);
2508     ms.SetAux2Down((mask & GDK_BUTTON5_MASK) != 0);
2509 
2510     ms.SetControlDown((mask & GDK_CONTROL_MASK) != 0);
2511     ms.SetShiftDown((mask & GDK_SHIFT_MASK) != 0);
2512     ms.SetAltDown((mask & GDK_MOD1_MASK) != 0);
2513     ms.SetMetaDown((mask & GDK_META_MASK) != 0);
2514 
2515     return ms;
2516 }
2517 
2518 //-----------------------------------------------------------------------------
2519 // wxWindowGTK
2520 //-----------------------------------------------------------------------------
2521 
2522 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2523 // method
2524 #ifdef __WXUNIVERSAL__
2525     wxIMPLEMENT_ABSTRACT_CLASS(wxWindowGTK, wxWindowBase);
2526 #endif // __WXUNIVERSAL__
2527 
Init()2528 void wxWindowGTK::Init()
2529 {
2530     // GTK specific
2531     m_widget = NULL;
2532     m_wxwindow = NULL;
2533     m_focusWidget = NULL;
2534 
2535     // position/size
2536     m_x = 0;
2537     m_y = 0;
2538     m_width = 0;
2539     m_height = 0;
2540 
2541     m_showOnIdle = false;
2542     m_needCursorReset = false;
2543     m_noExpose = false;
2544     m_nativeSizeEvent = false;
2545 #ifdef __WXGTK3__
2546     m_paintContext = NULL;
2547     m_styleProvider = NULL;
2548     m_needSizeEvent = false;
2549 #endif
2550 
2551     m_isScrolling = false;
2552     m_mouseButtonDown = false;
2553 
2554     // initialize scrolling stuff
2555     for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2556     {
2557         m_scrollBar[dir] = NULL;
2558         m_scrollPos[dir] = 0;
2559     }
2560 
2561     m_clientWidth =
2562     m_clientHeight = 0;
2563     m_useCachedClientSize = false;
2564     m_isGtkPositionValid = false;
2565 
2566     m_clipPaintRegion = false;
2567 
2568     m_imContext = NULL;
2569     m_imKeyEvent = NULL;
2570 
2571     m_dirtyTabOrder = false;
2572 }
2573 
wxWindowGTK()2574 wxWindowGTK::wxWindowGTK()
2575 {
2576     Init();
2577 }
2578 
wxWindowGTK(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)2579 wxWindowGTK::wxWindowGTK( wxWindow *parent,
2580                           wxWindowID id,
2581                           const wxPoint &pos,
2582                           const wxSize &size,
2583                           long style,
2584                           const wxString &name  )
2585 {
2586     Init();
2587 
2588     Create( parent, id, pos, size, style, name );
2589 }
2590 
GTKCreateScrolledWindowWith(GtkWidget * view)2591 void wxWindowGTK::GTKCreateScrolledWindowWith(GtkWidget* view)
2592 {
2593     wxASSERT_MSG( HasFlag(wxHSCROLL) || HasFlag(wxVSCROLL),
2594                   wxS("Must not be called if scrolling is not needed.") );
2595 
2596     m_widget = gtk_scrolled_window_new( NULL, NULL );
2597 
2598     GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
2599 
2600     // There is a conflict with default bindings at GTK+
2601     // level between scrolled windows and notebooks both of which want to use
2602     // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
2603     // direction and notebooks for changing pages -- we decide that if we don't
2604     // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
2605     // means we can get working keyboard navigation in notebooks
2606     if ( !HasFlag(wxHSCROLL) )
2607     {
2608         GtkBindingSet *
2609             bindings = gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget));
2610         if ( bindings )
2611         {
2612             gtk_binding_entry_remove(bindings, GDK_KEY_Page_Up, GDK_CONTROL_MASK);
2613             gtk_binding_entry_remove(bindings, GDK_KEY_Page_Down, GDK_CONTROL_MASK);
2614         }
2615     }
2616 
2617     // If wx[HV]SCROLL is not given, the corresponding scrollbar is not shown
2618     // at all. Otherwise it may be shown only on demand (default) or always, if
2619     // the wxALWAYS_SHOW_SB is specified.
2620     GtkPolicyType horzPolicy = HasFlag(wxHSCROLL)
2621                                 ? HasFlag(wxALWAYS_SHOW_SB)
2622                                     ? GTK_POLICY_ALWAYS
2623                                     : GTK_POLICY_AUTOMATIC
2624                                 : GTK_POLICY_NEVER;
2625     GtkPolicyType vertPolicy = HasFlag(wxVSCROLL)
2626                                 ? HasFlag(wxALWAYS_SHOW_SB)
2627                                     ? GTK_POLICY_ALWAYS
2628                                     : GTK_POLICY_AUTOMATIC
2629                                 : GTK_POLICY_NEVER;
2630     gtk_scrolled_window_set_policy( scrolledWindow, horzPolicy, vertPolicy );
2631 
2632     m_scrollBar[ScrollDir_Horz] = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(scrolledWindow));
2633     m_scrollBar[ScrollDir_Vert] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(scrolledWindow));
2634 
2635     gtk_container_add( GTK_CONTAINER(m_widget), view );
2636 
2637     // connect various scroll-related events
2638     for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2639     {
2640         // these handlers block mouse events to any window during scrolling
2641         // such as motion events and prevent GTK and wxWidgets from fighting
2642         // over where the slider should be
2643         g_signal_connect(m_scrollBar[dir], "button_press_event",
2644                      G_CALLBACK(gtk_scrollbar_button_press_event), this);
2645         g_signal_connect(m_scrollBar[dir], "button_release_event",
2646                      G_CALLBACK(gtk_scrollbar_button_release_event), this);
2647 
2648         gulong handler_id = g_signal_connect(m_scrollBar[dir], "event_after",
2649                             G_CALLBACK(gtk_scrollbar_event_after), this);
2650         g_signal_handler_block(m_scrollBar[dir], handler_id);
2651 
2652         // these handlers get notified when scrollbar slider moves
2653         g_signal_connect_after(m_scrollBar[dir], "value_changed",
2654                      G_CALLBACK(gtk_scrollbar_value_changed), this);
2655     }
2656 
2657     gtk_widget_show( view );
2658 }
2659 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)2660 bool wxWindowGTK::Create( wxWindow *parent,
2661                           wxWindowID id,
2662                           const wxPoint &pos,
2663                           const wxSize &size,
2664                           long style,
2665                           const wxString &name  )
2666 {
2667     // Get default border
2668     wxBorder border = GetBorder(style);
2669 
2670     style &= ~wxBORDER_MASK;
2671     style |= border;
2672 
2673     if (!PreCreation( parent, pos, size ) ||
2674         !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
2675     {
2676         wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2677         return false;
2678     }
2679 
2680         // We should accept the native look
2681 #if 0
2682         GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
2683         scroll_class->scrollbar_spacing = 0;
2684 #endif
2685 
2686 
2687     m_wxwindow = wxPizza::New(m_windowStyle);
2688 #ifndef __WXUNIVERSAL__
2689     if (HasFlag(wxPizza::BORDER_STYLES))
2690     {
2691         g_signal_connect(m_wxwindow, "parent_set",
2692             G_CALLBACK(parent_set), this);
2693     }
2694 #endif
2695     if (!HasFlag(wxHSCROLL) && !HasFlag(wxVSCROLL))
2696         m_widget = m_wxwindow;
2697     else
2698         GTKCreateScrolledWindowWith(m_wxwindow);
2699     g_object_ref(m_widget);
2700 
2701     if (m_parent)
2702         m_parent->DoAddChild( this );
2703 
2704     m_focusWidget = m_wxwindow;
2705 
2706     SetCanFocus(AcceptsFocus());
2707 
2708     PostCreation();
2709 
2710     return true;
2711 }
2712 
GTKDisconnect(void * instance)2713 void wxWindowGTK::GTKDisconnect(void* instance)
2714 {
2715     g_signal_handlers_disconnect_by_data(instance, this);
2716 }
2717 
~wxWindowGTK()2718 wxWindowGTK::~wxWindowGTK()
2719 {
2720     SendDestroyEvent();
2721 
2722     if (gs_currentFocus == this)
2723         gs_currentFocus = NULL;
2724     if (gs_pendingFocus == this)
2725         gs_pendingFocus = NULL;
2726     if (gs_lastFocus == this)
2727         gs_lastFocus = NULL;
2728 
2729     if ( gs_deferredFocusOut == this )
2730         gs_deferredFocusOut = NULL;
2731 
2732     // This is a real error, unlike the above, but it's already checked for in
2733     // the base class dtor and asserting here results is useless and, even
2734     // worse, results in abnormal termination when running unit tests which
2735     // throw exceptions from their assert handler, so don't assert here.
2736     if ( g_captureWindow == this )
2737         g_captureWindow = NULL;
2738 
2739     if (m_wxwindow)
2740     {
2741         GTKDisconnect(m_wxwindow);
2742         GtkWidget* parent = gtk_widget_get_parent(m_wxwindow);
2743         if (parent)
2744             GTKDisconnect(parent);
2745     }
2746     if (m_widget && m_widget != m_wxwindow)
2747         GTKDisconnect(m_widget);
2748 
2749     // destroy children before destroying this window itself
2750     DestroyChildren();
2751 
2752     // delete before the widgets to avoid a crash on solaris
2753     if ( m_imContext )
2754     {
2755         g_object_unref(m_imContext);
2756         m_imContext = NULL;
2757     }
2758 
2759 #ifdef __WXGTK3__
2760     if (m_styleProvider)
2761         g_object_unref(m_styleProvider);
2762 
2763     gs_sizeRevalidateList = g_list_remove_all(gs_sizeRevalidateList, this);
2764 #endif
2765 
2766 #ifdef wxGTK_HAS_GESTURES_SUPPORT
2767     wxWindowGestures::EraseForObject(static_cast<wxWindow*>(this));
2768 #endif // wxGTK_HAS_GESTURES_SUPPORT
2769 
2770     if (m_widget)
2771     {
2772         // Note that gtk_widget_destroy() does not destroy the widget, it just
2773         // emits the "destroy" signal. The widget is not actually destroyed
2774         // until its reference count drops to zero.
2775         gtk_widget_destroy(m_widget);
2776         // Release our reference, should be the last one
2777         g_object_unref(m_widget);
2778         m_widget = NULL;
2779     }
2780     m_wxwindow = NULL;
2781 }
2782 
PreCreation(wxWindowGTK * parent,const wxPoint & pos,const wxSize & size)2783 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos,  const wxSize &size )
2784 {
2785     if ( GTKNeedsParent() )
2786     {
2787         wxCHECK_MSG( parent, false, wxT("Must have non-NULL parent") );
2788     }
2789 
2790     // Use either the given size, or the default if -1 is given.
2791     // See wxWindowBase for these functions.
2792     m_width = WidthDefault(size.x) ;
2793     m_height = HeightDefault(size.y);
2794 
2795     if (pos != wxDefaultPosition)
2796     {
2797         m_x = pos.x;
2798         m_y = pos.y;
2799     }
2800 
2801     return true;
2802 }
2803 
PostCreation()2804 void wxWindowGTK::PostCreation()
2805 {
2806     wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2807 
2808     SetLayoutDirection(wxLayout_Default);
2809 
2810     GTKConnectFreezeWidget(m_widget);
2811     if (m_wxwindow && m_wxwindow != m_widget)
2812         GTKConnectFreezeWidget(m_wxwindow);
2813 
2814 #if wxGTK_HAS_COMPOSITING_SUPPORT
2815     // Set RGBA visual as soon as possible to minimize the possibility that
2816     // somebody uses the wrong one.
2817     if ( m_backgroundStyle == wxBG_STYLE_TRANSPARENT &&
2818             IsTransparentBackgroundSupported() )
2819     {
2820         GdkScreen *screen = gtk_widget_get_screen (m_widget);
2821 #ifdef __WXGTK3__
2822         gtk_widget_set_visual(m_widget, gdk_screen_get_rgba_visual(screen));
2823 #else
2824         GdkColormap *rgba_colormap = gdk_screen_get_rgba_colormap (screen);
2825 
2826         if (rgba_colormap)
2827             gtk_widget_set_colormap(m_widget, rgba_colormap);
2828 #endif
2829     }
2830 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
2831 
2832     if (m_wxwindow)
2833     {
2834         if (!m_noExpose)
2835         {
2836             // these get reported to wxWidgets -> wxPaintEvent
2837 #ifdef __WXGTK3__
2838             g_signal_connect(m_wxwindow, "draw", G_CALLBACK(draw), this);
2839 #else
2840             g_signal_connect(m_wxwindow, "expose_event", G_CALLBACK(expose_event), this);
2841 #endif
2842 
2843             if (GetLayoutDirection() == wxLayout_LeftToRight)
2844                 gtk_widget_set_redraw_on_allocate(m_wxwindow, HasFlag(wxFULL_REPAINT_ON_RESIZE));
2845         }
2846     }
2847 
2848     // focus handling
2849 
2850     if (!GTK_IS_WINDOW(m_widget))
2851     {
2852         if (m_focusWidget == NULL)
2853             m_focusWidget = m_widget;
2854 
2855         if (m_wxwindow)
2856         {
2857             g_signal_connect (m_focusWidget, "focus_in_event",
2858                           G_CALLBACK (gtk_window_focus_in_callback), this);
2859             g_signal_connect (m_focusWidget, "focus_out_event",
2860                                 G_CALLBACK (gtk_window_focus_out_callback), this);
2861         }
2862         else
2863         {
2864             g_signal_connect_after (m_focusWidget, "focus_in_event",
2865                           G_CALLBACK (gtk_window_focus_in_callback), this);
2866             g_signal_connect_after (m_focusWidget, "focus_out_event",
2867                                 G_CALLBACK (gtk_window_focus_out_callback), this);
2868         }
2869     }
2870 
2871     if ( !AcceptsFocusFromKeyboard() )
2872     {
2873         SetCanFocus(false);
2874 
2875         g_signal_connect(m_widget, "focus",
2876                             G_CALLBACK(wx_window_focus_callback), this);
2877     }
2878 
2879     // connect to the various key and mouse handlers
2880 
2881     GtkWidget *connect_widget = GetConnectWidget();
2882 
2883     ConnectWidget( connect_widget );
2884 
2885     // We cannot set colours, fonts and cursors before the widget has been
2886     // realized, so we do this directly after realization -- unless the widget
2887     // was in fact realized already.
2888     if ( gtk_widget_get_realized(connect_widget) )
2889     {
2890         GTKHandleRealized();
2891     }
2892     else
2893     {
2894         g_signal_connect (connect_widget, "realize",
2895                           G_CALLBACK (gtk_window_realized_callback), this);
2896     }
2897     g_signal_connect(connect_widget, "unrealize", G_CALLBACK(unrealize), this);
2898 
2899     if (!IsTopLevel())
2900     {
2901         g_signal_connect(m_wxwindow ? m_wxwindow : m_widget, "size_allocate",
2902             G_CALLBACK(size_allocate), this);
2903     }
2904 
2905 #if GTK_CHECK_VERSION(2, 8, 0)
2906     if ( wx_is_at_least_gtk2(8) )
2907     {
2908         // Make sure we can notify the app when mouse capture is lost
2909         if ( m_wxwindow )
2910         {
2911             g_signal_connect (m_wxwindow, "grab_broken_event",
2912                           G_CALLBACK (gtk_window_grab_broken), this);
2913         }
2914 
2915         if ( connect_widget != m_wxwindow )
2916         {
2917             g_signal_connect (connect_widget, "grab_broken_event",
2918                         G_CALLBACK (gtk_window_grab_broken), this);
2919         }
2920     }
2921 #endif // GTK+ >= 2.8
2922 
2923     if (!WX_IS_PIZZA(gtk_widget_get_parent(m_widget)) && !GTK_IS_WINDOW(m_widget))
2924         gtk_widget_set_size_request(m_widget, m_width, m_height);
2925 
2926     // apply any font or color changes made before creation
2927     GTKApplyWidgetStyle();
2928 
2929     InheritAttributes();
2930 
2931     // if the window had been disabled before being created, it should be
2932     // created in the initially disabled state
2933     if ( !m_isEnabled )
2934         DoEnable(false);
2935 
2936     // unless the window was created initially hidden (i.e. Hide() had been
2937     // called before Create()), we should show it at GTK+ level as well
2938     if (m_isShown)
2939         gtk_widget_show( m_widget );
2940 }
2941 
2942 unsigned long
GTKConnectWidget(const char * signal,wxGTKCallback callback)2943 wxWindowGTK::GTKConnectWidget(const char *signal, wxGTKCallback callback)
2944 {
2945     return g_signal_connect(m_widget, signal, callback, this);
2946 }
2947 
2948 // GSource callback functions for source used to detect new GDK events
2949 extern "C" {
source_prepare(GSource *,int *)2950 static gboolean source_prepare(GSource*, int*)
2951 {
2952     return !gs_isNewEvent;
2953 }
2954 
source_check(GSource *)2955 static gboolean source_check(GSource*)
2956 {
2957     // 'check' will only be called if 'prepare' returned false
2958     return false;
2959 }
2960 
source_dispatch(GSource *,GSourceFunc,void *)2961 static gboolean source_dispatch(GSource*, GSourceFunc, void*)
2962 {
2963     gs_isNewEvent = true;
2964     // don't remove this source
2965     return true;
2966 }
2967 }
2968 
2969 #ifdef wxGTK_HAS_GESTURES_SUPPORT
2970 
2971 // Currently used for Press and Tap gesture only
2972 enum GestureStates
2973 {
2974     begin  = 1,
2975     update,
2976     end
2977 };
2978 
2979 enum TrackedGestures
2980 {
2981     two_finger_tap = 0x0001,
2982     press_and_tap  = 0x0002,
2983     horizontal_pan = 0x0004,
2984     vertical_pan   = 0x0008
2985 };
2986 
2987 extern "C" {
2988 static void
pan_gesture_begin_callback(GtkGesture * WXUNUSED (gesture),GdkEventSequence * WXUNUSED (sequence),wxWindowGTK * WXUNUSED (win))2989 pan_gesture_begin_callback(GtkGesture* WXUNUSED(gesture), GdkEventSequence* WXUNUSED(sequence), wxWindowGTK* WXUNUSED(win))
2990 {
2991     gs_gestureStart = true;
2992 
2993     // Set it to 0, as this will be used to calculate the deltas for new pan gesture
2994     gs_lastOffset = 0;
2995 }
2996 }
2997 
2998 extern "C" {
2999 static void
horizontal_pan_gesture_end_callback(GtkGesture * gesture,GdkEventSequence * sequence,wxWindow * win)3000 horizontal_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* sequence, wxWindow* win)
3001 {
3002     wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
3003     if ( !data )
3004         return;
3005 
3006     // Do not process horizontal pan, if there was no "pan" signal for it.
3007     if ( !(data->m_allowedGestures & horizontal_pan) )
3008     {
3009         return;
3010     }
3011 
3012     gdouble x, y;
3013 
3014     if ( !gtk_gesture_get_point(gesture, sequence, &x, &y) )
3015     {
3016         return;
3017     }
3018 
3019     data->m_allowedGestures &= ~horizontal_pan;
3020 
3021     wxPanGestureEvent event(win->GetId());
3022 
3023     event.SetEventObject(win);
3024     event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
3025     event.SetGestureEnd();
3026 
3027     win->GTKProcessEvent(event);
3028 }
3029 }
3030 
3031 extern "C" {
3032 static void
vertical_pan_gesture_end_callback(GtkGesture * gesture,GdkEventSequence * sequence,wxWindow * win)3033 vertical_pan_gesture_end_callback(GtkGesture* gesture, GdkEventSequence* sequence, wxWindow* win)
3034 {
3035     wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
3036     if ( !data )
3037         return;
3038 
3039     // Do not process vertical pan, if there was no "pan" signal for it.
3040     if ( !(data->m_allowedGestures & vertical_pan) )
3041     {
3042         return;
3043     }
3044 
3045     gdouble x, y;
3046 
3047     if ( !gtk_gesture_get_point(gesture, sequence, &x, &y) )
3048     {
3049         return;
3050     }
3051 
3052     data->m_allowedGestures &= ~vertical_pan;
3053 
3054     wxPanGestureEvent event(win->GetId());
3055 
3056     event.SetEventObject(win);
3057     event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
3058     event.SetGestureEnd();
3059 
3060     win->GTKProcessEvent(event);
3061 }
3062 }
3063 
3064 extern "C" {
3065 static void
pan_gesture_callback(GtkGesture * gesture,GtkPanDirection direction,gdouble offset,wxWindow * win)3066 pan_gesture_callback(GtkGesture* gesture, GtkPanDirection direction, gdouble offset, wxWindow* win)
3067 {
3068     // The function that retrieves the GdkEventSequence (which will further be used to get the gesture point)
3069     // should be called only when the gestrure is active
3070     if ( !gtk_gesture_is_active(gesture) )
3071     {
3072         return;
3073     }
3074 
3075     GdkEventSequence* sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture));
3076 
3077     gdouble x, y;
3078 
3079     if ( !gtk_gesture_get_point(gesture, sequence, &x, &y) )
3080     {
3081         return;
3082     }
3083 
3084     wxPanGestureEvent event(win->GetId());
3085 
3086     event.SetEventObject(win);
3087     event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
3088 
3089     wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
3090     if ( !data )
3091         return;
3092 
3093     // This is the difference between this and the last pan gesture event in the current sequence
3094     int delta = wxRound(offset - gs_lastOffset);
3095 
3096     switch ( direction )
3097     {
3098         case GTK_PAN_DIRECTION_UP:
3099             data->m_allowedGestures |= vertical_pan;
3100             event.SetDelta(wxPoint(0, -delta));
3101             break;
3102 
3103         case GTK_PAN_DIRECTION_DOWN:
3104             data->m_allowedGestures |= vertical_pan;
3105             event.SetDelta(wxPoint(0, delta));
3106             break;
3107 
3108         case GTK_PAN_DIRECTION_RIGHT:
3109             data->m_allowedGestures |= horizontal_pan;
3110             event.SetDelta(wxPoint(delta, 0));
3111             break;
3112 
3113         case GTK_PAN_DIRECTION_LEFT:
3114             data->m_allowedGestures |= horizontal_pan;
3115             event.SetDelta(wxPoint(-delta, 0));
3116             break;
3117     }
3118 
3119     // Update gs_lastOffset
3120     gs_lastOffset = offset;
3121 
3122     if ( gs_gestureStart )
3123     {
3124         event.SetGestureStart();
3125         gs_gestureStart = false;
3126     }
3127 
3128     // Cancel press and tap gesture if it is not active during "pan" signal.
3129     if( !(data->m_activeGestures & press_and_tap) )
3130     {
3131         data->m_allowedGestures &= ~press_and_tap;
3132     }
3133 
3134     win->GTKProcessEvent(event);
3135 }
3136 }
3137 
3138 extern "C" {
3139 static void
zoom_gesture_callback(GtkGesture * gesture,gdouble scale,wxWindow * win)3140 zoom_gesture_callback(GtkGesture* gesture, gdouble scale, wxWindow* win)
3141 {
3142     gdouble x, y;
3143 
3144     if ( !gtk_gesture_get_bounding_box_center(gesture, &x, &y) )
3145     {
3146         return;
3147     }
3148 
3149     wxZoomGestureEvent event(win->GetId());
3150 
3151     event.SetEventObject(win);
3152     event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
3153     event.SetZoomFactor(scale);
3154 
3155     wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
3156     if ( !data )
3157         return;
3158 
3159     // Cancel "Two FInger Tap Event" if scale has changed
3160     if ( wxRound(scale * 1000) != wxRound(gs_lastScale * 1000) )
3161     {
3162         data->m_allowedGestures &= ~two_finger_tap;
3163     }
3164 
3165     gs_lastScale = scale;
3166 
3167     // Save this point because the point obtained through gtk_gesture_get_bounding_box_center()
3168     // in the "end" signal is not a zoom center
3169     gs_lastGesturePoint = wxPoint(wxRound(x), wxRound(y));
3170 
3171     win->GTKProcessEvent(event);
3172 }
3173 }
3174 
3175 extern "C" {
3176 static void
zoom_gesture_begin_callback(GtkGesture * gesture,GdkEventSequence * WXUNUSED (sequence),wxWindowGTK * win)3177 zoom_gesture_begin_callback(GtkGesture* gesture, GdkEventSequence* WXUNUSED(sequence), wxWindowGTK* win)
3178 {
3179     gdouble x, y;
3180 
3181     if ( !gtk_gesture_get_bounding_box_center(gesture, &x, &y) )
3182     {
3183         return;
3184     }
3185 
3186     gs_lastScale = 1.0;
3187 
3188     wxZoomGestureEvent event(win->GetId());
3189 
3190     event.SetEventObject(win);
3191     event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
3192     event.SetGestureStart();
3193 
3194     // Save this point because the point obtained through gtk_gesture_get_bounding_box_center()
3195     // in the "end" signal is not a zoom center
3196     gs_lastGesturePoint = wxPoint(wxRound(x), wxRound(y));
3197 
3198     win->GTKProcessEvent(event);
3199 }
3200 }
3201 
3202 extern "C" {
3203 static void
zoom_gesture_end_callback(GtkGesture * WXUNUSED (gesture),GdkEventSequence * WXUNUSED (sequence),wxWindowGTK * win)3204 zoom_gesture_end_callback(GtkGesture* WXUNUSED(gesture), GdkEventSequence* WXUNUSED(sequence), wxWindowGTK* win)
3205 {
3206     wxZoomGestureEvent event(win->GetId());
3207 
3208     event.SetEventObject(win);
3209     event.SetPosition(gs_lastGesturePoint);
3210     event.SetGestureEnd();
3211     event.SetZoomFactor(gs_lastScale);
3212 
3213     win->GTKProcessEvent(event);
3214 }
3215 }
3216 
3217 extern "C" {
3218 static void
rotate_gesture_begin_callback(GtkGesture * gesture,GdkEventSequence * WXUNUSED (sequence),wxWindowGTK * win)3219 rotate_gesture_begin_callback(GtkGesture* gesture, GdkEventSequence* WXUNUSED(sequence), wxWindowGTK* win)
3220 {
3221     gdouble x, y;
3222 
3223     if ( !gtk_gesture_get_bounding_box_center(gesture, &x, &y) )
3224     {
3225         return;
3226     }
3227 
3228     wxRotateGestureEvent event(win->GetId());
3229 
3230     event.SetEventObject(win);
3231     event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
3232     event.SetGestureStart();
3233 
3234     // Save this point because the point obtained through gtk_gesture_get_bounding_box_center()
3235     // in the "end" signal is not a rotation center
3236     gs_lastGesturePoint = wxPoint(wxRound(x), wxRound(y));
3237 
3238     win->GTKProcessEvent(event);
3239 }
3240 }
3241 
3242 extern "C" {
3243 static void
rotate_gesture_callback(GtkGesture * gesture,gdouble WXUNUSED (angle_delta),gdouble angle,wxWindowGTK * win)3244 rotate_gesture_callback(GtkGesture* gesture, gdouble WXUNUSED(angle_delta), gdouble angle, wxWindowGTK* win)
3245 {
3246     gdouble x, y;
3247 
3248     if ( !gtk_gesture_get_bounding_box_center(gesture, &x, &y) )
3249     {
3250         return;
3251     }
3252 
3253     wxRotateGestureEvent event(win->GetId());
3254 
3255     event.SetEventObject(win);
3256     event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
3257 
3258     event.SetRotationAngle(angle);
3259 
3260     // Save the angle to set it when the gesture ends.
3261     gs_lastAngle = angle;
3262 
3263     // Save this point because the point obtained through gtk_gesture_get_bounding_box_center()
3264     // in the "end" signal is not a rotation center
3265     gs_lastGesturePoint = wxPoint(wxRound(x), wxRound(y));
3266 
3267     win->GTKProcessEvent(event);
3268 }
3269 }
3270 
3271 extern "C" {
3272 static void
rotate_gesture_end_callback(GtkGesture * WXUNUSED (gesture),GdkEventSequence * WXUNUSED (sequence),wxWindowGTK * win)3273 rotate_gesture_end_callback(GtkGesture* WXUNUSED(gesture), GdkEventSequence* WXUNUSED(sequence), wxWindowGTK* win)
3274 {
3275     wxRotateGestureEvent event(win->GetId());
3276 
3277     event.SetEventObject(win);
3278     event.SetPosition(gs_lastGesturePoint);
3279     event.SetGestureEnd();
3280     event.SetRotationAngle(gs_lastAngle);
3281 
3282     win->GTKProcessEvent(event);
3283 }
3284 }
3285 
3286 extern "C" {
3287 static void
long_press_gesture_callback(GtkGesture * WXUNUSED (gesture),gdouble x,gdouble y,wxWindowGTK * win)3288 long_press_gesture_callback(GtkGesture* WXUNUSED(gesture), gdouble x, gdouble y, wxWindowGTK* win)
3289 {
3290     wxLongPressEvent event(win->GetId());
3291 
3292     event.SetEventObject(win);
3293     event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
3294     event.SetGestureStart();
3295     event.SetGestureEnd();
3296 
3297     win->GTKProcessEvent(event);
3298 }
3299 }
3300 
3301 static void
wxEmitTwoFingerTapEvent(GdkEventTouch * gdk_event,wxWindow * win)3302 wxEmitTwoFingerTapEvent(GdkEventTouch* gdk_event, wxWindow* win)
3303 {
3304     wxTwoFingerTapEvent event(win->GetId());
3305 
3306     event.SetEventObject(win);
3307 
3308     wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
3309     if ( !data )
3310         return;
3311 
3312     double lastX = data->m_lastTouchPoint.x;
3313     double lastY = data->m_lastTouchPoint.y;
3314 
3315     // Calculate smaller of x coordinate between 2 touches
3316     double left = lastX <= gdk_event->x ? lastX : gdk_event->x;
3317 
3318     // Calculate smaller of y coordinate between 2 touches
3319     double up = lastY <= gdk_event->y ? lastY : gdk_event->y;
3320 
3321     // Calculate gesture point .i.e center of the box formed by two touches
3322     double x = left + abs(lastX - gdk_event->x)/2;
3323     double y = up + abs(lastY - gdk_event->y)/2;
3324 
3325     event.SetPosition(wxPoint(wxRound(x), wxRound(y)));
3326     event.SetGestureStart();
3327     event.SetGestureEnd();
3328 
3329     win->GTKProcessEvent(event);
3330 }
3331 
3332 static void
wxEmitPressAndTapEvent(GdkEventTouch * gdk_event,wxWindow * win)3333 wxEmitPressAndTapEvent(GdkEventTouch* gdk_event, wxWindow* win)
3334 {
3335     wxPressAndTapEvent event(win->GetId());
3336 
3337     event.SetEventObject(win);
3338 
3339     wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
3340     if ( !data )
3341         return;
3342 
3343     switch ( data->m_gestureState )
3344     {
3345         case begin:
3346             event.SetGestureStart();
3347             break;
3348 
3349         case update:
3350             // Update touch point as the touch corresponding to "press" is moving
3351             if ( data->m_touchSequence == gdk_event->sequence )
3352             {
3353                 data->m_lastTouchPoint.x = gdk_event->x;
3354                 data->m_lastTouchPoint.y = gdk_event->y;
3355             }
3356             break;
3357 
3358         case end:
3359             event.SetGestureEnd();
3360             break;
3361     }
3362 
3363     event.SetPosition(data->m_lastTouchPoint);
3364 
3365     win->GTKProcessEvent(event);
3366 }
3367 
3368 extern "C" {
3369 static void
touch_callback(GtkWidget * WXUNUSED (widget),GdkEventTouch * gdk_event,wxWindow * win)3370 touch_callback(GtkWidget* WXUNUSED(widget), GdkEventTouch* gdk_event, wxWindow* win)
3371 {
3372     wxWindowGesturesData* const data = wxWindowGestures::FromObject(win);
3373     if ( !data )
3374         return;
3375 
3376     switch ( gdk_event->type )
3377     {
3378         case GDK_TOUCH_BEGIN:
3379             data->m_touchCount++;
3380 
3381             data->m_allowedGestures &= ~two_finger_tap;
3382 
3383             if ( data->m_touchCount == 1 )
3384             {
3385                 data->m_lastTouchTime = gdk_event->time;
3386                 data->m_lastTouchPoint.x = gdk_event->x;
3387                 data->m_lastTouchPoint.y = gdk_event->y;
3388 
3389                 // Save the sequence which identifies touch corresponding to "press"
3390                 data->m_touchSequence = gdk_event->sequence;
3391 
3392                 // "Press and Tap Event" may occur in future
3393                 data->m_allowedGestures |= press_and_tap;
3394             }
3395 
3396             // Check if two fingers are placed together .i.e difference between their time stamps is <= 200 milliseconds
3397             else if ( data->m_touchCount == 2 && gdk_event->time - data->m_lastTouchTime <= wxTwoFingerTimeInterval )
3398             {
3399                 // "Two Finger Tap Event" may be possible in the future
3400                 data->m_allowedGestures |= two_finger_tap;
3401 
3402                 // Cancel "Press and Tap Event"
3403                 data->m_allowedGestures &= ~press_and_tap;
3404             }
3405             break;
3406 
3407         case GDK_TOUCH_UPDATE:
3408             // If press and tap gesture is active and touch corresponding to that gesture is moving
3409             if ( (data->m_activeGestures & press_and_tap) && gdk_event->sequence == data->m_touchSequence )
3410             {
3411                 data->m_gestureState = update;
3412                 wxEmitPressAndTapEvent(gdk_event, win);
3413             }
3414             break;
3415 
3416         case GDK_TOUCH_END:
3417         case GDK_TOUCH_CANCEL:
3418             data->m_touchCount--;
3419 
3420             if ( data->m_touchCount == 1 )
3421             {
3422                 data->m_lastTouchTime = gdk_event->time;
3423 
3424                 // If the touch corresponding to "press" is present and "tap" is produced by some ather touch
3425                 if ( (data->m_allowedGestures & press_and_tap) && gdk_event->sequence != data->m_touchSequence )
3426                 {
3427                     // Press and Tap gesture becomes active now
3428                     if ( !(data->m_activeGestures & press_and_tap) )
3429                     {
3430                         data->m_gestureState = begin;
3431                         data->m_activeGestures |= press_and_tap;
3432                     }
3433 
3434                     else
3435                     {
3436                         data->m_gestureState = update;
3437                     }
3438 
3439                     wxEmitPressAndTapEvent(gdk_event, win);
3440                 }
3441             }
3442 
3443             // Check if "Two Finger Tap Event" is possible and both the fingers have been lifted up together
3444             else if ( (data->m_allowedGestures & two_finger_tap) && !data->m_touchCount
3445                       && gdk_event->time - data->m_lastTouchTime <= wxTwoFingerTimeInterval )
3446             {
3447                 // Process Two Finger Tap Event
3448                 wxEmitTwoFingerTapEvent(gdk_event, win);
3449             }
3450 
3451             // If the gesture was active and the touch corresponding to "press" is no longer on the screen
3452             if ( (data->m_activeGestures & press_and_tap) && gdk_event->sequence == data->m_touchSequence )
3453             {
3454                 data->m_gestureState = end;
3455 
3456                 data->m_activeGestures &= ~press_and_tap;
3457 
3458                 data->m_allowedGestures &= ~press_and_tap;
3459 
3460                 wxEmitPressAndTapEvent(gdk_event, win);
3461             }
3462             break;
3463 
3464         default:
3465         break;
3466     }
3467 }
3468 }
3469 
Reinit(wxWindowGTK * win,GtkWidget * widget,int eventsMask)3470 void wxWindowGesturesData::Reinit(wxWindowGTK* win,
3471                                   GtkWidget *widget,
3472                                   int eventsMask)
3473 {
3474     m_touchCount = 0;
3475     m_lastTouchTime = 0;
3476     m_gestureState = 0;
3477     m_allowedGestures = 0;
3478     m_activeGestures = 0;
3479     m_touchSequence = NULL;
3480 
3481     if ( eventsMask & wxTOUCH_VERTICAL_PAN_GESTURE )
3482     {
3483         eventsMask &= ~wxTOUCH_VERTICAL_PAN_GESTURE;
3484 
3485         m_vertical_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_VERTICAL);
3486 
3487         gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_vertical_pan_gesture), GTK_PHASE_TARGET);
3488 
3489         g_signal_connect (m_vertical_pan_gesture, "begin",
3490                           G_CALLBACK(pan_gesture_begin_callback), win);
3491         g_signal_connect (m_vertical_pan_gesture, "pan",
3492                           G_CALLBACK(pan_gesture_callback), win);
3493         g_signal_connect (m_vertical_pan_gesture, "end",
3494                           G_CALLBACK(vertical_pan_gesture_end_callback), win);
3495         g_signal_connect (m_vertical_pan_gesture, "cancel",
3496                           G_CALLBACK(vertical_pan_gesture_end_callback), win);
3497     }
3498     else
3499     {
3500         m_vertical_pan_gesture = NULL;
3501     }
3502 
3503     if ( eventsMask & wxTOUCH_HORIZONTAL_PAN_GESTURE )
3504     {
3505         eventsMask &= ~wxTOUCH_HORIZONTAL_PAN_GESTURE;
3506 
3507         m_horizontal_pan_gesture = gtk_gesture_pan_new(widget, GTK_ORIENTATION_HORIZONTAL);
3508 
3509         // Pan signals are also generated in case of "left mouse down + mouse move". This can be disabled by
3510         // calling gtk_gesture_single_set_touch_only(GTK_GESTURE_SINGLE(m_horizontal_pan_gesture), TRUE) and
3511         // gtk_gesture_single_set_touch_only(GTK_GESTURE_SINGLE(verticaal_pan_gesture), TRUE) which will allow
3512         // pan signals only for Touch events.
3513 
3514         gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_horizontal_pan_gesture), GTK_PHASE_TARGET);
3515 
3516         g_signal_connect (m_horizontal_pan_gesture, "begin",
3517                           G_CALLBACK(pan_gesture_begin_callback), win);
3518         g_signal_connect (m_horizontal_pan_gesture, "pan",
3519                           G_CALLBACK(pan_gesture_callback), win);
3520         g_signal_connect (m_horizontal_pan_gesture, "end",
3521                           G_CALLBACK(horizontal_pan_gesture_end_callback), win);
3522         g_signal_connect (m_horizontal_pan_gesture, "cancel",
3523                           G_CALLBACK(horizontal_pan_gesture_end_callback), win);
3524     }
3525     else
3526     {
3527         m_horizontal_pan_gesture = NULL;
3528     }
3529 
3530     if ( eventsMask & wxTOUCH_ZOOM_GESTURE )
3531     {
3532         eventsMask &= ~wxTOUCH_ZOOM_GESTURE;
3533 
3534         m_zoom_gesture = gtk_gesture_zoom_new(widget);
3535 
3536         gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_zoom_gesture), GTK_PHASE_TARGET);
3537 
3538         g_signal_connect (m_zoom_gesture, "begin",
3539                           G_CALLBACK(zoom_gesture_begin_callback), win);
3540         g_signal_connect (m_zoom_gesture, "scale-changed",
3541                           G_CALLBACK(zoom_gesture_callback), win);
3542         g_signal_connect (m_zoom_gesture, "end",
3543                           G_CALLBACK(zoom_gesture_end_callback), win);
3544         g_signal_connect (m_zoom_gesture, "cancel",
3545                           G_CALLBACK(zoom_gesture_end_callback), win);
3546     }
3547     else
3548     {
3549         m_zoom_gesture = NULL;
3550     }
3551 
3552     if ( eventsMask & wxTOUCH_ROTATE_GESTURE )
3553     {
3554         eventsMask &= ~wxTOUCH_ROTATE_GESTURE;
3555 
3556         m_rotate_gesture = gtk_gesture_rotate_new(widget);
3557 
3558         gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER(m_rotate_gesture), GTK_PHASE_TARGET);
3559 
3560         g_signal_connect (m_rotate_gesture, "begin",
3561                           G_CALLBACK(rotate_gesture_begin_callback), win);
3562         g_signal_connect (m_rotate_gesture, "angle-changed",
3563                           G_CALLBACK(rotate_gesture_callback), win);
3564         g_signal_connect (m_rotate_gesture, "end",
3565                           G_CALLBACK(rotate_gesture_end_callback), win);
3566         g_signal_connect (m_rotate_gesture, "cancel",
3567                           G_CALLBACK(rotate_gesture_end_callback), win);
3568     }
3569     else
3570     {
3571         m_rotate_gesture = NULL;
3572     }
3573 
3574     if ( eventsMask & wxTOUCH_PRESS_GESTURES )
3575     {
3576         eventsMask &= ~wxTOUCH_PRESS_GESTURES;
3577 
3578         m_long_press_gesture = gtk_gesture_long_press_new(widget);
3579 
3580         // "pressed" signal is also generated when left mouse is down for some minimum duration of time.
3581         // This can be disable by calling gtk_gesture_single_set_touch_only(GTK_GESTURE_SINGLE(m_long_press_gesture), TRUE)
3582         // which will allow "pressed" signal only for Touch events.
3583 
3584         gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(m_long_press_gesture), GTK_PHASE_TARGET);
3585 
3586         g_signal_connect (m_long_press_gesture, "pressed",
3587                           G_CALLBACK(long_press_gesture_callback), win);
3588     }
3589     else
3590     {
3591         m_long_press_gesture = NULL;
3592     }
3593 
3594     wxASSERT_MSG( eventsMask == 0, "Unknown touch event mask bit specified" );
3595 
3596     // GDK_TOUCHPAD_GESTURE_MASK was added in 3.18, but we can just define it
3597     // ourselves if we use an earlier version when compiling.
3598 #if !GTK_CHECK_VERSION(3,18,0)
3599     #define GDK_TOUCHPAD_GESTURE_MASK (1 << 24)
3600 #endif
3601     if ( gtk_check_version(3, 18, 0) == NULL )
3602     {
3603         gtk_widget_add_events(widget, GDK_TOUCHPAD_GESTURE_MASK);
3604     }
3605 
3606     g_signal_connect (widget, "touch-event",
3607                       G_CALLBACK(touch_callback), win);
3608 }
3609 
Free()3610 void wxWindowGesturesData::Free()
3611 {
3612     g_clear_object(&m_vertical_pan_gesture);
3613     g_clear_object(&m_horizontal_pan_gesture);
3614     g_clear_object(&m_zoom_gesture);
3615     g_clear_object(&m_rotate_gesture);
3616     g_clear_object(&m_long_press_gesture);
3617 
3618     // We don't current remove GDK_TOUCHPAD_GESTURE_MASK as this can't be done
3619     // for a window as long as it's realized, and this might still be the case
3620     // if we're called from EnableTouchEvents(wxTOUCH_NONE) and not from the
3621     // dtor, but it shouldn't really be a problem.
3622 }
3623 
3624 #endif // wxGTK_HAS_GESTURES_SUPPORT
3625 
3626 // This method must be always defined for GTK+ 3 as it's declared in the
3627 // header, where we can't (easily) test for wxGTK_HAS_GESTURES_SUPPORT.
3628 #ifdef __WXGTK3__
3629 
EnableTouchEvents(int eventsMask)3630 bool wxWindowGTK::EnableTouchEvents(int eventsMask)
3631 {
3632 #ifdef wxGTK_HAS_GESTURES_SUPPORT
3633     // Check if gestures support is also available during run-time.
3634     if ( gtk_check_version(3, 14, 0) == NULL )
3635     {
3636         wxWindowGesturesData* const dataOld = wxWindowGestures::FromObject(static_cast<wxWindow*>(this));
3637 
3638         if ( eventsMask == wxTOUCH_NONE )
3639         {
3640             // Reset the gestures data used by this object, but don't destroy
3641             // it, as we could be called from an event handler, in which case
3642             // this object could be still used after the event handler returns.
3643             if ( dataOld )
3644                 dataOld->Free();
3645         }
3646         else
3647         {
3648             GtkWidget* const widget = GetConnectWidget();
3649 
3650             if ( dataOld )
3651             {
3652                 dataOld->Reinit(this, widget, eventsMask);
3653             }
3654             else
3655             {
3656                 wxWindowGesturesData* const
3657                     dataNew = new wxWindowGesturesData(this, widget, eventsMask);
3658                 wxWindowGestures::StoreForObject(static_cast<wxWindow*>(this), dataNew);
3659             }
3660         }
3661 
3662         return true;
3663     }
3664 #endif // wxGTK_HAS_GESTURES_SUPPORT
3665 
3666     return wxWindowBase::EnableTouchEvents(eventsMask);
3667 }
3668 
3669 #endif // __WXGTK3__
3670 
ConnectWidget(GtkWidget * widget)3671 void wxWindowGTK::ConnectWidget( GtkWidget *widget )
3672 {
3673     static bool isSourceAttached;
3674     if (!isSourceAttached)
3675     {
3676         // attach GSource to detect new GDK events
3677         isSourceAttached = true;
3678         static GSourceFuncs funcs = {
3679             source_prepare, source_check, source_dispatch,
3680             NULL, NULL, NULL
3681         };
3682         GSource* source = g_source_new(&funcs, sizeof(GSource));
3683         // priority slightly higher than GDK_PRIORITY_EVENTS
3684         g_source_set_priority(source, GDK_PRIORITY_EVENTS - 1);
3685         g_source_attach(source, NULL);
3686         g_source_unref(source);
3687     }
3688 
3689     g_signal_connect (widget, "key_press_event",
3690                       G_CALLBACK (gtk_window_key_press_callback), this);
3691     g_signal_connect (widget, "key_release_event",
3692                       G_CALLBACK (gtk_window_key_release_callback), this);
3693     g_signal_connect (widget, "button_press_event",
3694                       G_CALLBACK (gtk_window_button_press_callback), this);
3695     g_signal_connect (widget, "button_release_event",
3696                       G_CALLBACK (gtk_window_button_release_callback), this);
3697     g_signal_connect (widget, "motion_notify_event",
3698                       G_CALLBACK (gtk_window_motion_notify_callback), this);
3699 
3700     g_signal_connect(widget, "scroll_event", G_CALLBACK(scroll_event), this);
3701     GtkRange* range = m_scrollBar[ScrollDir_Horz];
3702     if (range)
3703         g_signal_connect(range, "scroll_event", G_CALLBACK(scroll_event), this);
3704     range = m_scrollBar[ScrollDir_Vert];
3705     if (range)
3706         g_signal_connect(range, "scroll_event", G_CALLBACK(scroll_event), this);
3707 
3708     g_signal_connect (widget, "popup_menu",
3709                      G_CALLBACK (wxgtk_window_popup_menu_callback), this);
3710     g_signal_connect (widget, "enter_notify_event",
3711                       G_CALLBACK (gtk_window_enter_callback), this);
3712     g_signal_connect (widget, "leave_notify_event",
3713                       G_CALLBACK (gtk_window_leave_callback), this);
3714 }
3715 
DoMoveWindow(int x,int y,int width,int height)3716 void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
3717 {
3718     GtkWidget* parent = gtk_widget_get_parent(m_widget);
3719     wxPizza* pizza = NULL;
3720     if (WX_IS_PIZZA(parent))
3721     {
3722         pizza = WX_PIZZA(parent);
3723         pizza->move(m_widget, x, y, width, height);
3724         if (
3725 #ifdef __WXGTK3__
3726             !g_inSizeAllocate &&
3727 #endif
3728             gtk_widget_get_visible(m_widget))
3729         {
3730             // in case only the position is changing
3731             gtk_widget_queue_resize(m_widget);
3732         }
3733     }
3734 
3735     gtk_widget_set_size_request(m_widget, width, height);
3736 
3737 #ifdef __WXGTK3__
3738     // With GTK3, gtk_widget_queue_resize() is ignored while a size-allocate
3739     // is in progress. This situation is common in wxWidgets, since
3740     // size-allocate can generate wxSizeEvent and size event handlers often
3741     // call SetSize(), directly or indirectly. It should be fine to call
3742     // gtk_widget_size_allocate() immediately in this case.
3743     if (g_inSizeAllocate && gtk_widget_get_visible(m_widget) && width > 0 && height > 0)
3744     {
3745         // obligatory size request before size allocate to avoid GTK3 warnings
3746         GtkRequisition req;
3747         gtk_widget_get_preferred_size(m_widget, &req, NULL);
3748 
3749         if (pizza)
3750             pizza->size_allocate_child(m_widget, x, y, width, height);
3751         else
3752         {
3753             GtkAllocation a = { x, y, width, height };
3754             gtk_widget_size_allocate(m_widget, &a);
3755         }
3756     }
3757 #endif // __WXGTK3__
3758 }
3759 
ConstrainSize()3760 void wxWindowGTK::ConstrainSize()
3761 {
3762 #ifdef __WXGPE__
3763     // GPE's window manager doesn't like size hints at all, esp. when the user
3764     // has to use the virtual keyboard, so don't constrain size there
3765     if (!IsTopLevel())
3766 #endif
3767     {
3768         const wxSize minSize = GetMinSize();
3769         const wxSize maxSize = GetMaxSize();
3770         if (minSize.x > 0 && m_width  < minSize.x) m_width  = minSize.x;
3771         if (minSize.y > 0 && m_height < minSize.y) m_height = minSize.y;
3772         if (maxSize.x > 0 && m_width  > maxSize.x) m_width  = maxSize.x;
3773         if (maxSize.y > 0 && m_height > maxSize.y) m_height = maxSize.y;
3774     }
3775 }
3776 
DoSetSize(int x,int y,int width,int height,int sizeFlags)3777 void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
3778 {
3779     wxCHECK_RET(m_widget, "invalid window");
3780 
3781     int scrollX = 0, scrollY = 0;
3782     GtkWidget* parent = gtk_widget_get_parent(m_widget);
3783     if (WX_IS_PIZZA(parent))
3784     {
3785         wxPizza* pizza = WX_PIZZA(parent);
3786         scrollX = pizza->m_scroll_x;
3787         scrollY = pizza->m_scroll_y;
3788     }
3789     if (x != -1 || (sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
3790         x += scrollX;
3791     else
3792         x = m_x;
3793     if (y != -1 || (sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
3794         y += scrollY;
3795     else
3796         y = m_y;
3797 
3798     // calculate the best size if we should auto size the window
3799     if ( ((sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1) ||
3800          ((sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1) )
3801     {
3802         const wxSize sizeBest = GetBestSize();
3803         if ( (sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1 )
3804             width = sizeBest.x;
3805         if ( (sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1 )
3806             height = sizeBest.y;
3807     }
3808 
3809     if (width == -1)
3810         width = m_width;
3811     if (height == -1)
3812         height = m_height;
3813 
3814     const bool sizeChange = m_width != width || m_height != height;
3815     const bool positionChange = m_x != x || m_y != y;
3816 
3817     if (sizeChange)
3818         m_useCachedClientSize = false;
3819     if (positionChange)
3820         m_isGtkPositionValid = false;
3821 
3822     if (sizeChange || positionChange)
3823     {
3824         m_x = x;
3825         m_y = y;
3826         m_width = width;
3827         m_height = height;
3828 
3829         /* the default button has a border around it */
3830         if (gtk_widget_get_can_default(m_widget))
3831         {
3832             GtkBorder *default_border = NULL;
3833             gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
3834             if (default_border)
3835             {
3836                 x -= default_border->left;
3837                 y -= default_border->top;
3838                 width += default_border->left + default_border->right;
3839                 height += default_border->top + default_border->bottom;
3840                 gtk_border_free( default_border );
3841             }
3842         }
3843 
3844         DoMoveWindow(x, y, width, height);
3845     }
3846 
3847     if (((sizeChange
3848 #ifdef __WXGTK3__
3849                      || m_needSizeEvent
3850 #endif
3851                                        ) && !m_nativeSizeEvent) || (sizeFlags & wxSIZE_FORCE_EVENT))
3852     {
3853 #ifdef __WXGTK3__
3854         m_needSizeEvent = false;
3855 #endif
3856         // update these variables to keep size_allocate handler
3857         // from sending another size event for this change
3858         DoGetClientSize(&m_clientWidth, &m_clientHeight);
3859 
3860         wxSizeEvent event( wxSize(m_width,m_height), GetId() );
3861         event.SetEventObject( this );
3862         HandleWindowEvent( event );
3863     }
3864 }
3865 
GTKShowFromOnIdle()3866 bool wxWindowGTK::GTKShowFromOnIdle()
3867 {
3868     if (m_isShown && m_showOnIdle && !gtk_widget_get_visible(m_widget))
3869     {
3870         GtkAllocation alloc;
3871         alloc.x = m_x;
3872         alloc.y = m_y;
3873         alloc.width = m_width;
3874         alloc.height = m_height;
3875         gtk_widget_size_allocate( m_widget, &alloc );
3876         gtk_widget_show( m_widget );
3877         wxShowEvent eventShow(GetId(), true);
3878         eventShow.SetEventObject(this);
3879         HandleWindowEvent(eventShow);
3880         m_showOnIdle = false;
3881         return true;
3882     }
3883 
3884     return false;
3885 }
3886 
OnInternalIdle()3887 void wxWindowGTK::OnInternalIdle()
3888 {
3889     if ( gs_deferredFocusOut )
3890         gs_deferredFocusOut->GTKHandleDeferredFocusOut();
3891 
3892     // Check if we have to show window now
3893     if (GTKShowFromOnIdle()) return;
3894 
3895     if ( m_dirtyTabOrder )
3896     {
3897         m_dirtyTabOrder = false;
3898         RealizeTabOrder();
3899     }
3900 
3901     wxWindowBase::OnInternalIdle();
3902 }
3903 
DoGetSize(int * width,int * height) const3904 void wxWindowGTK::DoGetSize( int *width, int *height ) const
3905 {
3906     if (width) (*width) = m_width;
3907     if (height) (*height) = m_height;
3908 }
3909 
DoSetClientSize(int width,int height)3910 void wxWindowGTK::DoSetClientSize( int width, int height )
3911 {
3912     wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3913 
3914     const wxSize size = GetSize();
3915     const wxSize clientSize = GetClientSize();
3916     SetSize(width + (size.x - clientSize.x), height + (size.y - clientSize.y));
3917 }
3918 
DoGetClientSize(int * width,int * height) const3919 void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
3920 {
3921     wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3922 
3923     if (m_useCachedClientSize)
3924     {
3925         if (width)  *width  = m_clientWidth;
3926         if (height) *height = m_clientHeight;
3927         return;
3928     }
3929 
3930     int w = m_width;
3931     int h = m_height;
3932 
3933     if ( m_wxwindow )
3934     {
3935         // if window is scrollable, account for scrollbars
3936         if ( GTK_IS_SCROLLED_WINDOW(m_widget) )
3937         {
3938             GtkPolicyType policy[ScrollDir_Max];
3939             gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(m_widget),
3940                                            &policy[ScrollDir_Horz],
3941                                            &policy[ScrollDir_Vert]);
3942 
3943             // get scrollbar spacing the same way the GTK-private function
3944             // _gtk_scrolled_window_get_scrollbar_spacing() does it
3945             int scrollbar_spacing =
3946                 GTK_SCROLLED_WINDOW_GET_CLASS(m_widget)->scrollbar_spacing;
3947             if (scrollbar_spacing < 0)
3948             {
3949                 gtk_widget_style_get(
3950                     m_widget, "scrollbar-spacing", &scrollbar_spacing, NULL);
3951             }
3952 
3953             for ( int i = 0; i < ScrollDir_Max; i++ )
3954             {
3955                 // don't account for the scrollbars we don't have
3956                 GtkRange * const range = m_scrollBar[i];
3957                 if ( !range )
3958                     continue;
3959 
3960                 // nor for the ones we have but don't current show
3961                 switch ( policy[i] )
3962                 {
3963 #if GTK_CHECK_VERSION(3,16,0)
3964                     case GTK_POLICY_EXTERNAL:
3965 #endif
3966                     case GTK_POLICY_NEVER:
3967                         // never shown so doesn't take any place
3968                         continue;
3969 
3970                     case GTK_POLICY_ALWAYS:
3971                         // no checks necessary
3972                         break;
3973 
3974                     case GTK_POLICY_AUTOMATIC:
3975                         // may be shown or not, check
3976                         GtkAdjustment *adj = gtk_range_get_adjustment(range);
3977                         if (gtk_adjustment_get_upper(adj) <= gtk_adjustment_get_page_size(adj))
3978                             continue;
3979                 }
3980 
3981                 GtkRequisition req;
3982 #ifdef __WXGTK3__
3983                 GtkWidget* widget = GTK_WIDGET(range);
3984                 if (i == ScrollDir_Horz)
3985                 {
3986                     if (height)
3987                     {
3988                         gtk_widget_get_preferred_height(widget, NULL, &req.height);
3989                         h -= req.height + scrollbar_spacing;
3990                     }
3991                 }
3992                 else
3993                 {
3994                     if (width)
3995                     {
3996                         gtk_widget_get_preferred_width(widget, NULL, &req.width);
3997                         w -= req.width + scrollbar_spacing;
3998                     }
3999                 }
4000 #else // !__WXGTK3__
4001                 gtk_widget_size_request(GTK_WIDGET(range), &req);
4002                 if (i == ScrollDir_Horz)
4003                     h -= req.height + scrollbar_spacing;
4004                 else
4005                     w -= req.width + scrollbar_spacing;
4006 #endif // !__WXGTK3__
4007             }
4008         }
4009 
4010         const wxSize sizeBorders = DoGetBorderSize();
4011         w -= sizeBorders.x;
4012         h -= sizeBorders.y;
4013 
4014         if (w < 0)
4015             w = 0;
4016         if (h < 0)
4017             h = 0;
4018     }
4019 
4020     if (width) *width = w;
4021     if (height) *height = h;
4022 }
4023 
DoGetBorderSize() const4024 wxSize wxWindowGTK::DoGetBorderSize() const
4025 {
4026     if ( !m_wxwindow )
4027         return wxWindowBase::DoGetBorderSize();
4028 
4029     GtkBorder border;
4030     WX_PIZZA(m_wxwindow)->get_border(border);
4031     return wxSize(border.left + border.right, border.top + border.bottom);
4032 }
4033 
DoGetPosition(int * x,int * y) const4034 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
4035 {
4036     int dx = 0;
4037     int dy = 0;
4038     GtkWidget* parent = NULL;
4039     if (m_widget)
4040         parent = gtk_widget_get_parent(m_widget);
4041     if (WX_IS_PIZZA(parent))
4042     {
4043         wxPizza* pizza = WX_PIZZA(parent);
4044         dx = pizza->m_scroll_x;
4045         dy = pizza->m_scroll_y;
4046     }
4047     if (x) (*x) = m_x - dx;
4048     if (y) (*y) = m_y - dy;
4049 }
4050 
DoClientToScreen(int * x,int * y) const4051 void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
4052 {
4053     wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
4054 
4055     GtkWidget* widget = m_widget;
4056     if (m_wxwindow)
4057         widget = m_wxwindow;
4058     GdkWindow* source = gtk_widget_get_window(widget);
4059 
4060     if ((!m_isGtkPositionValid || source == NULL) && !IsTopLevel() && m_parent)
4061     {
4062         m_parent->DoClientToScreen(x, y);
4063         int xx, yy;
4064         DoGetPosition(&xx, &yy);
4065         if (m_wxwindow)
4066         {
4067             GtkBorder border;
4068             WX_PIZZA(m_wxwindow)->get_border(border);
4069             xx += border.left;
4070             yy += border.top;
4071         }
4072         if (y) *y += yy;
4073         if (x)
4074         {
4075             if (GetLayoutDirection() != wxLayout_RightToLeft)
4076                 *x += xx;
4077             else
4078             {
4079                 int w;
4080                 // undo RTL conversion done by parent
4081                 static_cast<wxWindowGTK*>(m_parent)->DoGetClientSize(&w, NULL);
4082                 *x = w - *x;
4083 
4084                 DoGetClientSize(&w, NULL);
4085                 *x += xx;
4086                 *x = w - *x;
4087             }
4088         }
4089         return;
4090     }
4091 
4092     if (source == NULL)
4093     {
4094         wxLogDebug("ClientToScreen cannot work when toplevel window is not shown");
4095         return;
4096     }
4097 
4098     int org_x = 0;
4099     int org_y = 0;
4100     gdk_window_get_origin( source, &org_x, &org_y );
4101 
4102     if (!m_wxwindow)
4103     {
4104         if (!gtk_widget_get_has_window(m_widget))
4105         {
4106             GtkAllocation a;
4107             gtk_widget_get_allocation(m_widget, &a);
4108             org_x += a.x;
4109             org_y += a.y;
4110         }
4111     }
4112 
4113 
4114     if (x)
4115     {
4116         if (GetLayoutDirection() == wxLayout_RightToLeft)
4117             *x = (GetClientSize().x - *x) + org_x;
4118         else
4119             *x += org_x;
4120     }
4121 
4122     if (y) *y += org_y;
4123 }
4124 
DoScreenToClient(int * x,int * y) const4125 void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
4126 {
4127     wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
4128 
4129     GtkWidget* widget = m_widget;
4130     if (m_wxwindow)
4131         widget = m_wxwindow;
4132     GdkWindow* source = gtk_widget_get_window(widget);
4133 
4134     if ((!m_isGtkPositionValid || source == NULL) && !IsTopLevel() && m_parent)
4135     {
4136         m_parent->DoScreenToClient(x, y);
4137         int xx, yy;
4138         DoGetPosition(&xx, &yy);
4139         if (m_wxwindow)
4140         {
4141             GtkBorder border;
4142             WX_PIZZA(m_wxwindow)->get_border(border);
4143             xx += border.left;
4144             yy += border.top;
4145         }
4146         if (y) *y -= yy;
4147         if (x)
4148         {
4149             if (GetLayoutDirection() != wxLayout_RightToLeft)
4150                 *x -= xx;
4151             else
4152             {
4153                 int w;
4154                 // undo RTL conversion done by parent
4155                 static_cast<wxWindowGTK*>(m_parent)->DoGetClientSize(&w, NULL);
4156                 *x = w - *x;
4157 
4158                 DoGetClientSize(&w, NULL);
4159                 *x -= xx;
4160                 *x = w - *x;
4161             }
4162         }
4163         return;
4164     }
4165 
4166     if (source == NULL)
4167     {
4168         wxLogDebug("ScreenToClient cannot work when toplevel window is not shown");
4169         return;
4170     }
4171 
4172     int org_x = 0;
4173     int org_y = 0;
4174     gdk_window_get_origin( source, &org_x, &org_y );
4175 
4176     if (!m_wxwindow)
4177     {
4178         if (!gtk_widget_get_has_window(m_widget))
4179         {
4180             GtkAllocation a;
4181             gtk_widget_get_allocation(m_widget, &a);
4182             org_x += a.x;
4183             org_y += a.y;
4184         }
4185     }
4186 
4187     if (x)
4188     {
4189         if (GetLayoutDirection() == wxLayout_RightToLeft)
4190             *x = (GetClientSize().x - *x) - org_x;
4191         else
4192             *x -= org_x;
4193     }
4194     if (y) *y -= org_y;
4195 }
4196 
Show(bool show)4197 bool wxWindowGTK::Show( bool show )
4198 {
4199     if ( !wxWindowBase::Show(show) )
4200     {
4201         // nothing to do
4202         return false;
4203     }
4204 
4205     // notice that we may call Hide() before the window is created and this is
4206     // actually useful to create it hidden initially -- but we can't call
4207     // Show() before it is created
4208     if ( !m_widget )
4209     {
4210         wxASSERT_MSG( !show, "can't show invalid window" );
4211         return true;
4212     }
4213 
4214     if ( show )
4215     {
4216         if ( m_showOnIdle )
4217         {
4218             // defer until later
4219             return true;
4220         }
4221 
4222         gtk_widget_show(m_widget);
4223     }
4224     else // hide
4225     {
4226         gtk_widget_hide(m_widget);
4227     }
4228 
4229     wxShowEvent eventShow(GetId(), show);
4230     eventShow.SetEventObject(this);
4231     HandleWindowEvent(eventShow);
4232 
4233     return true;
4234 }
4235 
IsShown() const4236 bool wxWindowGTK::IsShown() const
4237 {
4238     // return false for non-selected wxNotebook pages
4239     return m_isShown && (m_widget == NULL || gtk_widget_get_child_visible(m_widget));
4240 }
4241 
DoEnable(bool enable)4242 void wxWindowGTK::DoEnable( bool enable )
4243 {
4244     if ( !m_widget )
4245     {
4246         // The window can be disabled before being created, so just don't do
4247         // anything in this case and, in particular, don't assert.
4248         return;
4249     }
4250 
4251     gtk_widget_set_sensitive( m_widget, enable );
4252     if (m_wxwindow && (m_wxwindow != m_widget))
4253         gtk_widget_set_sensitive( m_wxwindow, enable );
4254 
4255     if (enable && AcceptsFocusFromKeyboard())
4256     {
4257         wxWindowGTK* parent = this;
4258         while ((parent = parent->GetParent()))
4259         {
4260             parent->m_dirtyTabOrder = true;
4261             if (parent->IsTopLevel())
4262                 break;
4263         }
4264         wxTheApp->WakeUpIdle();
4265     }
4266 }
4267 
GetCharHeight() const4268 int wxWindowGTK::GetCharHeight() const
4269 {
4270     wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
4271 
4272     wxFont font = GetFont();
4273     wxCHECK_MSG( font.IsOk(), 12, wxT("invalid font") );
4274 
4275     PangoContext* context = gtk_widget_get_pango_context(m_widget);
4276 
4277     if (!context)
4278         return 0;
4279 
4280     PangoFontDescription *desc = font.GetNativeFontInfo()->description;
4281     PangoLayout *layout = pango_layout_new(context);
4282     pango_layout_set_font_description(layout, desc);
4283     pango_layout_set_text(layout, "H", 1);
4284     PangoLayoutLine* line;
4285 #if PANGO_VERSION_CHECK(1,16,0)
4286     if ( wx_pango_version_check(1,16,0) == NULL )
4287     {
4288         line = pango_layout_get_line_readonly(layout, 0);
4289     }
4290     else
4291 #endif // Pango 1.16+
4292     {
4293         line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
4294     }
4295 
4296     PangoRectangle rect;
4297     pango_layout_line_get_extents(line, NULL, &rect);
4298 
4299     g_object_unref (layout);
4300 
4301     return (int) PANGO_PIXELS(rect.height);
4302 }
4303 
GetCharWidth() const4304 int wxWindowGTK::GetCharWidth() const
4305 {
4306     wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
4307 
4308     wxFont font = GetFont();
4309     wxCHECK_MSG( font.IsOk(), 8, wxT("invalid font") );
4310 
4311     PangoContext* context = gtk_widget_get_pango_context(m_widget);
4312 
4313     if (!context)
4314         return 0;
4315 
4316     PangoFontDescription *desc = font.GetNativeFontInfo()->description;
4317     PangoLayout *layout = pango_layout_new(context);
4318     pango_layout_set_font_description(layout, desc);
4319     pango_layout_set_text(layout, "g", 1);
4320     PangoLayoutLine* line;
4321 #if PANGO_VERSION_CHECK(1,16,0)
4322     if ( wx_pango_version_check(1,16,0) == NULL )
4323     {
4324         line = pango_layout_get_line_readonly(layout, 0);
4325     }
4326     else
4327 #endif // Pango 1.16+
4328     {
4329         line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
4330     }
4331 
4332     PangoRectangle rect;
4333     pango_layout_line_get_extents(line, NULL, &rect);
4334 
4335     g_object_unref (layout);
4336 
4337     return (int) PANGO_PIXELS(rect.width);
4338 }
4339 
DoGetTextExtent(const wxString & string,int * x,int * y,int * descent,int * externalLeading,const wxFont * theFont) const4340 void wxWindowGTK::DoGetTextExtent( const wxString& string,
4341                                    int *x,
4342                                    int *y,
4343                                    int *descent,
4344                                    int *externalLeading,
4345                                    const wxFont *theFont ) const
4346 {
4347     // ensure we work with a valid font
4348     wxFont fontToUse;
4349     if ( !theFont || !theFont->IsOk() )
4350         fontToUse = GetFont();
4351     else
4352         fontToUse = *theFont;
4353 
4354     wxCHECK_RET( fontToUse.IsOk(), wxT("invalid font") );
4355 
4356     const wxWindow* win = static_cast<const wxWindow*>(this);
4357     wxTextMeasure txm(win, &fontToUse);
4358     txm.GetTextExtent(string, x, y, descent, externalLeading);
4359 }
4360 
GetContentScaleFactor() const4361 double wxWindowGTK::GetContentScaleFactor() const
4362 {
4363     double scaleFactor = 1;
4364 #if GTK_CHECK_VERSION(3,10,0)
4365     if (m_widget && gtk_check_version(3,10,0) == NULL)
4366     {
4367         scaleFactor = gtk_widget_get_scale_factor(m_widget);
4368     }
4369 #endif
4370     return scaleFactor;
4371 }
4372 
GetDPIScaleFactor() const4373 double wxWindowGTK::GetDPIScaleFactor() const
4374 {
4375     // Under GTK 3 DPI scale factor is the same as content scale factor, while
4376     // under GTK 2 both are always 1, so they're still the same.
4377     return GetContentScaleFactor();
4378 }
4379 
GTKDisableFocusOutEvent()4380 void wxWindowGTK::GTKDisableFocusOutEvent()
4381 {
4382     g_signal_handlers_block_by_func( m_focusWidget,
4383                                 (gpointer) gtk_window_focus_out_callback, this);
4384 }
4385 
GTKEnableFocusOutEvent()4386 void wxWindowGTK::GTKEnableFocusOutEvent()
4387 {
4388     g_signal_handlers_unblock_by_func( m_focusWidget,
4389                                 (gpointer) gtk_window_focus_out_callback, this);
4390 }
4391 
GTKHandleFocusIn()4392 bool wxWindowGTK::GTKHandleFocusIn()
4393 {
4394     // Disable default focus handling for custom windows since the default GTK+
4395     // handler issues a repaint
4396     const bool retval = m_wxwindow ? true : false;
4397 
4398 
4399     // NB: if there's still unprocessed deferred focus-out event (see
4400     //     GTKHandleFocusOut() for explanation), we need to process it first so
4401     //     that the order of focus events -- focus-out first, then focus-in
4402     //     elsewhere -- is preserved
4403     if ( gs_deferredFocusOut )
4404     {
4405         if ( GTKNeedsToFilterSameWindowFocus() &&
4406              gs_deferredFocusOut == this )
4407         {
4408             // GTK+ focus changed from this wxWindow back to itself, so don't
4409             // emit any events at all
4410             wxLogTrace(TRACE_FOCUS,
4411                        "filtered out spurious focus change within %s",
4412                        wxDumpWindow(this));
4413             gs_deferredFocusOut = NULL;
4414             return retval;
4415         }
4416 
4417         // otherwise we need to send focus-out first
4418         wxASSERT_MSG ( gs_deferredFocusOut != this,
4419                        "GTKHandleFocusIn(GTKFocus_Normal) called even though focus changed back to itself - derived class should handle this" );
4420         gs_deferredFocusOut->GTKHandleDeferredFocusOut();
4421     }
4422 
4423 
4424     wxLogTrace(TRACE_FOCUS,
4425                "handling focus_in event for %s",
4426                wxDumpWindow(this));
4427 
4428     if (m_imContext)
4429         gtk_im_context_focus_in(m_imContext);
4430 
4431     gs_currentFocus = this;
4432 
4433     if ( gs_pendingFocus )
4434     {
4435         wxLogTrace(TRACE_FOCUS, "Resetting pending focus %s on focus set",
4436                    wxDumpWindow(gs_pendingFocus));
4437         gs_pendingFocus = NULL;
4438     }
4439 
4440 #if wxUSE_CARET
4441     // caret needs to be informed about focus change
4442     wxCaret *caret = GetCaret();
4443     if ( caret )
4444     {
4445         caret->OnSetFocus();
4446     }
4447 #endif // wxUSE_CARET
4448 
4449     // Notify the parent keeping track of focus for the kbd navigation
4450     // purposes that we got it.
4451     wxChildFocusEvent eventChildFocus(static_cast<wxWindow*>(this));
4452     GTKProcessEvent(eventChildFocus);
4453 
4454     wxFocusEvent eventFocus(wxEVT_SET_FOCUS, GetId());
4455     eventFocus.SetEventObject(this);
4456     eventFocus.SetWindow(static_cast<wxWindow*>(gs_lastFocus));
4457     gs_lastFocus = this;
4458 
4459     GTKProcessEvent(eventFocus);
4460 
4461     return retval;
4462 }
4463 
GTKHandleFocusOut()4464 bool wxWindowGTK::GTKHandleFocusOut()
4465 {
4466     // Disable default focus handling for custom windows since the default GTK+
4467     // handler issues a repaint
4468     const bool retval = m_wxwindow ? true : false;
4469 
4470     // If this window is still the pending focus one, reset that pointer as
4471     // we're not going to have focus any longer and DoFindFocus() must not
4472     // return this window.
4473     if ( gs_pendingFocus == this )
4474     {
4475         wxLogTrace(TRACE_FOCUS, "Resetting pending focus %s on focus loss",
4476                    wxDumpWindow(this));
4477         gs_pendingFocus = NULL;
4478     }
4479 
4480     // NB: If a control is composed of several GtkWidgets and when focus
4481     //     changes from one of them to another within the same wxWindow, we get
4482     //     a focus-out event followed by focus-in for another GtkWidget owned
4483     //     by the same wx control. We don't want to generate two spurious
4484     //     wxEVT_SET_FOCUS events in this case, so we defer sending wx events
4485     //     from GTKHandleFocusOut() until we know for sure it's not coming back
4486     //     (i.e. in GTKHandleFocusIn() or at idle time).
4487     if ( GTKNeedsToFilterSameWindowFocus() )
4488     {
4489         wxASSERT_MSG( gs_deferredFocusOut == NULL,
4490                       "deferred focus out event already pending" );
4491         wxLogTrace(TRACE_FOCUS,
4492                    "deferring focus_out event for %s",
4493                    wxDumpWindow(this));
4494         gs_deferredFocusOut = this;
4495         return retval;
4496     }
4497 
4498     GTKHandleFocusOutNoDeferring();
4499 
4500     return retval;
4501 }
4502 
GTKHandleFocusOutNoDeferring()4503 void wxWindowGTK::GTKHandleFocusOutNoDeferring()
4504 {
4505     wxLogTrace(TRACE_FOCUS,
4506                "handling focus_out event for %s",
4507                wxDumpWindow(this));
4508 
4509     gs_lastFocus = this;
4510 
4511     if (m_imContext)
4512         gtk_im_context_focus_out(m_imContext);
4513 
4514     if ( gs_currentFocus != this )
4515     {
4516         // Something is terribly wrong, gs_currentFocus is out of sync with the
4517         // real focus. We will reset it to NULL anyway, because after this
4518         // focus-out event is handled, one of the following with happen:
4519         //
4520         // * either focus will go out of the app altogether, in which case
4521         //   gs_currentFocus _should_ be NULL
4522         //
4523         // * or it goes to another control, in which case focus-in event will
4524         //   follow immediately and it will set gs_currentFocus to the right
4525         //   value
4526         wxLogDebug("window %s lost focus even though it didn't have it",
4527                    wxDumpWindow(this));
4528     }
4529     gs_currentFocus = NULL;
4530 
4531 #if wxUSE_CARET
4532     // caret needs to be informed about focus change
4533     wxCaret *caret = GetCaret();
4534     if ( caret )
4535     {
4536         caret->OnKillFocus();
4537     }
4538 #endif // wxUSE_CARET
4539 
4540     wxFocusEvent event( wxEVT_KILL_FOCUS, GetId() );
4541     event.SetEventObject( this );
4542     event.SetWindow( FindFocus() );
4543     GTKProcessEvent( event );
4544 }
4545 
GTKHandleDeferredFocusOut()4546 void wxWindowGTK::GTKHandleDeferredFocusOut()
4547 {
4548     // NB: See GTKHandleFocusOut() for explanation. This function is called
4549     //     from either GTKHandleFocusIn() or OnInternalIdle() to process
4550     //     deferred event for this window.
4551     gs_deferredFocusOut = NULL;
4552 
4553     wxLogTrace(TRACE_FOCUS,
4554                "processing deferred focus_out event for %s",
4555                wxDumpWindow(this));
4556 
4557     GTKHandleFocusOutNoDeferring();
4558 }
4559 
SetFocus()4560 void wxWindowGTK::SetFocus()
4561 {
4562     wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4563 
4564     // Setting "physical" focus is not immediate in GTK+ and while
4565     // gtk_widget_is_focus ("determines if the widget is the focus widget
4566     // within its toplevel", i.e. returns true for one widget per TLW, not
4567     // globally) returns true immediately after grabbing focus,
4568     // GTK_WIDGET_HAS_FOCUS (which returns true only for the one widget that
4569     // has focus at the moment) takes effect only after the window is shown
4570     // (if it was hidden at the moment of the call) or at the next event loop
4571     // iteration.
4572     //
4573     // Because we want to FindFocus() call immediately following
4574     // foo->SetFocus() to return foo, we have to keep track of "pending" focus
4575     // ourselves.
4576     gs_pendingFocus = NULL;
4577     if (gs_currentFocus != this)
4578         gs_pendingFocus = this;
4579 
4580     // Toplevel must be active for child to actually receive focus.
4581     // But avoid activating if tlw is not yet shown, as that will
4582     // cause it to be immediately shown.
4583     GtkWidget* tlw = gtk_widget_get_ancestor(m_widget, GTK_TYPE_WINDOW);
4584     if (tlw && gtk_widget_get_visible(tlw) && !gtk_window_is_active(GTK_WINDOW(tlw)))
4585         gtk_window_present(GTK_WINDOW(tlw));
4586 
4587     GtkWidget *widget = m_wxwindow ? m_wxwindow : m_focusWidget;
4588 
4589     if ( GTK_IS_CONTAINER(widget) &&
4590          !gtk_widget_get_can_focus(widget) )
4591     {
4592         wxLogTrace(TRACE_FOCUS,
4593                    wxT("Setting focus to a child of %s"),
4594                    wxDumpWindow(this));
4595         gtk_widget_child_focus(widget, GTK_DIR_TAB_FORWARD);
4596     }
4597     else
4598     {
4599         wxLogTrace(TRACE_FOCUS,
4600                    wxT("Setting focus to %s"),
4601                    wxDumpWindow(this));
4602         gtk_widget_grab_focus(widget);
4603     }
4604 }
4605 
SetCanFocus(bool canFocus)4606 void wxWindowGTK::SetCanFocus(bool canFocus)
4607 {
4608     wxCHECK_RET(m_widget, "invalid window");
4609 
4610     gtk_widget_set_can_focus(m_widget, canFocus);
4611 
4612     if ( m_wxwindow && (m_widget != m_wxwindow) )
4613     {
4614         gtk_widget_set_can_focus(m_wxwindow, canFocus);
4615     }
4616 }
4617 
Reparent(wxWindowBase * newParentBase)4618 bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
4619 {
4620     wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
4621 
4622     wxWindowGTK * const newParent = (wxWindowGTK *)newParentBase;
4623 
4624     wxASSERT( GTK_IS_WIDGET(m_widget) );
4625 
4626     if ( !wxWindowBase::Reparent(newParent) )
4627         return false;
4628 
4629     wxASSERT( GTK_IS_WIDGET(m_widget) );
4630 
4631     // Notice that old m_parent pointer might be non-NULL here but the widget
4632     // still not have any parent at GTK level if it's a notebook page that had
4633     // been removed from the notebook so test this at GTK level and not wx one.
4634     if ( GtkWidget *parentGTK = gtk_widget_get_parent(m_widget) )
4635         gtk_container_remove(GTK_CONTAINER(parentGTK), m_widget);
4636 
4637     wxASSERT( GTK_IS_WIDGET(m_widget) );
4638 
4639     if (newParent)
4640     {
4641         if (gtk_widget_get_visible (newParent->m_widget))
4642         {
4643             m_showOnIdle = true;
4644             gtk_widget_hide( m_widget );
4645         }
4646         /* insert GTK representation */
4647         newParent->AddChildGTK(this);
4648     }
4649 
4650     SetLayoutDirection(wxLayout_Default);
4651 
4652     return true;
4653 }
4654 
DoAddChild(wxWindowGTK * child)4655 void wxWindowGTK::DoAddChild(wxWindowGTK *child)
4656 {
4657     wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
4658     wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
4659 
4660     /* add to list */
4661     AddChild( child );
4662 
4663     /* insert GTK representation */
4664     AddChildGTK(child);
4665 }
4666 
AddChild(wxWindowBase * child)4667 void wxWindowGTK::AddChild(wxWindowBase *child)
4668 {
4669     wxWindowBase::AddChild(child);
4670     m_dirtyTabOrder = true;
4671     wxTheApp->WakeUpIdle();
4672 }
4673 
RemoveChild(wxWindowBase * child)4674 void wxWindowGTK::RemoveChild(wxWindowBase *child)
4675 {
4676     wxWindowBase::RemoveChild(child);
4677     m_dirtyTabOrder = true;
4678     wxTheApp->WakeUpIdle();
4679 }
4680 
4681 /* static */
GTKGetLayout(GtkWidget * widget)4682 wxLayoutDirection wxWindowGTK::GTKGetLayout(GtkWidget *widget)
4683 {
4684     return gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL
4685                 ? wxLayout_RightToLeft
4686                 : wxLayout_LeftToRight;
4687 }
4688 
4689 /* static */
GTKSetLayout(GtkWidget * widget,wxLayoutDirection dir)4690 void wxWindowGTK::GTKSetLayout(GtkWidget *widget, wxLayoutDirection dir)
4691 {
4692     wxASSERT_MSG( dir != wxLayout_Default, wxT("invalid layout direction") );
4693 
4694     gtk_widget_set_direction(widget,
4695                              dir == wxLayout_RightToLeft ? GTK_TEXT_DIR_RTL
4696                                                          : GTK_TEXT_DIR_LTR);
4697 }
4698 
GetLayoutDirection() const4699 wxLayoutDirection wxWindowGTK::GetLayoutDirection() const
4700 {
4701     return GTKGetLayout(m_widget);
4702 }
4703 
SetLayoutDirection(wxLayoutDirection dir)4704 void wxWindowGTK::SetLayoutDirection(wxLayoutDirection dir)
4705 {
4706     if ( dir == wxLayout_Default )
4707     {
4708         const wxWindow *const parent = GetParent();
4709         if ( parent )
4710         {
4711             // inherit layout from parent.
4712             dir = parent->GetLayoutDirection();
4713         }
4714         else // no parent, use global default layout
4715         {
4716             dir = wxTheApp->GetLayoutDirection();
4717         }
4718     }
4719 
4720     if ( dir == wxLayout_Default )
4721         return;
4722 
4723     GTKSetLayout(m_widget, dir);
4724 
4725     if (GtkRange* range = m_scrollBar[ScrollDir_Horz])
4726         gtk_range_set_inverted(range, dir == wxLayout_RightToLeft);
4727 
4728     if (m_wxwindow && (m_wxwindow != m_widget))
4729         GTKSetLayout(m_wxwindow, dir);
4730 }
4731 
4732 wxCoord
AdjustForLayoutDirection(wxCoord x,wxCoord WXUNUSED (width),wxCoord WXUNUSED (widthTotal)) const4733 wxWindowGTK::AdjustForLayoutDirection(wxCoord x,
4734                                       wxCoord WXUNUSED(width),
4735                                       wxCoord WXUNUSED(widthTotal)) const
4736 {
4737     // We now mirror the coordinates of RTL windows in wxPizza
4738     return x;
4739 }
4740 
DoMoveInTabOrder(wxWindow * win,WindowOrder move)4741 void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, WindowOrder move)
4742 {
4743     wxWindowBase::DoMoveInTabOrder(win, move);
4744 
4745     // Update the TAB order at GTK+ level too, but do it slightly later in case
4746     // we're changing the TAB order of several controls at once, as is common.
4747     wxWindow* const parent = GetParent();
4748     if ( parent )
4749     {
4750         parent->m_dirtyTabOrder = true;
4751         wxTheApp->WakeUpIdle();
4752     }
4753 }
4754 
DoNavigateIn(int flags)4755 bool wxWindowGTK::DoNavigateIn(int flags)
4756 {
4757     wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
4758     wxCHECK_MSG( parent, false, wxT("every window must have a TLW parent") );
4759 
4760     GtkDirectionType dir;
4761     dir = flags & wxNavigationKeyEvent::IsForward ? GTK_DIR_TAB_FORWARD
4762                                                   : GTK_DIR_TAB_BACKWARD;
4763 
4764     gboolean rc;
4765     g_signal_emit_by_name(parent->m_widget, "focus", dir, &rc);
4766 
4767     return rc != 0;
4768 }
4769 
GTKWidgetNeedsMnemonic() const4770 bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
4771 {
4772     // none needed by default
4773     return false;
4774 }
4775 
GTKWidgetDoSetMnemonic(GtkWidget * WXUNUSED (w))4776 void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget* WXUNUSED(w))
4777 {
4778     // nothing to do by default since none is needed
4779 }
4780 
RealizeTabOrder()4781 void wxWindowGTK::RealizeTabOrder()
4782 {
4783     if (m_wxwindow)
4784     {
4785         if ( !m_children.empty() )
4786         {
4787             // we don't only construct the correct focus chain but also use
4788             // this opportunity to update the mnemonic widgets for the widgets
4789             // that need them
4790 
4791             GList *chain = NULL;
4792             wxWindowGTK* mnemonicWindow = NULL;
4793 
4794             for ( wxWindowList::const_iterator i = m_children.begin();
4795                   i != m_children.end();
4796                   ++i )
4797             {
4798                 wxWindowGTK *win = *i;
4799 
4800                 bool focusableFromKeyboard = win->AcceptsFocusFromKeyboard();
4801 
4802                 if ( mnemonicWindow )
4803                 {
4804                     if ( focusableFromKeyboard )
4805                     {
4806                         // We may need to focus on the connect widget if the
4807                         // main one isn't focusable, but note that we still use
4808                         // the main widget if neither it nor connect widget is
4809                         // focusable, without this using a wxStaticText before
4810                         // wxChoice wouldn't work at all, for example.
4811                         GtkWidget* w = win->m_widget;
4812                         if ( !gtk_widget_get_can_focus(w) )
4813                         {
4814                             GtkWidget* const cw = win->GetConnectWidget();
4815                             if ( cw != w && gtk_widget_get_can_focus(cw) )
4816                                 w = cw;
4817                         }
4818 
4819                         mnemonicWindow->GTKWidgetDoSetMnemonic(w);
4820                         mnemonicWindow = NULL;
4821                     }
4822                 }
4823 
4824                 if ( win->GTKWidgetNeedsMnemonic() )
4825                 {
4826                     mnemonicWindow = win;
4827                 }
4828 
4829                 if ( focusableFromKeyboard )
4830                     chain = g_list_prepend(chain, win->m_widget);
4831             }
4832 
4833             chain = g_list_reverse(chain);
4834 
4835             gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain);
4836             g_list_free(chain);
4837         }
4838         else // no children
4839         {
4840             gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow));
4841         }
4842     }
4843 }
4844 
Raise()4845 void wxWindowGTK::Raise()
4846 {
4847     wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
4848 
4849     if (m_wxwindow && gtk_widget_get_window(m_wxwindow))
4850     {
4851         gdk_window_raise(gtk_widget_get_window(m_wxwindow));
4852     }
4853     else if (gtk_widget_get_window(m_widget))
4854     {
4855         gdk_window_raise(gtk_widget_get_window(m_widget));
4856     }
4857 }
4858 
Lower()4859 void wxWindowGTK::Lower()
4860 {
4861     wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
4862 
4863     if (m_wxwindow && gtk_widget_get_window(m_wxwindow))
4864     {
4865         gdk_window_lower(gtk_widget_get_window(m_wxwindow));
4866     }
4867     else if (gtk_widget_get_window(m_widget))
4868     {
4869         gdk_window_lower(gtk_widget_get_window(m_widget));
4870     }
4871 }
4872 
SetCursor(const wxCursor & cursor)4873 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
4874 {
4875     if (!wxWindowBase::SetCursor(cursor))
4876         return false;
4877 
4878     GTKUpdateCursor();
4879 
4880     return true;
4881 }
4882 
GTKUpdateCursor(bool isBusyOrGlobalCursor,bool isRealize,const wxCursor * overrideCursor)4883 void wxWindowGTK::GTKUpdateCursor(bool isBusyOrGlobalCursor, bool isRealize, const wxCursor* overrideCursor)
4884 {
4885     m_needCursorReset = false;
4886 
4887     if (m_widget == NULL || !gtk_widget_get_realized(m_widget))
4888         return;
4889 
4890     // if we don't already know there is a busy/global cursor, we have to check for one
4891     if (!isBusyOrGlobalCursor)
4892     {
4893         if (g_globalCursor.IsOk())
4894             isBusyOrGlobalCursor = true;
4895         else if (wxIsBusy())
4896         {
4897             wxWindow* win = wxGetTopLevelParent(static_cast<wxWindow*>(this));
4898             if (win && win->m_widget && !gtk_window_get_modal(GTK_WINDOW(win->m_widget)))
4899                 isBusyOrGlobalCursor = true;
4900         }
4901     }
4902     GdkCursor* cursor = NULL;
4903     if (!isBusyOrGlobalCursor)
4904         cursor = (overrideCursor ? *overrideCursor : m_cursor).GetCursor();
4905 
4906     GdkWindow* window = NULL;
4907     if (cursor || isBusyOrGlobalCursor || !isRealize)
4908     {
4909         wxArrayGdkWindows windows;
4910         window = GTKGetWindow(windows);
4911         if (window)
4912             gdk_window_set_cursor(window, cursor);
4913         else
4914         {
4915             for (size_t i = windows.size(); i--;)
4916             {
4917                 window = windows[i];
4918                 if (window)
4919                     gdk_window_set_cursor(window, cursor);
4920             }
4921         }
4922     }
4923     if (window && cursor == NULL && m_wxwindow == NULL && !isBusyOrGlobalCursor && !isRealize)
4924     {
4925         void* data;
4926         gdk_window_get_user_data(window, &data);
4927         if (data)
4928         {
4929 #ifdef __WXGTK3__
4930             const char sig_name[] = "state-flags-changed";
4931             GtkStateFlags state = gtk_widget_get_state_flags(GTK_WIDGET(data));
4932 #else
4933             const char sig_name[] = "state-changed";
4934             GtkStateType state = gtk_widget_get_state(GTK_WIDGET(data));
4935 #endif
4936             static unsigned sig_id = g_signal_lookup(sig_name, GTK_TYPE_WIDGET);
4937 
4938             // encourage native widget to restore any non-default cursors
4939             g_signal_emit(data, sig_id, 0, state);
4940         }
4941     }
4942 }
4943 
WarpPointer(int x,int y)4944 void wxWindowGTK::WarpPointer( int x, int y )
4945 {
4946     wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
4947 
4948     ClientToScreen(&x, &y);
4949     GdkDisplay* display = gtk_widget_get_display(m_widget);
4950     GdkScreen* screen = gtk_widget_get_screen(m_widget);
4951 #ifdef __WXGTK3__
4952 #ifdef __WXGTK4__
4953     GdkSeat* seat = gdk_display_get_default_seat(display);
4954     GdkDevice* device = gdk_seat_get_pointer(seat);
4955 #else
4956     wxGCC_WARNING_SUPPRESS(deprecated-declarations)
4957     GdkDeviceManager* manager = gdk_display_get_device_manager(display);
4958     GdkDevice* device = gdk_device_manager_get_client_pointer(manager);
4959     wxGCC_WARNING_RESTORE()
4960 #endif
4961     gdk_device_warp(device, screen, x, y);
4962 #else
4963 #ifdef GDK_WINDOWING_X11
4964     XWarpPointer(GDK_DISPLAY_XDISPLAY(display),
4965         None,
4966         GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
4967         0, 0, 0, 0, x, y);
4968 #endif
4969 #endif
4970 }
4971 
ScrollDirFromRange(GtkRange * range) const4972 wxWindowGTK::ScrollDir wxWindowGTK::ScrollDirFromRange(GtkRange *range) const
4973 {
4974     // find the scrollbar which generated the event
4975     for ( int dir = 0; dir < ScrollDir_Max; dir++ )
4976     {
4977         if ( range == m_scrollBar[dir] )
4978             return (ScrollDir)dir;
4979     }
4980 
4981     wxFAIL_MSG( wxT("event from unknown scrollbar received") );
4982 
4983     return ScrollDir_Max;
4984 }
4985 
DoScrollByUnits(ScrollDir dir,ScrollUnit unit,int units)4986 bool wxWindowGTK::DoScrollByUnits(ScrollDir dir, ScrollUnit unit, int units)
4987 {
4988     bool changed = false;
4989     GtkRange* range = m_scrollBar[dir];
4990     if ( range && units )
4991     {
4992         GtkAdjustment* adj = gtk_range_get_adjustment(range);
4993         double inc = unit == ScrollUnit_Line ? gtk_adjustment_get_step_increment(adj)
4994                                              : gtk_adjustment_get_page_increment(adj);
4995 
4996         const int posOld = wxRound(gtk_adjustment_get_value(adj));
4997         gtk_range_set_value(range, posOld + units*inc);
4998 
4999         changed = wxRound(gtk_adjustment_get_value(adj)) != posOld;
5000     }
5001 
5002     return changed;
5003 }
5004 
ScrollLines(int lines)5005 bool wxWindowGTK::ScrollLines(int lines)
5006 {
5007     return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Line, lines);
5008 }
5009 
ScrollPages(int pages)5010 bool wxWindowGTK::ScrollPages(int pages)
5011 {
5012     return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Page, pages);
5013 }
5014 
Refresh(bool WXUNUSED (eraseBackground),const wxRect * rect)5015 void wxWindowGTK::Refresh(bool WXUNUSED(eraseBackground),
5016                           const wxRect *rect)
5017 {
5018     if (m_wxwindow)
5019     {
5020         if (gtk_widget_get_mapped(m_wxwindow))
5021         {
5022             GdkWindow* window = gtk_widget_get_window(m_wxwindow);
5023             if (rect)
5024             {
5025                 GdkRectangle r = { rect->x, rect->y, rect->width, rect->height };
5026                 if (GetLayoutDirection() == wxLayout_RightToLeft)
5027                     r.x = gdk_window_get_width(window) - r.x - rect->width;
5028                 gdk_window_invalidate_rect(window, &r, true);
5029             }
5030             else
5031                 gdk_window_invalidate_rect(window, NULL, true);
5032         }
5033     }
5034     else if (m_widget)
5035     {
5036         if (gtk_widget_get_mapped(m_widget))
5037         {
5038             if (rect)
5039                 gtk_widget_queue_draw_area(m_widget, rect->x, rect->y, rect->width, rect->height);
5040             else
5041                 gtk_widget_queue_draw(m_widget);
5042         }
5043     }
5044 }
5045 
Update()5046 void wxWindowGTK::Update()
5047 {
5048     if (m_widget && gtk_widget_get_mapped(m_widget) && m_width > 0 && m_height > 0)
5049     {
5050         GdkDisplay* display = gtk_widget_get_display(m_widget);
5051         // Flush everything out to the server, and wait for it to finish.
5052         // This ensures nothing will overwrite the drawing we are about to do.
5053         gdk_display_sync(display);
5054 
5055         GdkWindow* window = GTKGetDrawingWindow();
5056         if (window == NULL)
5057             window = gtk_widget_get_window(m_widget);
5058         gdk_window_process_updates(window, true);
5059 
5060         // Flush again, but no need to wait for it to finish
5061         gdk_display_flush(display);
5062     }
5063 }
5064 
DoIsExposed(int x,int y) const5065 bool wxWindowGTK::DoIsExposed( int x, int y ) const
5066 {
5067     return m_updateRegion.Contains(x, y) != wxOutRegion;
5068 }
5069 
DoIsExposed(int x,int y,int w,int h) const5070 bool wxWindowGTK::DoIsExposed( int x, int y, int w, int h ) const
5071 {
5072 #ifndef __WXGTK3__
5073     if (GetLayoutDirection() == wxLayout_RightToLeft)
5074         return m_updateRegion.Contains(x-w, y, w, h) != wxOutRegion;
5075 #endif
5076 
5077     return m_updateRegion.Contains(x, y, w, h) != wxOutRegion;
5078 }
5079 
5080 #ifdef __WXGTK3__
GTKSendPaintEvents(cairo_t * cr)5081 void wxWindowGTK::GTKSendPaintEvents(cairo_t* cr)
5082 #else
5083 void wxWindowGTK::GTKSendPaintEvents(const GdkRegion* region)
5084 #endif
5085 {
5086 #ifdef __WXGTK3__
5087     {
5088         cairo_region_t* region = gdk_window_get_clip_region(gtk_widget_get_window(m_wxwindow));
5089         cairo_rectangle_int_t rect;
5090         cairo_region_get_extents(region, &rect);
5091         cairo_region_destroy(region);
5092         cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height);
5093         cairo_clip(cr);
5094     }
5095     if (GetLayoutDirection() == wxLayout_RightToLeft)
5096     {
5097         // wxDC is mirrored for RTL
5098         const int w = gdk_window_get_width(gtk_widget_get_window(m_wxwindow));
5099         cairo_translate(cr, w, 0);
5100         cairo_scale(cr, -1, 1);
5101     }
5102     double x1, y1, x2, y2;
5103     cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
5104 
5105     if (x1 >= x2 || y1 >= y2)
5106         return;
5107 
5108     m_paintContext = cr;
5109     m_updateRegion = wxRegion(int(x1), int(y1), int(x2 - x1), int(y2 - y1));
5110 #else // !__WXGTK3__
5111     m_updateRegion = wxRegion(region);
5112 #if wxGTK_HAS_COMPOSITING_SUPPORT
5113     cairo_t* cr = NULL;
5114 #endif
5115 #endif // !__WXGTK3__
5116     // Clip to paint region in wxClientDC
5117     m_clipPaintRegion = true;
5118 
5119     m_nativeUpdateRegion = m_updateRegion;
5120 
5121 #ifndef __WXGTK3__
5122     if (GetLayoutDirection() == wxLayout_RightToLeft)
5123     {
5124         // Transform m_updateRegion under RTL
5125         m_updateRegion.Clear();
5126 
5127         const int width = gdk_window_get_width(GTKGetDrawingWindow());
5128 
5129         wxRegionIterator upd( m_nativeUpdateRegion );
5130         while (upd)
5131         {
5132             wxRect rect;
5133             rect.x = upd.GetX();
5134             rect.y = upd.GetY();
5135             rect.width = upd.GetWidth();
5136             rect.height = upd.GetHeight();
5137 
5138             rect.x = width - rect.x - rect.width;
5139             m_updateRegion.Union( rect );
5140 
5141             ++upd;
5142         }
5143     }
5144 #endif
5145 
5146     switch ( GetBackgroundStyle() )
5147     {
5148         case wxBG_STYLE_TRANSPARENT:
5149 #if wxGTK_HAS_COMPOSITING_SUPPORT
5150             if (IsTransparentBackgroundSupported())
5151             {
5152                 // Set a transparent background, so that overlaying in parent
5153                 // might indeed let see through where this child did not
5154                 // explicitly paint.
5155                 // NB: it works also for top level windows (but this is the
5156                 // windows manager which then does the compositing job)
5157 #ifndef __WXGTK3__
5158                 cr = gdk_cairo_create(m_wxwindow->window);
5159                 gdk_cairo_region(cr, m_nativeUpdateRegion.GetRegion());
5160                 cairo_clip(cr);
5161 #endif
5162                 cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
5163                 cairo_paint(cr);
5164                 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
5165 #ifndef __WXGTK3__
5166                 cairo_surface_flush(cairo_get_target(cr));
5167 #endif
5168             }
5169 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
5170             break;
5171 
5172         case wxBG_STYLE_ERASE:
5173             {
5174 #ifdef __WXGTK3__
5175                 wxGTKCairoDC dc(cr, static_cast<wxWindow*>(this), GetLayoutDirection());
5176 #else
5177                 wxWindowDC dc( (wxWindow*)this );
5178                 dc.SetDeviceClippingRegion( m_updateRegion );
5179 
5180                 // Work around gtk-qt <= 0.60 bug whereby the window colour
5181                 // remains grey
5182                 if ( UseBgCol() &&
5183                         wxSystemOptions::
5184                             GetOptionInt("gtk.window.force-background-colour") )
5185                 {
5186                     dc.SetBackground(GetBackgroundColour());
5187                     dc.Clear();
5188                 }
5189 #endif // !__WXGTK3__
5190                 wxEraseEvent erase_event( GetId(), &dc );
5191                 erase_event.SetEventObject( this );
5192 
5193                 if ( HandleWindowEvent(erase_event) )
5194                 {
5195                     // background erased, don't do it again
5196                     break;
5197                 }
5198             }
5199             wxFALLTHROUGH;
5200 
5201         case wxBG_STYLE_SYSTEM:
5202             if ( GetThemeEnabled() )
5203             {
5204                 GdkWindow* gdkWindow = GTKGetDrawingWindow();
5205                 const int w = gdk_window_get_width(gdkWindow);
5206                 const int h = gdk_window_get_height(gdkWindow);
5207 #ifdef __WXGTK3__
5208                 GtkStyleContext* sc = gtk_widget_get_style_context(m_wxwindow);
5209                 gtk_render_background(sc, cr, 0, 0, w, h);
5210 #else
5211                 // find ancestor from which to steal background
5212                 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
5213                 if (!parent)
5214                     parent = (wxWindow*)this;
5215                 GdkRectangle rect;
5216                 m_nativeUpdateRegion.GetBox(rect.x, rect.y, rect.width, rect.height);
5217                 gtk_paint_flat_box(gtk_widget_get_style(parent->m_widget),
5218                                     gdkWindow,
5219                                     gtk_widget_get_state(m_wxwindow),
5220                                     GTK_SHADOW_NONE,
5221                                     &rect,
5222                                     parent->m_widget,
5223                                     const_cast<char*>("base"),
5224                                     0, 0, w, h);
5225 #endif // !__WXGTK3__
5226             }
5227 #ifdef __WXGTK3__
5228             else if (m_backgroundColour.IsOk() && gtk_check_version(3,20,0) == NULL)
5229             {
5230                 cairo_save(cr);
5231                 gdk_cairo_set_source_rgba(cr, m_backgroundColour);
5232                 cairo_paint(cr);
5233                 cairo_restore(cr);
5234             }
5235 #endif
5236             break;
5237 
5238         case wxBG_STYLE_PAINT:
5239             // nothing to do: window will be painted over in EVT_PAINT
5240             break;
5241 
5242         default:
5243             wxFAIL_MSG( "unsupported background style" );
5244     }
5245 
5246     wxNcPaintEvent nc_paint_event( this );
5247     HandleWindowEvent( nc_paint_event );
5248 
5249     wxPaintEvent paint_event( this );
5250     HandleWindowEvent( paint_event );
5251 
5252 #if wxGTK_HAS_COMPOSITING_SUPPORT
5253     if (IsTransparentBackgroundSupported())
5254     { // now composite children which need it
5255         // Overlay all our composite children on top of the painted area
5256         wxWindowList::compatibility_iterator node;
5257         for ( node = m_children.GetFirst(); node ; node = node->GetNext() )
5258         {
5259             wxWindow *compositeChild = node->GetData();
5260             if (compositeChild->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT)
5261             {
5262 #ifndef __WXGTK3__
5263                 if (cr == NULL)
5264                 {
5265                     cr = gdk_cairo_create(m_wxwindow->window);
5266                     gdk_cairo_region(cr, m_nativeUpdateRegion.GetRegion());
5267                     cairo_clip(cr);
5268                 }
5269 #endif // !__WXGTK3__
5270                 GtkWidget *child = compositeChild->m_wxwindow;
5271                 GtkAllocation alloc;
5272                 gtk_widget_get_allocation(child, &alloc);
5273 
5274                 // The source data is the (composited) child
5275                 gdk_cairo_set_source_window(
5276                     cr, gtk_widget_get_window(child), alloc.x, alloc.y);
5277 
5278                 cairo_paint(cr);
5279             }
5280         }
5281 #ifndef __WXGTK3__
5282         if (cr)
5283             cairo_destroy(cr);
5284 #endif
5285     }
5286 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
5287 
5288     m_clipPaintRegion = false;
5289 #ifdef __WXGTK3__
5290     m_paintContext = NULL;
5291 #endif
5292     m_updateRegion.Clear();
5293     m_nativeUpdateRegion.Clear();
5294 }
5295 
SetDoubleBuffered(bool on)5296 void wxWindowGTK::SetDoubleBuffered( bool on )
5297 {
5298     wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
5299 
5300     if ( m_wxwindow )
5301         gtk_widget_set_double_buffered( m_wxwindow, on );
5302 }
5303 
IsDoubleBuffered() const5304 bool wxWindowGTK::IsDoubleBuffered() const
5305 {
5306     return gtk_widget_get_double_buffered( m_wxwindow ) != 0;
5307 }
5308 
ClearBackground()5309 void wxWindowGTK::ClearBackground()
5310 {
5311     wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
5312 }
5313 
5314 #if wxUSE_TOOLTIPS
DoSetToolTip(wxToolTip * tip)5315 void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
5316 {
5317     if (m_tooltip != tip)
5318     {
5319         wxWindowBase::DoSetToolTip(tip);
5320 
5321         if (m_tooltip)
5322             m_tooltip->GTKSetWindow(static_cast<wxWindow*>(this));
5323         else
5324             GTKApplyToolTip(NULL);
5325     }
5326 }
5327 
GTKApplyToolTip(const char * tip)5328 void wxWindowGTK::GTKApplyToolTip(const char* tip)
5329 {
5330     wxToolTip::GTKApply(GetConnectWidget(), tip);
5331 }
5332 #endif // wxUSE_TOOLTIPS
5333 
SetBackgroundColour(const wxColour & colour)5334 bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
5335 {
5336     if (!wxWindowBase::SetBackgroundColour(colour))
5337         return false;
5338 
5339     if (m_widget)
5340     {
5341 #ifndef __WXGTK3__
5342         if (colour.IsOk())
5343         {
5344             // We need the pixel value e.g. for background clearing.
5345             m_backgroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
5346         }
5347 #endif
5348 
5349         // apply style change (forceStyle=true so that new style is applied
5350         // even if the bg colour changed from valid to wxNullColour)
5351         GTKApplyWidgetStyle(true);
5352     }
5353 
5354     return true;
5355 }
5356 
SetForegroundColour(const wxColour & colour)5357 bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
5358 {
5359     if (!wxWindowBase::SetForegroundColour(colour))
5360         return false;
5361 
5362     if (m_widget)
5363     {
5364 #ifndef __WXGTK3__
5365         if (colour.IsOk())
5366         {
5367             // We need the pixel value e.g. for background clearing.
5368             m_foregroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
5369         }
5370 #endif
5371 
5372         // apply style change (forceStyle=true so that new style is applied
5373         // even if the bg colour changed from valid to wxNullColour):
5374         GTKApplyWidgetStyle(true);
5375     }
5376 
5377     return true;
5378 }
5379 
GTKGetPangoDefaultContext()5380 PangoContext *wxWindowGTK::GTKGetPangoDefaultContext()
5381 {
5382     return gtk_widget_get_pango_context( m_widget );
5383 }
5384 
5385 #ifdef __WXGTK3__
GTKApplyCssStyle(GtkCssProvider * provider,const char * style)5386 void wxWindowGTK::GTKApplyCssStyle(GtkCssProvider* provider, const char* style)
5387 {
5388     wxCHECK_RET(m_widget, "invalid window");
5389 
5390     gtk_style_context_remove_provider(gtk_widget_get_style_context(m_widget),
5391                                       GTK_STYLE_PROVIDER(provider));
5392 
5393     gtk_css_provider_load_from_data(provider, style, -1, NULL);
5394 
5395     gtk_style_context_add_provider(gtk_widget_get_style_context(m_widget),
5396                                    GTK_STYLE_PROVIDER(provider),
5397                                    GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
5398 }
5399 
GTKApplyCssStyle(const char * style)5400 void wxWindowGTK::GTKApplyCssStyle(const char* style)
5401 {
5402     GtkCssProvider* provider = gtk_css_provider_new();
5403     GTKApplyCssStyle(provider, style);
5404     g_object_unref(provider);
5405 }
5406 #else // GTK+ < 3
GTKCreateWidgetStyle()5407 GtkRcStyle* wxWindowGTK::GTKCreateWidgetStyle()
5408 {
5409     GtkRcStyle *style = gtk_rc_style_new();
5410 
5411     if ( m_font.IsOk() )
5412     {
5413         style->font_desc =
5414             pango_font_description_copy( m_font.GetNativeFontInfo()->description );
5415     }
5416 
5417     int flagsNormal = 0,
5418         flagsPrelight = 0,
5419         flagsActive = 0,
5420         flagsInsensitive = 0;
5421 
5422     if ( m_foregroundColour.IsOk() )
5423     {
5424         const GdkColor *fg = m_foregroundColour.GetColor();
5425 
5426         style->fg[GTK_STATE_NORMAL] =
5427         style->text[GTK_STATE_NORMAL] = *fg;
5428         flagsNormal |= GTK_RC_FG | GTK_RC_TEXT;
5429 
5430         style->fg[GTK_STATE_PRELIGHT] =
5431         style->text[GTK_STATE_PRELIGHT] = *fg;
5432         flagsPrelight |= GTK_RC_FG | GTK_RC_TEXT;
5433 
5434         style->fg[GTK_STATE_ACTIVE] =
5435         style->text[GTK_STATE_ACTIVE] = *fg;
5436         flagsActive |= GTK_RC_FG | GTK_RC_TEXT;
5437     }
5438 
5439     if ( m_backgroundColour.IsOk() )
5440     {
5441         const GdkColor *bg = m_backgroundColour.GetColor();
5442 
5443         style->bg[GTK_STATE_NORMAL] =
5444         style->base[GTK_STATE_NORMAL] = *bg;
5445         flagsNormal |= GTK_RC_BG | GTK_RC_BASE;
5446 
5447         style->bg[GTK_STATE_PRELIGHT] =
5448         style->base[GTK_STATE_PRELIGHT] = *bg;
5449         flagsPrelight |= GTK_RC_BG | GTK_RC_BASE;
5450 
5451         style->bg[GTK_STATE_ACTIVE] =
5452         style->base[GTK_STATE_ACTIVE] = *bg;
5453         flagsActive |= GTK_RC_BG | GTK_RC_BASE;
5454 
5455         style->bg[GTK_STATE_INSENSITIVE] =
5456         style->base[GTK_STATE_INSENSITIVE] = *bg;
5457         flagsInsensitive |= GTK_RC_BG | GTK_RC_BASE;
5458     }
5459 
5460     style->color_flags[GTK_STATE_NORMAL] = (GtkRcFlags)flagsNormal;
5461     style->color_flags[GTK_STATE_PRELIGHT] = (GtkRcFlags)flagsPrelight;
5462     style->color_flags[GTK_STATE_ACTIVE] = (GtkRcFlags)flagsActive;
5463     style->color_flags[GTK_STATE_INSENSITIVE] = (GtkRcFlags)flagsInsensitive;
5464 
5465     return style;
5466 }
5467 #endif // !__WXGTK3__
5468 
GTKApplyWidgetStyle(bool forceStyle)5469 void wxWindowGTK::GTKApplyWidgetStyle(bool forceStyle)
5470 {
5471     const wxColour& fg = m_foregroundColour;
5472     const wxColour& bg = m_backgroundColour;
5473     const bool isFg = fg.IsOk();
5474     const bool isBg = bg.IsOk();
5475     const bool isFont = m_font.IsOk();
5476     if (forceStyle || isFg || isBg || isFont)
5477     {
5478 #ifdef __WXGTK3__
5479         GString* css = g_string_new("*{");
5480         if (isFg)
5481         {
5482             g_string_append_printf(css, "color:%s;",
5483                 wxGtkString(gdk_rgba_to_string(fg)).c_str());
5484         }
5485         if (isBg)
5486         {
5487             g_string_append_printf(css, "background:%s;",
5488                 wxGtkString(gdk_rgba_to_string(bg)).c_str());
5489         }
5490         if (isFont)
5491         {
5492             g_string_append(css, "font:");
5493             const PangoFontDescription* pfd = m_font.GetNativeFontInfo()->description;
5494             if (gtk_check_version(3,22,0))
5495                 g_string_append(css, wxGtkString(pango_font_description_to_string(pfd)));
5496             else
5497             {
5498                 const PangoFontMask pfm = pango_font_description_get_set_fields(pfd);
5499                 if (pfm & PANGO_FONT_MASK_STYLE)
5500                 {
5501                     const char* s = "";
5502                     switch (pango_font_description_get_style(pfd))
5503                     {
5504                     case PANGO_STYLE_NORMAL: break;
5505                     case PANGO_STYLE_OBLIQUE: s = "oblique "; break;
5506                     case PANGO_STYLE_ITALIC: s = "italic "; break;
5507                     }
5508                     g_string_append(css, s);
5509                 }
5510                 if (pfm & PANGO_FONT_MASK_VARIANT)
5511                 {
5512                     switch (pango_font_description_get_variant(pfd))
5513                     {
5514                     case PANGO_VARIANT_NORMAL:
5515                         break;
5516                     case PANGO_VARIANT_SMALL_CAPS:
5517                         g_string_append(css, "small-caps ");
5518                         break;
5519                     }
5520                 }
5521                 if (pfm & PANGO_FONT_MASK_WEIGHT)
5522                 {
5523                     const int weight = pango_font_description_get_weight(pfd);
5524                     if (weight != PANGO_WEIGHT_NORMAL)
5525                         g_string_append_printf(css, "%d ", weight);
5526                 }
5527                 if (pfm & PANGO_FONT_MASK_STRETCH)
5528                 {
5529                     const char* s = "";
5530                     switch (pango_font_description_get_stretch(pfd))
5531                     {
5532                     case PANGO_STRETCH_ULTRA_CONDENSED: s = "ultra-condensed "; break;
5533                     case PANGO_STRETCH_EXTRA_CONDENSED: s = "extra-condensed "; break;
5534                     case PANGO_STRETCH_CONDENSED: s = "condensed "; break;
5535                     case PANGO_STRETCH_SEMI_CONDENSED: s = "semi-condensed "; break;
5536                     case PANGO_STRETCH_NORMAL: break;
5537                     case PANGO_STRETCH_SEMI_EXPANDED: s = "semi-expanded "; break;
5538                     case PANGO_STRETCH_EXPANDED: s = "expanded "; break;
5539                     case PANGO_STRETCH_EXTRA_EXPANDED: s = "extra-expanded "; break;
5540                     case PANGO_STRETCH_ULTRA_EXPANDED: s = "ultra-expanded "; break;
5541                     }
5542                     g_string_append(css, s);
5543                 }
5544                 if (pfm & PANGO_FONT_MASK_SIZE)
5545                 {
5546                     const int size = pango_font_description_get_size(pfd);
5547                     if (pango_font_description_get_size_is_absolute(pfd))
5548                         g_string_append_printf(css, "%dpx ", size);
5549                     else
5550                         g_string_append_printf(css, "%dpt ", size / PANGO_SCALE);
5551                 }
5552                 if (pfm & PANGO_FONT_MASK_FAMILY)
5553                 {
5554                     g_string_append_printf(css, "\"%s\"",
5555                         pango_font_description_get_family(pfd));
5556                 }
5557             }
5558         }
5559         g_string_append_c(css, '}');
5560 
5561         if (isFg || isBg)
5562         {
5563             const bool isGTK3_20 = wx_is_at_least_gtk3(20);
5564             // Selection may be invisible, so add textview selection colors.
5565             // This is specifically for wxTextCtrl, but may be useful for other
5566             // controls, and seems to do no harm to apply to all.
5567             const wxColour fg_sel(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
5568             const wxColour bg_sel(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
5569             g_string_append_printf(css, "%s{color:%s;background:%s}",
5570                 isGTK3_20 ? "selection" : "*:selected",
5571                 wxGtkString(gdk_rgba_to_string(fg_sel)).c_str(),
5572                 wxGtkString(gdk_rgba_to_string(bg_sel)).c_str());
5573 
5574             if (isFg && isGTK3_20)
5575             {
5576                 g_string_append_printf(css, "*{caret-color:%s}",
5577                     wxGtkString(gdk_rgba_to_string(fg)).c_str());
5578             }
5579             if (isBg)
5580             {
5581                 // make "undershoot" node background transparent,
5582                 // keeps expected look of GtkEntry with default theme
5583                 g_string_append(css, "* undershoot{background:transparent}");
5584             }
5585         }
5586 
5587         if (m_styleProvider == NULL && (isFg || isBg || isFont))
5588             m_styleProvider = GTK_STYLE_PROVIDER(gtk_css_provider_new());
5589 
5590         wxGtkString s(g_string_free(css, false));
5591         if (m_styleProvider)
5592         {
5593             gtk_css_provider_load_from_data(
5594                 GTK_CSS_PROVIDER(m_styleProvider), s, -1, NULL);
5595             DoApplyWidgetStyle(NULL);
5596         }
5597 #else
5598         GtkRcStyle* style = GTKCreateWidgetStyle();
5599         DoApplyWidgetStyle(style);
5600         g_object_unref(style);
5601 #endif
5602     }
5603 }
5604 
DoApplyWidgetStyle(GtkRcStyle * style)5605 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
5606 {
5607     GtkWidget* widget = m_wxwindow ? m_wxwindow : m_widget;
5608     GTKApplyStyle(widget, style);
5609 }
5610 
GTKApplyStyle(GtkWidget * widget,GtkRcStyle * WXUNUSED_IN_GTK3 (style))5611 void wxWindowGTK::GTKApplyStyle(GtkWidget* widget, GtkRcStyle* WXUNUSED_IN_GTK3(style))
5612 {
5613 #ifdef __WXGTK3__
5614     if (m_styleProvider)
5615     {
5616         GtkStyleContext* context = gtk_widget_get_style_context(widget);
5617         gtk_style_context_add_provider(context,
5618             m_styleProvider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
5619     }
5620 #else
5621     gtk_widget_modify_style(widget, style);
5622 #endif
5623 }
5624 
SetBackgroundStyle(wxBackgroundStyle style)5625 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
5626 {
5627     if (!wxWindowBase::SetBackgroundStyle(style))
5628         return false;
5629 
5630 #ifndef __WXGTK3__
5631     GdkWindow *window;
5632     if ((style == wxBG_STYLE_PAINT || style == wxBG_STYLE_TRANSPARENT) &&
5633         (window = GTKGetDrawingWindow()))
5634     {
5635         gdk_window_set_back_pixmap(window, NULL, false);
5636     }
5637 #endif // !__WXGTK3__
5638 
5639     return true;
5640 }
5641 
IsTransparentBackgroundSupported(wxString * reason) const5642 bool wxWindowGTK::IsTransparentBackgroundSupported(wxString* reason) const
5643 {
5644 #if wxGTK_HAS_COMPOSITING_SUPPORT
5645 #ifndef __WXGTK3__
5646     if (!wx_is_at_least_gtk2(12))
5647     {
5648         if (reason)
5649         {
5650             *reason = _("GTK+ installed on this machine is too old to "
5651                         "support screen compositing, please install "
5652                         "GTK+ 2.12 or later.");
5653         }
5654 
5655         return false;
5656     }
5657 #endif // !__WXGTK3__
5658 
5659     // NB: We don't check here if the particular kind of widget supports
5660     // transparency, we check only if it would be possible for a generic window
5661 
5662     wxCHECK_MSG ( m_widget, false, "Window must be created first" );
5663 
5664     if (!gdk_screen_is_composited(gtk_widget_get_screen(m_widget)))
5665     {
5666         if (reason)
5667         {
5668             *reason = _("Compositing not supported by this system, "
5669                         "please enable it in your Window Manager.");
5670         }
5671 
5672         return false;
5673     }
5674 
5675     return true;
5676 #else
5677     if (reason)
5678     {
5679         *reason = _("This program was compiled with a too old version of GTK+, "
5680                     "please rebuild with GTK+ 2.12 or newer.");
5681     }
5682 
5683     return false;
5684 #endif // wxGTK_HAS_COMPOSITING_SUPPORT/!wxGTK_HAS_COMPOSITING_SUPPORT
5685 }
5686 
5687 #ifdef __WXGTK3__
GTKFindWindow(GtkWidget * widget)5688 GdkWindow* wxWindowGTK::GTKFindWindow(GtkWidget* widget)
5689 {
5690     GdkWindow* window = gtk_widget_get_window(widget);
5691     if (window == NULL)
5692         return NULL;
5693     for (const GList* p = gdk_window_peek_children(window); p; p = p->next)
5694     {
5695         window = GDK_WINDOW(p->data);
5696         void* data;
5697         gdk_window_get_user_data(window, &data);
5698         if (data == widget)
5699             return window;
5700     }
5701     return NULL;
5702 }
5703 
GTKFindWindow(GtkWidget * widget,wxArrayGdkWindows & windows)5704 void wxWindowGTK::GTKFindWindow(GtkWidget* widget, wxArrayGdkWindows& windows)
5705 {
5706     GdkWindow* window = gtk_widget_get_window(widget);
5707     if (window == NULL)
5708         return;
5709     for (const GList* p = gdk_window_peek_children(window); p; p = p->next)
5710     {
5711         window = GDK_WINDOW(p->data);
5712         void* data;
5713         gdk_window_get_user_data(window, &data);
5714         if (data == widget)
5715             windows.push_back(window);
5716     }
5717 }
5718 #endif // __WXGTK3__
5719 
5720 // ----------------------------------------------------------------------------
5721 // Pop-up menu stuff
5722 // ----------------------------------------------------------------------------
5723 
5724 #if wxUSE_MENUS_NATIVE
5725 
5726 struct wxPopupMenuPositionCallbackData
5727 {
5728     wxPoint pos;
5729     wxMenu *menu;
5730 };
5731 
5732 extern "C" {
5733 static
wxPopupMenuPositionCallback(GtkMenu * menu,gint * x,gint * y,gboolean * WXUNUSED (whatever),gpointer user_data)5734 void wxPopupMenuPositionCallback( GtkMenu *menu,
5735                                   gint *x, gint *y,
5736                                   gboolean * WXUNUSED(whatever),
5737                                   gpointer user_data )
5738 {
5739     // ensure that the menu appears entirely on the same display as the window
5740     GtkRequisition req;
5741 #ifdef __WXGTK3__
5742     gtk_widget_get_preferred_size(GTK_WIDGET(menu), &req, NULL);
5743 #else
5744     gtk_widget_get_child_requisition(GTK_WIDGET(menu), &req);
5745 #endif
5746 
5747     const wxPopupMenuPositionCallbackData&
5748         data = *static_cast<wxPopupMenuPositionCallbackData*>(user_data);
5749 
5750     const wxRect
5751         rect = wxDisplay(data.menu->GetInvokingWindow()).GetClientArea();
5752 
5753     wxPoint pos = data.pos;
5754     if ( pos.x < rect.x )
5755         pos.x = rect.x;
5756     if ( pos.y < rect.y )
5757         pos.y = rect.y;
5758     if ( pos.x + req.width > rect.GetRight() )
5759         pos.x = rect.GetRight() - req.width;
5760     if ( pos.y + req.height > rect.GetBottom() )
5761         pos.y = rect.GetBottom() - req.height;
5762 
5763     *x = pos.x;
5764     *y = pos.y;
5765 }
5766 }
5767 
DoPopupMenu(wxMenu * menu,int x,int y)5768 bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
5769 {
5770     wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
5771 
5772     wxPopupMenuPositionCallbackData data;
5773     gpointer userdata;
5774     GtkMenuPositionFunc posfunc;
5775     if ( x == -1 && y == -1 )
5776     {
5777         // use GTK's default positioning algorithm
5778         userdata = NULL;
5779         posfunc = NULL;
5780     }
5781     else
5782     {
5783         data.pos = ClientToScreen(wxPoint(x, y));
5784         data.menu = menu;
5785         userdata = &data;
5786         posfunc = wxPopupMenuPositionCallback;
5787     }
5788 
5789     menu->m_popupShown = true;
5790     gtk_menu_popup(
5791                   GTK_MENU(menu->m_menu),
5792                   NULL,           // parent menu shell
5793                   NULL,           // parent menu item
5794                   posfunc,                      // function to position it
5795                   userdata,                     // client data
5796                   0,                            // button used to activate it
5797                   gtk_get_current_event_time()
5798                 );
5799 
5800     // it is possible for gtk_menu_popup() to fail
5801     if (!gtk_widget_get_visible(GTK_WIDGET(menu->m_menu)))
5802     {
5803         menu->m_popupShown = false;
5804         return false;
5805     }
5806 
5807     while (menu->m_popupShown)
5808     {
5809         gtk_main_iteration();
5810     }
5811 
5812     return true;
5813 }
5814 
5815 #endif // wxUSE_MENUS_NATIVE
5816 
5817 #if wxUSE_DRAG_AND_DROP
5818 
SetDropTarget(wxDropTarget * dropTarget)5819 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
5820 {
5821     wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
5822 
5823     GtkWidget *dnd_widget = GetConnectWidget();
5824 
5825     if (m_dropTarget) m_dropTarget->GtkUnregisterWidget( dnd_widget );
5826 
5827     delete m_dropTarget;
5828     m_dropTarget = dropTarget;
5829 
5830     if (m_dropTarget) m_dropTarget->GtkRegisterWidget( dnd_widget );
5831 }
5832 
5833 #endif // wxUSE_DRAG_AND_DROP
5834 
GetConnectWidget()5835 GtkWidget* wxWindowGTK::GetConnectWidget()
5836 {
5837     GtkWidget *connect_widget = m_widget;
5838     if (m_wxwindow) connect_widget = m_wxwindow;
5839 
5840     return connect_widget;
5841 }
5842 
GTKIsOwnWindow(GdkWindow * window) const5843 bool wxWindowGTK::GTKIsOwnWindow(GdkWindow *window) const
5844 {
5845     wxArrayGdkWindows windowsThis;
5846     GdkWindow * const winThis = GTKGetWindow(windowsThis);
5847 
5848     return winThis ? window == winThis
5849                    : windowsThis.Index(window) != wxNOT_FOUND;
5850 }
5851 
GTKGetWindow(wxArrayGdkWindows & WXUNUSED (windows)) const5852 GdkWindow *wxWindowGTK::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
5853 {
5854     return m_wxwindow ? GTKGetDrawingWindow() : gtk_widget_get_window(m_widget);
5855 }
5856 
5857 #ifdef __WXGTK3__
GTKSizeRevalidate()5858 void wxWindowGTK::GTKSizeRevalidate()
5859 {
5860     GList* next;
5861     for (GList* p = gs_sizeRevalidateList; p; p = next)
5862     {
5863         next = p->next;
5864         wxWindow* win = static_cast<wxWindow*>(p->data);
5865         if (wxGetTopLevelParent(win) == this)
5866         {
5867             win->InvalidateBestSize();
5868             gs_sizeRevalidateList = g_list_delete_link(gs_sizeRevalidateList, p);
5869             for (;;)
5870             {
5871                 win = win->m_parent;
5872                 if (win == NULL || win->m_needSizeEvent)
5873                     break;
5874                 win->m_needSizeEvent = true;
5875                 if (win->IsTopLevel())
5876                     break;
5877             }
5878         }
5879     }
5880 }
5881 
5882 extern "C" {
before_resize(void * data)5883 static gboolean before_resize(void* data)
5884 {
5885     wxWindow* win = static_cast<wxWindow*>(data);
5886     win->InvalidateBestSize();
5887     return false;
5888 }
5889 }
5890 #endif // __WXGTK3__
5891 
SetFont(const wxFont & font)5892 bool wxWindowGTK::SetFont( const wxFont &font )
5893 {
5894     if (!wxWindowBase::SetFont(font))
5895         return false;
5896 
5897     if (m_widget)
5898     {
5899         // apply style change (forceStyle=true so that new style is applied
5900         // even if the font changed from valid to wxNullFont):
5901         GTKApplyWidgetStyle(true);
5902         InvalidateBestSize();
5903     }
5904 
5905 #ifdef __WXGTK3__
5906     // Starting with GTK 3.6, style information is cached, and the cache is only
5907     // updated before resizing, or when showing a TLW. If a different size font
5908     // is set, our best size calculation will be wrong. All we can do is
5909     // invalidate the best size right before the style cache is updated, so any
5910     // subsequent best size requests use the correct font.
5911     if (gtk_check_version(3,8,0) == NULL)
5912         gs_sizeRevalidateList = g_list_prepend(gs_sizeRevalidateList, this);
5913     else if (gtk_check_version(3,6,0) == NULL)
5914     {
5915         wxWindow* tlw = wxGetTopLevelParent(static_cast<wxWindow*>(this));
5916         if (tlw->m_widget && gtk_widget_get_visible(tlw->m_widget))
5917             g_idle_add_full(GTK_PRIORITY_RESIZE - 1, before_resize, this, NULL);
5918         else
5919             gs_sizeRevalidateList = g_list_prepend(gs_sizeRevalidateList, this);
5920     }
5921 #endif
5922 
5923     return true;
5924 }
5925 
DoCaptureMouse()5926 void wxWindowGTK::DoCaptureMouse()
5927 {
5928     wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
5929 
5930     GdkWindow *window = NULL;
5931     if (m_wxwindow)
5932         window = GTKGetDrawingWindow();
5933     else
5934         window = gtk_widget_get_window(GetConnectWidget());
5935 
5936     wxCHECK_RET( window, wxT("CaptureMouse() failed") );
5937 
5938 #ifdef __WXGTK4__
5939     GdkDisplay* display = gdk_window_get_display(window);
5940     GdkSeat* seat = gdk_display_get_default_seat(display);
5941     gdk_seat_grab(seat, window, GDK_SEAT_CAPABILITY_POINTER, false, NULL, NULL, NULL, 0);
5942 #else
5943     const GdkEventMask mask = GdkEventMask(
5944         GDK_SCROLL_MASK |
5945         GDK_BUTTON_PRESS_MASK |
5946         GDK_BUTTON_RELEASE_MASK |
5947         GDK_POINTER_MOTION_HINT_MASK |
5948         GDK_POINTER_MOTION_MASK);
5949 #ifdef __WXGTK3__
5950     GdkDisplay* display = gdk_window_get_display(window);
5951     wxGCC_WARNING_SUPPRESS(deprecated-declarations)
5952     GdkDeviceManager* manager = gdk_display_get_device_manager(display);
5953     GdkDevice* device = gdk_device_manager_get_client_pointer(manager);
5954     gdk_device_grab(
5955         device, window, GDK_OWNERSHIP_NONE, false, mask,
5956         NULL, unsigned(GDK_CURRENT_TIME));
5957     wxGCC_WARNING_RESTORE()
5958 #else
5959     gdk_pointer_grab( window, FALSE,
5960                       mask,
5961                       NULL,
5962                       NULL,
5963                       (guint32)GDK_CURRENT_TIME );
5964 #endif
5965 #endif // !__WXGTK4__
5966     g_captureWindow = this;
5967     g_captureWindowHasMouse = true;
5968 }
5969 
DoReleaseMouse()5970 void wxWindowGTK::DoReleaseMouse()
5971 {
5972     wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
5973 
5974     wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
5975 
5976     g_captureWindow = NULL;
5977 
5978     GdkWindow *window = NULL;
5979     if (m_wxwindow)
5980         window = GTKGetDrawingWindow();
5981     else
5982         window = gtk_widget_get_window(GetConnectWidget());
5983 
5984     if (!window)
5985         return;
5986 
5987 #ifdef __WXGTK3__
5988     GdkDisplay* display = gdk_window_get_display(window);
5989 #ifdef __WXGTK4__
5990     gdk_seat_ungrab(gdk_display_get_default_seat(display));
5991 #else
5992     wxGCC_WARNING_SUPPRESS(deprecated-declarations)
5993     GdkDeviceManager* manager = gdk_display_get_device_manager(display);
5994     GdkDevice* device = gdk_device_manager_get_client_pointer(manager);
5995     gdk_device_ungrab(device, unsigned(GDK_CURRENT_TIME));
5996     wxGCC_WARNING_RESTORE()
5997 #endif
5998 #else
5999     gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
6000 #endif
6001 }
6002 
GTKReleaseMouseAndNotify()6003 void wxWindowGTK::GTKReleaseMouseAndNotify()
6004 {
6005     GdkDisplay* display = gtk_widget_get_display(m_widget);
6006 #ifdef __WXGTK3__
6007 #ifdef __WXGTK4__
6008     gdk_seat_ungrab(gdk_display_get_default_seat(display));
6009 #else
6010     wxGCC_WARNING_SUPPRESS(deprecated-declarations)
6011     GdkDeviceManager* manager = gdk_display_get_device_manager(display);
6012     GdkDevice* device = gdk_device_manager_get_client_pointer(manager);
6013     gdk_device_ungrab(device, unsigned(GDK_CURRENT_TIME));
6014     wxGCC_WARNING_RESTORE()
6015 #endif
6016 #else
6017     gdk_display_pointer_ungrab(display, unsigned(GDK_CURRENT_TIME));
6018 #endif
6019     g_captureWindow = NULL;
6020     NotifyCaptureLost();
6021 }
6022 
GTKHandleCaptureLost()6023 void wxWindowGTK::GTKHandleCaptureLost()
6024 {
6025     g_captureWindow = NULL;
6026     NotifyCaptureLost();
6027 }
6028 
6029 /* static */
GetCapture()6030 wxWindow *wxWindowBase::GetCapture()
6031 {
6032     return (wxWindow *)g_captureWindow;
6033 }
6034 
IsRetained() const6035 bool wxWindowGTK::IsRetained() const
6036 {
6037     return false;
6038 }
6039 
SetScrollbar(int orient,int pos,int thumbVisible,int range,bool WXUNUSED (update))6040 void wxWindowGTK::SetScrollbar(int orient,
6041                                int pos,
6042                                int thumbVisible,
6043                                int range,
6044                                bool WXUNUSED(update))
6045 {
6046     const int dir = ScrollDirFromOrient(orient);
6047     GtkRange* const sb = m_scrollBar[dir];
6048     wxCHECK_RET( sb, wxT("this window is not scrollable") );
6049 
6050     if (range <= 0)
6051     {
6052         // GtkRange requires upper > lower
6053         range =
6054         thumbVisible = 1;
6055     }
6056     else if (thumbVisible <= 0)
6057         thumbVisible = 1;
6058 
6059     g_signal_handlers_block_by_func(
6060         sb, (void*)gtk_scrollbar_value_changed, this);
6061 
6062     GtkAdjustment* adj = gtk_range_get_adjustment(sb);
6063     const bool wasVisible = gtk_adjustment_get_upper(adj) > gtk_adjustment_get_page_size(adj);
6064 
6065     g_object_freeze_notify(G_OBJECT(adj));
6066     gtk_range_set_increments(sb, 1, thumbVisible);
6067     gtk_adjustment_set_page_size(adj, thumbVisible);
6068     gtk_range_set_range(sb, 0, range);
6069     g_object_thaw_notify(G_OBJECT(adj));
6070 
6071     gtk_range_set_value(sb, pos);
6072     m_scrollPos[dir] = gtk_range_get_value(sb);
6073 
6074     const bool isVisible = gtk_adjustment_get_upper(adj) > gtk_adjustment_get_page_size(adj);
6075     if (isVisible != wasVisible)
6076         m_useCachedClientSize = false;
6077 
6078     g_signal_handlers_unblock_by_func(
6079         sb, (void*)gtk_scrollbar_value_changed, this);
6080 }
6081 
SetScrollPos(int orient,int pos,bool WXUNUSED (refresh))6082 void wxWindowGTK::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh))
6083 {
6084     const int dir = ScrollDirFromOrient(orient);
6085     GtkRange * const sb = m_scrollBar[dir];
6086     wxCHECK_RET( sb, wxT("this window is not scrollable") );
6087 
6088     // This check is more than an optimization. Without it, the slider
6089     //   will not move smoothly while tracking when using wxScrollHelper.
6090     if (GetScrollPos(orient) != pos)
6091     {
6092         g_signal_handlers_block_by_func(
6093             sb, (void*)gtk_scrollbar_value_changed, this);
6094 
6095         gtk_range_set_value(sb, pos);
6096         m_scrollPos[dir] = gtk_range_get_value(sb);
6097 
6098         g_signal_handlers_unblock_by_func(
6099             sb, (void*)gtk_scrollbar_value_changed, this);
6100     }
6101 }
6102 
GetScrollThumb(int orient) const6103 int wxWindowGTK::GetScrollThumb(int orient) const
6104 {
6105     GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
6106     wxCHECK_MSG( sb, 0, wxT("this window is not scrollable") );
6107 
6108     return wxRound(gtk_adjustment_get_page_size(gtk_range_get_adjustment(sb)));
6109 }
6110 
GetScrollPos(int orient) const6111 int wxWindowGTK::GetScrollPos( int orient ) const
6112 {
6113     GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
6114     wxCHECK_MSG( sb, 0, wxT("this window is not scrollable") );
6115 
6116     return wxRound(gtk_range_get_value(sb));
6117 }
6118 
GetScrollRange(int orient) const6119 int wxWindowGTK::GetScrollRange( int orient ) const
6120 {
6121     GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
6122     wxCHECK_MSG( sb, 0, wxT("this window is not scrollable") );
6123 
6124     return wxRound(gtk_adjustment_get_upper(gtk_range_get_adjustment(sb)));
6125 }
6126 
6127 // Determine if increment is the same as +/-x, allowing for some small
6128 //   difference due to possible inexactness in floating point arithmetic
IsScrollIncrement(double increment,double x)6129 static inline bool IsScrollIncrement(double increment, double x)
6130 {
6131     wxASSERT(increment > 0);
6132     const double tolerance = 1.0 / 1024;
6133     return fabs(increment - fabs(x)) < tolerance;
6134 }
6135 
GTKGetScrollEventType(GtkRange * range)6136 wxEventType wxWindowGTK::GTKGetScrollEventType(GtkRange* range)
6137 {
6138     wxASSERT(range == m_scrollBar[0] || range == m_scrollBar[1]);
6139 
6140     const int barIndex = range == m_scrollBar[1];
6141 
6142     GtkAdjustment* adj = gtk_range_get_adjustment(range);
6143     const double value = gtk_adjustment_get_value(adj);
6144 
6145     // save previous position
6146     const double oldPos = m_scrollPos[barIndex];
6147     // update current position
6148     m_scrollPos[barIndex] = value;
6149     // If event should be ignored, or integral position has not changed
6150     // or scrollbar is disabled (webkitgtk is known to cause a "value-changed"
6151     // by setting the GtkAdjustment to all zeros)
6152     if (g_blockEventsOnDrag || wxRound(value) == wxRound(oldPos) ||
6153         gtk_adjustment_get_upper(adj) <= gtk_adjustment_get_page_size(adj))
6154     {
6155         return wxEVT_NULL;
6156     }
6157 
6158     wxEventType eventType = wxEVT_SCROLL_THUMBTRACK;
6159     if (!m_isScrolling)
6160     {
6161         // Difference from last change event
6162         const double diff = value - oldPos;
6163         const bool isDown = diff > 0;
6164 
6165         if (IsScrollIncrement(gtk_adjustment_get_step_increment(adj), diff))
6166         {
6167             eventType = isDown ? wxEVT_SCROLL_LINEDOWN : wxEVT_SCROLL_LINEUP;
6168         }
6169         else if (IsScrollIncrement(gtk_adjustment_get_page_increment(adj), diff))
6170         {
6171             eventType = isDown ? wxEVT_SCROLL_PAGEDOWN : wxEVT_SCROLL_PAGEUP;
6172         }
6173         else if (m_mouseButtonDown)
6174         {
6175             // Assume track event
6176             m_isScrolling = true;
6177         }
6178     }
6179     return eventType;
6180 }
6181 
ScrollWindow(int dx,int dy,const wxRect * WXUNUSED (rect))6182 void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
6183 {
6184     wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
6185 
6186     wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
6187 
6188     // No scrolling requested.
6189     if ((dx == 0) && (dy == 0)) return;
6190 
6191     m_clipPaintRegion = true;
6192 
6193     WX_PIZZA(m_wxwindow)->scroll(dx, dy);
6194 
6195     m_clipPaintRegion = false;
6196 
6197 #if wxUSE_CARET
6198     bool restoreCaret = (GetCaret() != NULL && GetCaret()->IsVisible());
6199     if (restoreCaret)
6200     {
6201         wxRect caretRect(GetCaret()->GetPosition(), GetCaret()->GetSize());
6202         if (dx > 0)
6203             caretRect.width += dx;
6204         else
6205         {
6206             caretRect.x += dx; caretRect.width -= dx;
6207         }
6208         if (dy > 0)
6209             caretRect.height += dy;
6210         else
6211         {
6212             caretRect.y += dy; caretRect.height -= dy;
6213         }
6214 
6215         RefreshRect(caretRect);
6216     }
6217 #endif // wxUSE_CARET
6218 }
6219 
GTKScrolledWindowSetBorder(GtkWidget * w,int wxstyle)6220 void wxWindowGTK::GTKScrolledWindowSetBorder(GtkWidget* w, int wxstyle)
6221 {
6222     //RN: Note that static controls usually have no border on gtk, so maybe
6223     //it makes sense to treat that as simply no border at the wx level
6224     //as well...
6225     if (!(wxstyle & wxNO_BORDER) && !(wxstyle & wxBORDER_STATIC))
6226     {
6227         GtkShadowType gtkstyle;
6228 
6229         if(wxstyle & wxBORDER_RAISED)
6230             gtkstyle = GTK_SHADOW_OUT;
6231         else if ((wxstyle & wxBORDER_SUNKEN) || (wxstyle & wxBORDER_THEME))
6232             gtkstyle = GTK_SHADOW_IN;
6233 #if 0
6234         // Now obsolete
6235         else if (wxstyle & wxBORDER_DOUBLE)
6236             gtkstyle = GTK_SHADOW_ETCHED_IN;
6237 #endif
6238         else //default
6239             gtkstyle = GTK_SHADOW_IN;
6240 
6241         gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w),
6242                                              gtkstyle );
6243     }
6244 }
6245 
6246 // Find the wxWindow at the current mouse position, also returning the mouse
6247 // position.
wxFindWindowAtPointer(wxPoint & pt)6248 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
6249 {
6250     pt = wxGetMousePosition();
6251     wxWindow* found = wxFindWindowAtPoint(pt);
6252     return found;
6253 }
6254 
6255 // Get the current mouse position.
wxGetMousePosition(int * x,int * y)6256 void wxGetMousePosition(int* x, int* y)
6257 {
6258     GdkDisplay* display = gdk_window_get_display(wxGetTopLevelGDK());
6259 #ifdef __WXGTK3__
6260 #ifdef __WXGTK4__
6261     GdkSeat* seat = gdk_display_get_default_seat(display);
6262     GdkDevice* device = gdk_seat_get_pointer(seat);
6263 #else
6264     wxGCC_WARNING_SUPPRESS(deprecated-declarations)
6265     GdkDeviceManager* manager = gdk_display_get_device_manager(display);
6266     GdkDevice* device = gdk_device_manager_get_client_pointer(manager);
6267     wxGCC_WARNING_RESTORE()
6268 #endif
6269     gdk_device_get_position(device, NULL, x, y);
6270 #else
6271     gdk_display_get_pointer(display, NULL, x, y, NULL);
6272 #endif
6273 }
6274 
wxGetMousePosition()6275 wxPoint wxGetMousePosition()
6276 {
6277     wxPoint pt;
6278     wxGetMousePosition(&pt.x, &pt.y);
6279     return pt;
6280 }
6281 
GTKGetDrawingWindow() const6282 GdkWindow* wxWindowGTK::GTKGetDrawingWindow() const
6283 {
6284     GdkWindow* window = NULL;
6285     if (m_wxwindow)
6286         window = gtk_widget_get_window(m_wxwindow);
6287     return window;
6288 }
6289 
6290 // ----------------------------------------------------------------------------
6291 // freeze/thaw
6292 // ----------------------------------------------------------------------------
6293 
6294 extern "C" {
draw_freeze(GtkWidget *,void *,wxWindow *)6295 static gboolean draw_freeze(GtkWidget*, void*, wxWindow*)
6296 {
6297     // stop other handlers from being invoked
6298     return true;
6299 }
6300 }
6301 
GTKConnectFreezeWidget(GtkWidget * widget)6302 void wxWindowGTK::GTKConnectFreezeWidget(GtkWidget* widget)
6303 {
6304 #ifdef __WXGTK3__
6305     gulong id = g_signal_connect(widget, "draw", G_CALLBACK(draw_freeze), this);
6306 #else
6307     gulong id = g_signal_connect(widget, "expose-event", G_CALLBACK(draw_freeze), this);
6308 #endif
6309     g_signal_handler_block(widget, id);
6310 }
6311 
GTKFreezeWidget(GtkWidget * widget)6312 void wxWindowGTK::GTKFreezeWidget(GtkWidget* widget)
6313 {
6314     g_signal_handlers_unblock_by_func(widget, (void*)draw_freeze, this);
6315 }
6316 
GTKThawWidget(GtkWidget * widget)6317 void wxWindowGTK::GTKThawWidget(GtkWidget* widget)
6318 {
6319     g_signal_handlers_block_by_func(widget, (void*)draw_freeze, this);
6320     gtk_widget_queue_draw(widget);
6321 }
6322 
DoFreeze()6323 void wxWindowGTK::DoFreeze()
6324 {
6325     wxCHECK_RET(m_widget, "invalid window");
6326 
6327     GTKFreezeWidget(m_widget);
6328     if (m_wxwindow && m_wxwindow != m_widget)
6329         GTKFreezeWidget(m_wxwindow);
6330 }
6331 
DoThaw()6332 void wxWindowGTK::DoThaw()
6333 {
6334     wxCHECK_RET(m_widget, "invalid window");
6335 
6336     GTKThawWidget(m_widget);
6337     if (m_wxwindow && m_wxwindow != m_widget)
6338         GTKThawWidget(m_wxwindow);
6339 }
6340