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