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