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