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