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