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