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