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