Implement wxWindow::SetFocusIgnoringChildren for GTK+
[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 if ((!ret) && (win->m_imData != NULL))
1017 {
1018 // We should let GTK+ IM filter key event first. According to GTK+ 2.0 API
1019 // docs, if IM filter returns true, no further processing should be done.
1020 // we should send the key_down event anyway.
1021 bool intercepted_by_IM = gtk_im_context_filter_keypress(win->m_imData->context, gdk_event);
1022 win->m_imData->lastKeyEvent = NULL;
1023 if (intercepted_by_IM)
1024 {
1025 wxLogTrace(TRACE_KEYS, _T("Key event intercepted by IM"));
1026 return TRUE;
1027 }
1028 }
1029
1030 if (return_after_IM)
1031 return FALSE;
1032
1033 #if wxUSE_ACCEL
1034 if (!ret)
1035 {
1036 wxWindowGTK *ancestor = win;
1037 while (ancestor)
1038 {
1039 int command = ancestor->GetAcceleratorTable()->GetCommand( event );
1040 if (command != -1)
1041 {
1042 wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command );
1043 ret = ancestor->GetEventHandler()->ProcessEvent( command_event );
1044 break;
1045 }
1046 if (ancestor->IsTopLevel())
1047 break;
1048 ancestor = ancestor->GetParent();
1049 }
1050 }
1051 #endif // wxUSE_ACCEL
1052
1053 // Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
1054 // will only be sent if it is not in an accelerator table.
1055 if (!ret)
1056 {
1057 long key_code;
1058 KeySym keysym = gdk_event->keyval;
1059 // Find key code for EVT_CHAR and EVT_CHAR_HOOK events
1060 key_code = wxTranslateKeySymToWXKey(keysym, true /* isChar */);
1061 if ( !key_code )
1062 {
1063 if ( wxIsAsciiKeysym(keysym) )
1064 {
1065 // ASCII key
1066 key_code = (unsigned char)keysym;
1067 }
1068 // gdk_event->string is actually deprecated
1069 else if ( gdk_event->length == 1 )
1070 {
1071 key_code = (unsigned char)gdk_event->string[0];
1072 }
1073 }
1074
1075 if ( key_code )
1076 {
1077 wxLogTrace(TRACE_KEYS, _T("Char event: %ld"), key_code);
1078
1079 event.m_keyCode = key_code;
1080
1081 // To conform to the docs we need to translate Ctrl-alpha
1082 // characters to values in the range 1-26.
1083 if ( event.ControlDown() &&
1084 ( wxIsLowerChar(key_code) || wxIsUpperChar(key_code) ))
1085 {
1086 if ( wxIsLowerChar(key_code) )
1087 event.m_keyCode = key_code - 'a' + 1;
1088 if ( wxIsUpperChar(key_code) )
1089 event.m_keyCode = key_code - 'A' + 1;
1090 #if wxUSE_UNICODE
1091 event.m_uniChar = event.m_keyCode;
1092 #endif
1093 }
1094
1095 // Implement OnCharHook by checking ancestor top level windows
1096 wxWindow *parent = win;
1097 while (parent && !parent->IsTopLevel())
1098 parent = parent->GetParent();
1099 if (parent)
1100 {
1101 event.SetEventType( wxEVT_CHAR_HOOK );
1102 ret = parent->GetEventHandler()->ProcessEvent( event );
1103 }
1104
1105 if (!ret)
1106 {
1107 event.SetEventType(wxEVT_CHAR);
1108 ret = win->GetEventHandler()->ProcessEvent( event );
1109 }
1110 }
1111 }
1112
1113 return ret;
1114 }
1115 }
1116
1117 extern "C" {
1118 static void
1119 gtk_wxwindow_commit_cb (GtkIMContext *context,
1120 const gchar *str,
1121 wxWindow *window)
1122 {
1123 wxKeyEvent event( wxEVT_KEY_DOWN );
1124
1125 // take modifiers, cursor position, timestamp etc. from the last
1126 // key_press_event that was fed into Input Method:
1127 if (window->m_imData->lastKeyEvent)
1128 {
1129 wxFillOtherKeyEventFields(event,
1130 window, window->m_imData->lastKeyEvent);
1131 }
1132 else
1133 {
1134 event.SetEventObject( window );
1135 }
1136
1137 const wxString data(wxGTK_CONV_BACK_SYS(str));
1138 if( data.empty() )
1139 return;
1140
1141 bool ret = false;
1142
1143 // Implement OnCharHook by checking ancestor top level windows
1144 wxWindow *parent = window;
1145 while (parent && !parent->IsTopLevel())
1146 parent = parent->GetParent();
1147
1148 for( wxString::const_iterator pstr = data.begin(); pstr != data.end(); ++pstr )
1149 {
1150 #if wxUSE_UNICODE
1151 event.m_uniChar = *pstr;
1152 // Backward compatible for ISO-8859-1
1153 event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0;
1154 wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), event.m_uniChar);
1155 #else
1156 event.m_keyCode = (char)*pstr;
1157 #endif // wxUSE_UNICODE
1158
1159 // To conform to the docs we need to translate Ctrl-alpha
1160 // characters to values in the range 1-26.
1161 if ( event.ControlDown() &&
1162 ( wxIsLowerChar(*pstr) || wxIsUpperChar(*pstr) ))
1163 {
1164 if ( wxIsLowerChar(*pstr) )
1165 event.m_keyCode = *pstr - 'a' + 1;
1166 if ( wxIsUpperChar(*pstr) )
1167 event.m_keyCode = *pstr - 'A' + 1;
1168
1169 event.m_keyCode = *pstr - 'a' + 1;
1170 #if wxUSE_UNICODE
1171 event.m_uniChar = event.m_keyCode;
1172 #endif
1173 }
1174
1175 if (parent)
1176 {
1177 event.SetEventType( wxEVT_CHAR_HOOK );
1178 ret = parent->GetEventHandler()->ProcessEvent( event );
1179 }
1180
1181 if (!ret)
1182 {
1183 event.SetEventType(wxEVT_CHAR);
1184 ret = window->GetEventHandler()->ProcessEvent( event );
1185 }
1186 }
1187 }
1188 }
1189
1190
1191 //-----------------------------------------------------------------------------
1192 // "key_release_event" from any window
1193 //-----------------------------------------------------------------------------
1194
1195 extern "C" {
1196 static gboolean
1197 gtk_window_key_release_callback( GtkWidget *widget,
1198 GdkEventKey *gdk_event,
1199 wxWindowGTK *win )
1200 {
1201 DEBUG_MAIN_THREAD
1202
1203 if (!win->m_hasVMT)
1204 return FALSE;
1205
1206 if (g_blockEventsOnDrag)
1207 return FALSE;
1208
1209 wxKeyEvent event( wxEVT_KEY_UP );
1210 if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
1211 {
1212 // unknown key pressed, ignore (the event would be useless anyhow)
1213 return FALSE;
1214 }
1215
1216 return win->GTKProcessEvent(event);
1217 }
1218 }
1219
1220 // ============================================================================
1221 // the mouse events
1222 // ============================================================================
1223
1224 // ----------------------------------------------------------------------------
1225 // mouse event processing helpers
1226 // ----------------------------------------------------------------------------
1227
1228 // init wxMouseEvent with the info from GdkEventXXX struct
1229 template<typename T> void InitMouseEvent(wxWindowGTK *win,
1230 wxMouseEvent& event,
1231 T *gdk_event)
1232 {
1233 event.SetTimestamp( gdk_event->time );
1234 event.m_shiftDown = gdk_event->state & GDK_SHIFT_MASK;
1235 event.m_controlDown = gdk_event->state & GDK_CONTROL_MASK;
1236 event.m_altDown = gdk_event->state & GDK_MOD1_MASK;
1237 event.m_metaDown = gdk_event->state & GDK_MOD2_MASK;
1238 event.m_leftDown = gdk_event->state & GDK_BUTTON1_MASK;
1239 event.m_middleDown = gdk_event->state & GDK_BUTTON2_MASK;
1240 event.m_rightDown = gdk_event->state & GDK_BUTTON3_MASK;
1241 event.m_aux1Down = gdk_event->state & GDK_BUTTON4_MASK;
1242 event.m_aux2Down = gdk_event->state & GDK_BUTTON5_MASK;
1243
1244 wxPoint pt = win->GetClientAreaOrigin();
1245 event.m_x = (wxCoord)gdk_event->x - pt.x;
1246 event.m_y = (wxCoord)gdk_event->y - pt.y;
1247
1248 if ((win->m_wxwindow) && (win->GetLayoutDirection() == wxLayout_RightToLeft))
1249 {
1250 // origin in the upper right corner
1251 int window_width = gtk_pizza_get_rtl_offset( GTK_PIZZA(win->m_wxwindow) );
1252 event.m_x = window_width - event.m_x;
1253 }
1254
1255 event.SetEventObject( win );
1256 event.SetId( win->GetId() );
1257 event.SetTimestamp( gdk_event->time );
1258 }
1259
1260 static void AdjustEventButtonState(wxMouseEvent& event)
1261 {
1262 // GDK reports the old state of the button for a button press event, but
1263 // for compatibility with MSW and common sense we want m_leftDown be TRUE
1264 // for a LEFT_DOWN event, not FALSE, so we will invert
1265 // left/right/middleDown for the corresponding click events
1266
1267 if ((event.GetEventType() == wxEVT_LEFT_DOWN) ||
1268 (event.GetEventType() == wxEVT_LEFT_DCLICK) ||
1269 (event.GetEventType() == wxEVT_LEFT_UP))
1270 {
1271 event.m_leftDown = !event.m_leftDown;
1272 return;
1273 }
1274
1275 if ((event.GetEventType() == wxEVT_MIDDLE_DOWN) ||
1276 (event.GetEventType() == wxEVT_MIDDLE_DCLICK) ||
1277 (event.GetEventType() == wxEVT_MIDDLE_UP))
1278 {
1279 event.m_middleDown = !event.m_middleDown;
1280 return;
1281 }
1282
1283 if ((event.GetEventType() == wxEVT_RIGHT_DOWN) ||
1284 (event.GetEventType() == wxEVT_RIGHT_DCLICK) ||
1285 (event.GetEventType() == wxEVT_RIGHT_UP))
1286 {
1287 event.m_rightDown = !event.m_rightDown;
1288 return;
1289 }
1290 }
1291
1292 // find the window to send the mouse event too
1293 static
1294 wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
1295 {
1296 wxCoord xx = x;
1297 wxCoord yy = y;
1298
1299 if (win->m_wxwindow)
1300 {
1301 GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
1302 xx += gtk_pizza_get_xoffset( pizza );
1303 yy += gtk_pizza_get_yoffset( pizza );
1304 }
1305
1306 wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
1307 while (node)
1308 {
1309 wxWindowGTK *child = node->GetData();
1310
1311 node = node->GetNext();
1312 if (!child->IsShown())
1313 continue;
1314
1315 if (child->IsTransparentForMouse())
1316 {
1317 // wxStaticBox is transparent in the box itself
1318 int xx1 = child->m_x;
1319 int yy1 = child->m_y;
1320 int xx2 = child->m_x + child->m_width;
1321 int yy2 = child->m_y + child->m_height;
1322
1323 // left
1324 if (((xx >= xx1) && (xx <= xx1+10) && (yy >= yy1) && (yy <= yy2)) ||
1325 // right
1326 ((xx >= xx2-10) && (xx <= xx2) && (yy >= yy1) && (yy <= yy2)) ||
1327 // top
1328 ((xx >= xx1) && (xx <= xx2) && (yy >= yy1) && (yy <= yy1+10)) ||
1329 // bottom
1330 ((xx >= xx1) && (xx <= xx2) && (yy >= yy2-1) && (yy <= yy2)))
1331 {
1332 win = child;
1333 x -= child->m_x;
1334 y -= child->m_y;
1335 break;
1336 }
1337
1338 }
1339 else
1340 {
1341 if ((child->m_wxwindow == (GtkWidget*) NULL) &&
1342 (child->m_x <= xx) &&
1343 (child->m_y <= yy) &&
1344 (child->m_x+child->m_width >= xx) &&
1345 (child->m_y+child->m_height >= yy))
1346 {
1347 win = child;
1348 x -= child->m_x;
1349 y -= child->m_y;
1350 break;
1351 }
1352 }
1353 }
1354
1355 return win;
1356 }
1357
1358 // ----------------------------------------------------------------------------
1359 // common event handlers helpers
1360 // ----------------------------------------------------------------------------
1361
1362 bool wxWindowGTK::GTKProcessEvent(wxEvent& event) const
1363 {
1364 // nothing special at this level
1365 return GetEventHandler()->ProcessEvent(event);
1366 }
1367
1368 int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny *event) const
1369 {
1370 DEBUG_MAIN_THREAD
1371
1372 if (!m_hasVMT)
1373 return FALSE;
1374 if (g_blockEventsOnDrag)
1375 return TRUE;
1376 if (g_blockEventsOnScroll)
1377 return TRUE;
1378
1379 if (!GTKIsOwnWindow(event->window))
1380 return FALSE;
1381
1382 return -1;
1383 }
1384
1385 // overloads for all GDK event types we use here: we need to have this as
1386 // GdkEventXXX can't be implicitly cast to GdkEventAny even if it, in fact,
1387 // derives from it in the sense that the structs have the same layout
1388 #define wxDEFINE_COMMON_PROLOGUE_OVERLOAD(T) \
1389 static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win) \
1390 { \
1391 return win->GTKCallbackCommonPrologue((GdkEventAny *)event); \
1392 }
1393
1394 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventButton)
1395 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion)
1396 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing)
1397
1398 #undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD
1399
1400 #define wxCOMMON_CALLBACK_PROLOGUE(event, win) \
1401 const int rc = wxGtkCallbackCommonPrologue(event, win); \
1402 if ( rc != -1 ) \
1403 return rc
1404
1405 // send the wxChildFocusEvent and wxFocusEvent, common code of
1406 // gtk_window_focus_in_callback() and SetFocus()
1407 static bool DoSendFocusEvents(wxWindow *win)
1408 {
1409 // Notify the parent keeping track of focus for the kbd navigation
1410 // purposes that we got it.
1411 wxChildFocusEvent eventChildFocus(win);
1412 (void)win->GetEventHandler()->ProcessEvent(eventChildFocus);
1413
1414 wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
1415 eventFocus.SetEventObject(win);
1416
1417 return win->GetEventHandler()->ProcessEvent(eventFocus);
1418 }
1419
1420 // all event handlers must have C linkage as they're called from GTK+ C code
1421 extern "C"
1422 {
1423
1424 //-----------------------------------------------------------------------------
1425 // "button_press_event"
1426 //-----------------------------------------------------------------------------
1427
1428 static gboolean
1429 gtk_window_button_press_callback( GtkWidget *widget,
1430 GdkEventButton *gdk_event,
1431 wxWindowGTK *win )
1432 {
1433 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1434
1435 g_lastButtonNumber = gdk_event->button;
1436
1437 // GDK sends surplus button down events
1438 // before a double click event. We
1439 // need to filter these out.
1440 if ((gdk_event->type == GDK_BUTTON_PRESS) && (win->m_wxwindow))
1441 {
1442 GdkEvent *peek_event = gdk_event_peek();
1443 if (peek_event)
1444 {
1445 if ((peek_event->type == GDK_2BUTTON_PRESS) ||
1446 (peek_event->type == GDK_3BUTTON_PRESS))
1447 {
1448 gdk_event_free( peek_event );
1449 return TRUE;
1450 }
1451 else
1452 {
1453 gdk_event_free( peek_event );
1454 }
1455 }
1456 }
1457
1458 wxEventType event_type = wxEVT_NULL;
1459
1460 // GdkDisplay is a GTK+ 2.2.0 thing
1461 #if defined(__WXGTK20__) && GTK_CHECK_VERSION(2, 2, 0)
1462 if ( gdk_event->type == GDK_2BUTTON_PRESS &&
1463 !gtk_check_version(2,2,0) &&
1464 gdk_event->button >= 1 && gdk_event->button <= 3 )
1465 {
1466 // Reset GDK internal timestamp variables in order to disable GDK
1467 // triple click events. GDK will then next time believe no button has
1468 // been clicked just before, and send a normal button click event.
1469 GdkDisplay* display = gtk_widget_get_display (widget);
1470 display->button_click_time[1] = 0;
1471 display->button_click_time[0] = 0;
1472 }
1473 #endif // GTK 2+
1474
1475 if (gdk_event->button == 1)
1476 {
1477 // note that GDK generates triple click events which are not supported
1478 // by wxWidgets but still have to be passed to the app as otherwise
1479 // clicks would simply go missing
1480 switch (gdk_event->type)
1481 {
1482 // we shouldn't get triple clicks at all for GTK2 because we
1483 // suppress them artificially using the code above but we still
1484 // should map them to something for GTK1 and not just ignore them
1485 // as this would lose clicks
1486 case GDK_3BUTTON_PRESS: // we could also map this to DCLICK...
1487 case GDK_BUTTON_PRESS:
1488 event_type = wxEVT_LEFT_DOWN;
1489 break;
1490
1491 case GDK_2BUTTON_PRESS:
1492 event_type = wxEVT_LEFT_DCLICK;
1493 break;
1494
1495 default:
1496 // just to silence gcc warnings
1497 ;
1498 }
1499 }
1500 else if (gdk_event->button == 2)
1501 {
1502 switch (gdk_event->type)
1503 {
1504 case GDK_3BUTTON_PRESS:
1505 case GDK_BUTTON_PRESS:
1506 event_type = wxEVT_MIDDLE_DOWN;
1507 break;
1508
1509 case GDK_2BUTTON_PRESS:
1510 event_type = wxEVT_MIDDLE_DCLICK;
1511 break;
1512
1513 default:
1514 ;
1515 }
1516 }
1517 else if (gdk_event->button == 3)
1518 {
1519 switch (gdk_event->type)
1520 {
1521 case GDK_3BUTTON_PRESS:
1522 case GDK_BUTTON_PRESS:
1523 event_type = wxEVT_RIGHT_DOWN;
1524 break;
1525
1526 case GDK_2BUTTON_PRESS:
1527 event_type = wxEVT_RIGHT_DCLICK;
1528 break;
1529
1530 default:
1531 ;
1532 }
1533 }
1534
1535 if ( event_type == wxEVT_NULL )
1536 {
1537 // unknown mouse button or click type
1538 return FALSE;
1539 }
1540
1541 g_lastMouseEvent = (GdkEvent*) gdk_event;
1542
1543 wxMouseEvent event( event_type );
1544 InitMouseEvent( win, event, gdk_event );
1545
1546 AdjustEventButtonState(event);
1547
1548 // wxListBox actually gets mouse events from the item, so we need to give it
1549 // a chance to correct this
1550 win->FixUpMouseEvent(widget, event.m_x, event.m_y);
1551
1552 // find the correct window to send the event to: it may be a different one
1553 // from the one which got it at GTK+ level because some controls don't have
1554 // their own X window and thus cannot get any events.
1555 if ( !g_captureWindow )
1556 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1557
1558 // reset the event object and id in case win changed.
1559 event.SetEventObject( win );
1560 event.SetId( win->GetId() );
1561
1562 bool ret = win->GTKProcessEvent( event );
1563 g_lastMouseEvent = NULL;
1564 if ( ret )
1565 return TRUE;
1566
1567 if ((event_type == wxEVT_LEFT_DOWN) && !win->IsOfStandardClass() &&
1568 (g_focusWindow != win) /* && win->IsFocusable() */)
1569 {
1570 win->SetFocus();
1571 }
1572
1573 if (event_type == wxEVT_RIGHT_DOWN)
1574 {
1575 // generate a "context menu" event: this is similar to right mouse
1576 // click under many GUIs except that it is generated differently
1577 // (right up under MSW, ctrl-click under Mac, right down here) and
1578 //
1579 // (a) it's a command event and so is propagated to the parent
1580 // (b) under some ports it can be generated from kbd too
1581 // (c) it uses screen coords (because of (a))
1582 wxContextMenuEvent evtCtx(
1583 wxEVT_CONTEXT_MENU,
1584 win->GetId(),
1585 win->ClientToScreen(event.GetPosition()));
1586 evtCtx.SetEventObject(win);
1587 return win->GTKProcessEvent(evtCtx);
1588 }
1589
1590 return FALSE;
1591 }
1592
1593 //-----------------------------------------------------------------------------
1594 // "button_release_event"
1595 //-----------------------------------------------------------------------------
1596
1597 static gboolean
1598 gtk_window_button_release_callback( GtkWidget *widget,
1599 GdkEventButton *gdk_event,
1600 wxWindowGTK *win )
1601 {
1602 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1603
1604 g_lastButtonNumber = 0;
1605
1606 wxEventType event_type = wxEVT_NULL;
1607
1608 switch (gdk_event->button)
1609 {
1610 case 1:
1611 event_type = wxEVT_LEFT_UP;
1612 break;
1613
1614 case 2:
1615 event_type = wxEVT_MIDDLE_UP;
1616 break;
1617
1618 case 3:
1619 event_type = wxEVT_RIGHT_UP;
1620 break;
1621
1622 default:
1623 // unknown button, don't process
1624 return FALSE;
1625 }
1626
1627 g_lastMouseEvent = (GdkEvent*) gdk_event;
1628
1629 wxMouseEvent event( event_type );
1630 InitMouseEvent( win, event, gdk_event );
1631
1632 AdjustEventButtonState(event);
1633
1634 // same wxListBox hack as above
1635 win->FixUpMouseEvent(widget, event.m_x, event.m_y);
1636
1637 if ( !g_captureWindow )
1638 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1639
1640 // reset the event object and id in case win changed.
1641 event.SetEventObject( win );
1642 event.SetId( win->GetId() );
1643
1644 bool ret = win->GTKProcessEvent(event);
1645
1646 g_lastMouseEvent = NULL;
1647
1648 return ret;
1649 }
1650
1651 //-----------------------------------------------------------------------------
1652 // "motion_notify_event"
1653 //-----------------------------------------------------------------------------
1654
1655 static gboolean
1656 gtk_window_motion_notify_callback( GtkWidget *widget,
1657 GdkEventMotion *gdk_event,
1658 wxWindowGTK *win )
1659 {
1660 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1661
1662 if (gdk_event->is_hint)
1663 {
1664 int x = 0;
1665 int y = 0;
1666 GdkModifierType state;
1667 gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
1668 gdk_event->x = x;
1669 gdk_event->y = y;
1670 }
1671
1672 g_lastMouseEvent = (GdkEvent*) gdk_event;
1673
1674 wxMouseEvent event( wxEVT_MOTION );
1675 InitMouseEvent(win, event, gdk_event);
1676
1677 if ( g_captureWindow )
1678 {
1679 // synthesise a mouse enter or leave event if needed
1680 GdkWindow *winUnderMouse = gdk_window_at_pointer(NULL, NULL);
1681 // This seems to be necessary and actually been added to
1682 // GDK itself in version 2.0.X
1683 gdk_flush();
1684
1685 bool hasMouse = winUnderMouse == gdk_event->window;
1686 if ( hasMouse != g_captureWindowHasMouse )
1687 {
1688 // the mouse changed window
1689 g_captureWindowHasMouse = hasMouse;
1690
1691 wxMouseEvent eventM(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW
1692 : wxEVT_LEAVE_WINDOW);
1693 InitMouseEvent(win, eventM, gdk_event);
1694 eventM.SetEventObject(win);
1695 win->GTKProcessEvent(eventM);
1696 }
1697 }
1698 else // no capture
1699 {
1700 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1701
1702 // reset the event object and id in case win changed.
1703 event.SetEventObject( win );
1704 event.SetId( win->GetId() );
1705 }
1706
1707 if ( !g_captureWindow )
1708 {
1709 wxSetCursorEvent cevent( event.m_x, event.m_y );
1710 if (win->GTKProcessEvent( cevent ))
1711 {
1712 win->SetCursor( cevent.GetCursor() );
1713 }
1714 }
1715
1716 bool ret = win->GTKProcessEvent(event);
1717
1718 g_lastMouseEvent = NULL;
1719
1720 return ret;
1721 }
1722
1723 //-----------------------------------------------------------------------------
1724 // "scroll_event" (mouse wheel event)
1725 //-----------------------------------------------------------------------------
1726
1727 static gboolean
1728 window_scroll_event(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
1729 {
1730 DEBUG_MAIN_THREAD
1731
1732 if (gdk_event->direction != GDK_SCROLL_UP &&
1733 gdk_event->direction != GDK_SCROLL_DOWN)
1734 {
1735 return false;
1736 }
1737
1738 wxMouseEvent event(wxEVT_MOUSEWHEEL);
1739 InitMouseEvent(win, event, gdk_event);
1740 event.m_linesPerAction = 3;
1741 event.m_wheelDelta = 120;
1742 if (gdk_event->direction == GDK_SCROLL_UP)
1743 event.m_wheelRotation = 120;
1744 else
1745 event.m_wheelRotation = -120;
1746
1747 return win->GTKProcessEvent(event);
1748 }
1749
1750 //-----------------------------------------------------------------------------
1751 // "popup-menu"
1752 //-----------------------------------------------------------------------------
1753
1754 static gboolean wxgtk_window_popup_menu_callback(GtkWidget*, wxWindowGTK* win)
1755 {
1756 wxContextMenuEvent event(wxEVT_CONTEXT_MENU, win->GetId(), wxPoint(-1, -1));
1757 event.SetEventObject(win);
1758 return win->GTKProcessEvent(event);
1759 }
1760
1761 //-----------------------------------------------------------------------------
1762 // "focus_in_event"
1763 //-----------------------------------------------------------------------------
1764
1765 static gboolean
1766 gtk_window_focus_in_callback( GtkWidget *widget,
1767 GdkEventFocus *WXUNUSED(event),
1768 wxWindow *win )
1769 {
1770 DEBUG_MAIN_THREAD
1771
1772 if (win->m_imData)
1773 gtk_im_context_focus_in(win->m_imData->context);
1774
1775 g_focusWindowLast =
1776 g_focusWindow = win;
1777
1778 wxLogTrace(TRACE_FOCUS,
1779 _T("%s: focus in"), win->GetName().c_str());
1780
1781 #if wxUSE_CARET
1782 // caret needs to be informed about focus change
1783 wxCaret *caret = win->GetCaret();
1784 if ( caret )
1785 {
1786 caret->OnSetFocus();
1787 }
1788 #endif // wxUSE_CARET
1789
1790 gboolean ret = FALSE;
1791
1792 // does the window itself think that it has the focus?
1793 if ( !win->m_hasFocus )
1794 {
1795 // not yet, notify it
1796 win->m_hasFocus = true;
1797
1798 (void)DoSendFocusEvents(win);
1799
1800 ret = TRUE;
1801 }
1802
1803 // Disable default focus handling for custom windows
1804 // since the default GTK+ handler issues a repaint
1805 if (win->m_wxwindow)
1806 return ret;
1807
1808 return FALSE;
1809 }
1810
1811 //-----------------------------------------------------------------------------
1812 // "focus_out_event"
1813 //-----------------------------------------------------------------------------
1814
1815 static gboolean
1816 gtk_window_focus_out_callback( GtkWidget *widget,
1817 GdkEventFocus *gdk_event,
1818 wxWindowGTK *win )
1819 {
1820 DEBUG_MAIN_THREAD
1821
1822 if (win->m_imData)
1823 gtk_im_context_focus_out(win->m_imData->context);
1824
1825 wxLogTrace( TRACE_FOCUS,
1826 _T("%s: focus out"), win->GetName().c_str() );
1827
1828
1829 wxWindowGTK *winFocus = wxFindFocusedChild(win);
1830 if ( winFocus )
1831 win = winFocus;
1832
1833 g_focusWindow = (wxWindowGTK *)NULL;
1834
1835 #if wxUSE_CARET
1836 // caret needs to be informed about focus change
1837 wxCaret *caret = win->GetCaret();
1838 if ( caret )
1839 {
1840 caret->OnKillFocus();
1841 }
1842 #endif // wxUSE_CARET
1843
1844 // don't send the window a kill focus event if it thinks that it doesn't
1845 // have focus already
1846 if ( win->m_hasFocus )
1847 {
1848 // the event handler might delete the window when it loses focus, so
1849 // check whether this is a custom window before calling it
1850 const bool has_wxwindow = win->m_wxwindow != NULL;
1851
1852 win->m_hasFocus = false;
1853
1854 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
1855 event.SetEventObject( win );
1856
1857 (void)win->GTKProcessEvent( event );
1858
1859 // Disable default focus handling for custom windows
1860 // since the default GTK+ handler issues a repaint
1861 if ( has_wxwindow )
1862 {
1863 return TRUE;
1864 }
1865 }
1866
1867 // continue with normal processing
1868 return FALSE;
1869 }
1870
1871 static gboolean
1872 wx_window_focus_callback(GtkWidget *widget,
1873 GtkDirectionType direction,
1874 wxWindowGTK *win)
1875 {
1876 // the default handler for focus signal in GtkPizza (or, rather, in
1877 // GtkScrolledWindow from which GtkPizza inherits this behaviour) sets
1878 // focus to the window itself even if it doesn't accept focus, i.e. has no
1879 // GTK_CAN_FOCUS in its style -- work around this by forcibly preventing
1880 // the signal from reaching gtk_scrolled_window_focus() if we don't have
1881 // any children which might accept focus (we know we don't accept the focus
1882 // ourselves as this signal is only connected in this case)
1883 if ( win->GetChildren().empty() )
1884 g_signal_stop_emission_by_name(widget, "focus");
1885
1886 // we didn't change the focus
1887 return FALSE;
1888 }
1889
1890 //-----------------------------------------------------------------------------
1891 // "enter_notify_event"
1892 //-----------------------------------------------------------------------------
1893
1894 static gboolean
1895 gtk_window_enter_callback( GtkWidget *widget,
1896 GdkEventCrossing *gdk_event,
1897 wxWindowGTK *win )
1898 {
1899 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1900
1901 // Event was emitted after a grab
1902 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
1903
1904 int x = 0;
1905 int y = 0;
1906 GdkModifierType state = (GdkModifierType)0;
1907
1908 gdk_window_get_pointer( widget->window, &x, &y, &state );
1909
1910 wxMouseEvent event( wxEVT_ENTER_WINDOW );
1911 InitMouseEvent(win, event, gdk_event);
1912 wxPoint pt = win->GetClientAreaOrigin();
1913 event.m_x = x + pt.x;
1914 event.m_y = y + pt.y;
1915
1916 if ( !g_captureWindow )
1917 {
1918 wxSetCursorEvent cevent( event.m_x, event.m_y );
1919 if (win->GTKProcessEvent( cevent ))
1920 {
1921 win->SetCursor( cevent.GetCursor() );
1922 }
1923 }
1924
1925 return win->GTKProcessEvent(event);
1926 }
1927
1928 //-----------------------------------------------------------------------------
1929 // "leave_notify_event"
1930 //-----------------------------------------------------------------------------
1931
1932 static gboolean
1933 gtk_window_leave_callback( GtkWidget *widget,
1934 GdkEventCrossing *gdk_event,
1935 wxWindowGTK *win )
1936 {
1937 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1938
1939 // Event was emitted after an ungrab
1940 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
1941
1942 wxMouseEvent event( wxEVT_LEAVE_WINDOW );
1943
1944 int x = 0;
1945 int y = 0;
1946 GdkModifierType state = (GdkModifierType)0;
1947
1948 gdk_window_get_pointer( widget->window, &x, &y, &state );
1949
1950 InitMouseEvent(win, event, gdk_event);
1951
1952 return win->GTKProcessEvent(event);
1953 }
1954
1955 //-----------------------------------------------------------------------------
1956 // "value_changed" from scrollbar
1957 //-----------------------------------------------------------------------------
1958
1959 static void
1960 gtk_scrollbar_value_changed(GtkRange* range, wxWindow* win)
1961 {
1962 wxEventType eventType = win->GetScrollEventType(range);
1963 if (eventType != wxEVT_NULL)
1964 {
1965 // Convert scroll event type to scrollwin event type
1966 eventType += wxEVT_SCROLLWIN_TOP - wxEVT_SCROLL_TOP;
1967
1968 // find the scrollbar which generated the event
1969 wxWindowGTK::ScrollDir dir = win->ScrollDirFromRange(range);
1970
1971 // generate the corresponding wx event
1972 const int orient = wxWindow::OrientFromScrollDir(dir);
1973 wxScrollWinEvent event(eventType, win->GetScrollPos(orient), orient);
1974 event.SetEventObject(win);
1975
1976 win->GTKProcessEvent(event);
1977 }
1978 }
1979
1980 //-----------------------------------------------------------------------------
1981 // "button_press_event" from scrollbar
1982 //-----------------------------------------------------------------------------
1983
1984 static gboolean
1985 gtk_scrollbar_button_press_event(GtkRange*, GdkEventButton*, wxWindow* win)
1986 {
1987 DEBUG_MAIN_THREAD
1988
1989 g_blockEventsOnScroll = true;
1990 win->m_mouseButtonDown = true;
1991
1992 return false;
1993 }
1994
1995 //-----------------------------------------------------------------------------
1996 // "event_after" from scrollbar
1997 //-----------------------------------------------------------------------------
1998
1999 static void
2000 gtk_scrollbar_event_after(GtkRange* range, GdkEvent* event, wxWindow* win)
2001 {
2002 if (event->type == GDK_BUTTON_RELEASE)
2003 {
2004 g_signal_handlers_block_by_func(range, (void*)gtk_scrollbar_event_after, win);
2005
2006 const int orient = wxWindow::OrientFromScrollDir(
2007 win->ScrollDirFromRange(range));
2008 wxScrollWinEvent event(wxEVT_SCROLLWIN_THUMBRELEASE, win->GetScrollPos(orient), orient);
2009 event.SetEventObject(win);
2010 win->GTKProcessEvent(event);
2011 }
2012 }
2013
2014 //-----------------------------------------------------------------------------
2015 // "button_release_event" from scrollbar
2016 //-----------------------------------------------------------------------------
2017
2018 static gboolean
2019 gtk_scrollbar_button_release_event(GtkRange* range, GdkEventButton*, wxWindow* win)
2020 {
2021 DEBUG_MAIN_THREAD
2022
2023 g_blockEventsOnScroll = false;
2024 win->m_mouseButtonDown = false;
2025 // If thumb tracking
2026 if (win->m_isScrolling)
2027 {
2028 win->m_isScrolling = false;
2029 // Hook up handler to send thumb release event after this emission is finished.
2030 // To allow setting scroll position from event handler, sending event must
2031 // be deferred until after the GtkRange handler for this signal has run
2032 g_signal_handlers_unblock_by_func(range, (void*)gtk_scrollbar_event_after, win);
2033 }
2034
2035 return false;
2036 }
2037
2038 //-----------------------------------------------------------------------------
2039 // "realize" from m_widget
2040 //-----------------------------------------------------------------------------
2041
2042 static void
2043 gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win )
2044 {
2045 DEBUG_MAIN_THREAD
2046
2047 if (win->m_imData)
2048 {
2049 GtkPizza *pizza = GTK_PIZZA( m_widget );
2050 gtk_im_context_set_client_window( win->m_imData->context,
2051 pizza->bin_window );
2052 }
2053
2054 // We cannot set colours and fonts before the widget
2055 // been realized, so we do this directly after realization
2056 // or otherwise in idle time
2057
2058 if (win->m_needsStyleChange)
2059 {
2060 win->SetBackgroundStyle(win->GetBackgroundStyle());
2061 win->m_needsStyleChange = false;
2062 }
2063
2064 wxWindowCreateEvent event( win );
2065 event.SetEventObject( win );
2066 win->GTKProcessEvent( event );
2067 }
2068
2069 //-----------------------------------------------------------------------------
2070 // "size_allocate"
2071 //-----------------------------------------------------------------------------
2072
2073 static
2074 void gtk_window_size_callback( GtkWidget *WXUNUSED(widget),
2075 GtkAllocation *alloc,
2076 wxWindow *win )
2077 {
2078 int client_width = 0;
2079 int client_height = 0;
2080 win->GetClientSize( &client_width, &client_height );
2081 if ((client_width == win->m_oldClientWidth) && (client_height == win->m_oldClientHeight))
2082 return;
2083
2084 if ( !client_width && !client_height )
2085 {
2086 // the window is currently unmapped, don't generate size events
2087 return;
2088 }
2089
2090 win->m_oldClientWidth = client_width;
2091 win->m_oldClientHeight = client_height;
2092
2093 if (!win->m_nativeSizeEvent)
2094 {
2095 wxSizeEvent event( win->GetSize(), win->GetId() );
2096 event.SetEventObject( win );
2097 win->GTKProcessEvent( event );
2098 }
2099 }
2100
2101 //-----------------------------------------------------------------------------
2102 // "grab_broken"
2103 //-----------------------------------------------------------------------------
2104
2105 #ifdef __WXGTK210__
2106 static void
2107 gtk_window_grab_broken( GtkWidget *m_widget,
2108 GdkEventGrabBroken *event,
2109 wxWindow *win )
2110 {
2111 // Mouse capture has been lost involuntarily, notify the application
2112 if( !event->keyboard && win && wxWindow::GetCapture() == win )
2113 {
2114 wxMouseCaptureLostEvent evt( win->GetId() );
2115 evt.SetEventObject( win );
2116 win->GetEventHandler()->ProcessEvent( evt );
2117 }
2118 }
2119 #endif
2120
2121
2122 } // extern "C"
2123
2124 // ----------------------------------------------------------------------------
2125 // this wxWindowBase function is implemented here (in platform-specific file)
2126 // because it is static and so couldn't be made virtual
2127 // ----------------------------------------------------------------------------
2128
2129 wxWindow *wxWindowBase::DoFindFocus()
2130 {
2131 // the cast is necessary when we compile in wxUniversal mode
2132 return (wxWindow *)g_focusWindow;
2133 }
2134
2135 //-----------------------------------------------------------------------------
2136 // InsertChild for wxWindowGTK.
2137 //-----------------------------------------------------------------------------
2138
2139 /* Callback for wxWindowGTK. This very strange beast has to be used because
2140 * C++ has no virtual methods in a constructor. We have to emulate a
2141 * virtual function here as wxNotebook requires a different way to insert
2142 * a child in it. I had opted for creating a wxNotebookPage window class
2143 * which would have made this superfluous (such in the MDI window system),
2144 * but no-one was listening to me... */
2145
2146 static void wxInsertChildInWindow( wxWindowGTK* parent, wxWindowGTK* child )
2147 {
2148 /* the window might have been scrolled already, do we
2149 have to adapt the position */
2150 GtkPizza *pizza = GTK_PIZZA(parent->m_wxwindow);
2151 child->m_x += gtk_pizza_get_xoffset( pizza );
2152 child->m_y += gtk_pizza_get_yoffset( pizza );
2153
2154 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
2155 child->m_widget,
2156 child->m_x,
2157 child->m_y,
2158 child->m_width,
2159 child->m_height );
2160 }
2161
2162 //-----------------------------------------------------------------------------
2163 // global functions
2164 //-----------------------------------------------------------------------------
2165
2166 wxWindow *wxGetActiveWindow()
2167 {
2168 return wxWindow::FindFocus();
2169 }
2170
2171
2172 wxMouseState wxGetMouseState()
2173 {
2174 wxMouseState ms;
2175
2176 gint x;
2177 gint y;
2178 GdkModifierType mask;
2179
2180 gdk_window_get_pointer(NULL, &x, &y, &mask);
2181
2182 ms.SetX(x);
2183 ms.SetY(y);
2184 ms.SetLeftDown(mask & GDK_BUTTON1_MASK);
2185 ms.SetMiddleDown(mask & GDK_BUTTON2_MASK);
2186 ms.SetRightDown(mask & GDK_BUTTON3_MASK);
2187 ms.SetAux1Down(mask & GDK_BUTTON4_MASK);
2188 ms.SetAux2Down(mask & GDK_BUTTON5_MASK);
2189
2190 ms.SetControlDown(mask & GDK_CONTROL_MASK);
2191 ms.SetShiftDown(mask & GDK_SHIFT_MASK);
2192 ms.SetAltDown(mask & GDK_MOD1_MASK);
2193 ms.SetMetaDown(mask & GDK_MOD2_MASK);
2194
2195 return ms;
2196 }
2197
2198 //-----------------------------------------------------------------------------
2199 // wxWindowGTK
2200 //-----------------------------------------------------------------------------
2201
2202 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2203 // method
2204 #ifdef __WXUNIVERSAL__
2205 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK, wxWindowBase)
2206 #else // __WXGTK__
2207 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
2208 #endif // __WXUNIVERSAL__/__WXGTK__
2209
2210 void wxWindowGTK::Init()
2211 {
2212 // GTK specific
2213 m_widget = (GtkWidget *) NULL;
2214 m_wxwindow = (GtkWidget *) NULL;
2215 m_focusWidget = (GtkWidget *) NULL;
2216
2217 // position/size
2218 m_x = 0;
2219 m_y = 0;
2220 m_width = 0;
2221 m_height = 0;
2222
2223 m_hasVMT = false;
2224 m_isBeingDeleted = false;
2225
2226 m_showOnIdle= false;
2227
2228 m_noExpose = false;
2229 m_nativeSizeEvent = false;
2230
2231 m_hasScrolling = false;
2232 m_isScrolling = false;
2233 m_mouseButtonDown = false;
2234
2235 // initialize scrolling stuff
2236 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2237 {
2238 m_scrollBar[dir] = NULL;
2239 m_scrollPos[dir] = 0;
2240 }
2241
2242 m_oldClientWidth =
2243 m_oldClientHeight = 0;
2244
2245 m_resizing = false;
2246
2247 m_insertCallback = wxInsertChildInWindow;
2248
2249 m_hasFocus = false;
2250
2251 m_clipPaintRegion = false;
2252
2253 m_needsStyleChange = false;
2254
2255 m_cursor = *wxSTANDARD_CURSOR;
2256
2257 m_imData = NULL;
2258 m_dirtyTabOrder = false;
2259 }
2260
2261 wxWindowGTK::wxWindowGTK()
2262 {
2263 Init();
2264 }
2265
2266 wxWindowGTK::wxWindowGTK( wxWindow *parent,
2267 wxWindowID id,
2268 const wxPoint &pos,
2269 const wxSize &size,
2270 long style,
2271 const wxString &name )
2272 {
2273 Init();
2274
2275 Create( parent, id, pos, size, style, name );
2276 }
2277
2278 bool wxWindowGTK::Create( wxWindow *parent,
2279 wxWindowID id,
2280 const wxPoint &pos,
2281 const wxSize &size,
2282 long style,
2283 const wxString &name )
2284 {
2285 if (!PreCreation( parent, pos, size ) ||
2286 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
2287 {
2288 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2289 return false;
2290 }
2291
2292 if (!HasFlag(wxHSCROLL) && !HasFlag(wxVSCROLL))
2293 {
2294 m_wxwindow = gtk_pizza_new_no_scroll();
2295
2296 #ifndef __WXUNIVERSAL__
2297 if (HasFlag(wxSIMPLE_BORDER))
2298 gtk_container_set_border_width((GtkContainer*)m_wxwindow, 1);
2299 else if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
2300 gtk_container_set_border_width((GtkContainer*)m_wxwindow, 2);
2301 #endif // __WXUNIVERSAL__
2302
2303 m_widget = m_wxwindow;
2304 }
2305 else
2306 {
2307 m_wxwindow = gtk_pizza_new();
2308
2309 #ifndef __WXUNIVERSAL__
2310 if (HasFlag(wxSIMPLE_BORDER))
2311 gtk_container_set_border_width((GtkContainer*)m_wxwindow, 1);
2312 else if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
2313 gtk_container_set_border_width((GtkContainer*)m_wxwindow, 2);
2314 #endif // __WXUNIVERSAL__
2315
2316 m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
2317
2318 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
2319
2320 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
2321 scroll_class->scrollbar_spacing = 0;
2322
2323 // There is a conflict with default bindings at GTK+
2324 // level between scrolled windows and notebooks both of which want to use
2325 // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
2326 // direction and notebooks for changing pages -- we decide that if we don't
2327 // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
2328 // means we can get working keyboard navigation in notebooks
2329 if ( !HasFlag(wxHSCROLL) )
2330 {
2331 GtkBindingSet *
2332 bindings = gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget));
2333 if ( bindings )
2334 {
2335 gtk_binding_entry_remove(bindings, GDK_Page_Up, GDK_CONTROL_MASK);
2336 gtk_binding_entry_remove(bindings, GDK_Page_Down, GDK_CONTROL_MASK);
2337 }
2338 }
2339
2340 if (HasFlag(wxALWAYS_SHOW_SB))
2341 {
2342 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS );
2343
2344 scrolledWindow->hscrollbar_visible = TRUE;
2345 scrolledWindow->vscrollbar_visible = TRUE;
2346 }
2347 else
2348 {
2349 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
2350 }
2351
2352 m_scrollBar[ScrollDir_Horz] = GTK_RANGE(scrolledWindow->hscrollbar);
2353 m_scrollBar[ScrollDir_Vert] = GTK_RANGE(scrolledWindow->vscrollbar);
2354 if (GetLayoutDirection() == wxLayout_RightToLeft)
2355 gtk_range_set_inverted( m_scrollBar[ScrollDir_Horz], TRUE );
2356
2357 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
2358
2359 // connect various scroll-related events
2360 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2361 {
2362 // these handlers block mouse events to any window during scrolling
2363 // such as motion events and prevent GTK and wxWidgets from fighting
2364 // over where the slider should be
2365 g_signal_connect(m_scrollBar[dir], "button_press_event",
2366 G_CALLBACK(gtk_scrollbar_button_press_event), this);
2367 g_signal_connect(m_scrollBar[dir], "button_release_event",
2368 G_CALLBACK(gtk_scrollbar_button_release_event), this);
2369
2370 gulong handler_id = g_signal_connect(m_scrollBar[dir], "event_after",
2371 G_CALLBACK(gtk_scrollbar_event_after), this);
2372 g_signal_handler_block(m_scrollBar[dir], handler_id);
2373
2374 // these handlers get notified when scrollbar slider moves
2375 g_signal_connect_after(m_scrollBar[dir], "value_changed",
2376 G_CALLBACK(gtk_scrollbar_value_changed), this);
2377 }
2378
2379 gtk_widget_show( m_wxwindow );
2380 }
2381
2382 if (m_parent)
2383 m_parent->DoAddChild( this );
2384
2385 m_focusWidget = m_wxwindow;
2386
2387 PostCreation();
2388
2389 return true;
2390 }
2391
2392 wxWindowGTK::~wxWindowGTK()
2393 {
2394 SendDestroyEvent();
2395
2396 if (g_focusWindow == this)
2397 g_focusWindow = NULL;
2398
2399 if ( g_delayedFocus == this )
2400 g_delayedFocus = NULL;
2401
2402 m_isBeingDeleted = true;
2403 m_hasVMT = false;
2404
2405 // destroy children before destroying this window itself
2406 DestroyChildren();
2407
2408 // unhook focus handlers to prevent stray events being
2409 // propagated to this (soon to be) dead object
2410 if (m_focusWidget != NULL)
2411 {
2412 g_signal_handlers_disconnect_by_func (m_focusWidget,
2413 (gpointer) gtk_window_focus_in_callback,
2414 this);
2415 g_signal_handlers_disconnect_by_func (m_focusWidget,
2416 (gpointer) gtk_window_focus_out_callback,
2417 this);
2418 }
2419
2420 if (m_widget)
2421 Show( false );
2422
2423 // delete before the widgets to avoid a crash on solaris
2424 delete m_imData;
2425
2426 if (m_wxwindow && (m_wxwindow != m_widget))
2427 {
2428 gtk_widget_destroy( m_wxwindow );
2429 m_wxwindow = (GtkWidget*) NULL;
2430 }
2431
2432 if (m_widget)
2433 {
2434 gtk_widget_destroy( m_widget );
2435 m_widget = (GtkWidget*) NULL;
2436 }
2437 }
2438
2439 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
2440 {
2441 if ( GTKNeedsParent() )
2442 {
2443 wxCHECK_MSG( parent, false, wxT("Must have non-NULL parent") );
2444 }
2445
2446 // Use either the given size, or the default if -1 is given.
2447 // See wxWindowBase for these functions.
2448 m_width = WidthDefault(size.x) ;
2449 m_height = HeightDefault(size.y);
2450
2451 m_x = (int)pos.x;
2452 m_y = (int)pos.y;
2453
2454 return true;
2455 }
2456
2457 void wxWindowGTK::PostCreation()
2458 {
2459 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2460
2461 if (m_wxwindow)
2462 {
2463 if (!m_noExpose)
2464 {
2465 // these get reported to wxWidgets -> wxPaintEvent
2466
2467 g_signal_connect (m_wxwindow, "expose_event",
2468 G_CALLBACK (gtk_window_expose_callback), this);
2469
2470 if (GetLayoutDirection() == wxLayout_LeftToRight)
2471 gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), HasFlag( wxFULL_REPAINT_ON_RESIZE ) );
2472 }
2473
2474 // Create input method handler
2475 m_imData = new wxGtkIMData;
2476
2477 // Cannot handle drawing preedited text yet
2478 gtk_im_context_set_use_preedit( m_imData->context, FALSE );
2479
2480 g_signal_connect (m_imData->context, "commit",
2481 G_CALLBACK (gtk_wxwindow_commit_cb), this);
2482
2483 // these are called when the "sunken" or "raised" borders are drawn
2484 g_signal_connect (m_widget, "expose_event",
2485 G_CALLBACK (gtk_window_own_expose_callback), this);
2486 }
2487
2488 // focus handling
2489
2490 if (!GTK_IS_WINDOW(m_widget))
2491 {
2492 if (m_focusWidget == NULL)
2493 m_focusWidget = m_widget;
2494
2495 if (m_wxwindow)
2496 {
2497 g_signal_connect (m_focusWidget, "focus_in_event",
2498 G_CALLBACK (gtk_window_focus_in_callback), this);
2499 g_signal_connect (m_focusWidget, "focus_out_event",
2500 G_CALLBACK (gtk_window_focus_out_callback), this);
2501 }
2502 else
2503 {
2504 g_signal_connect_after (m_focusWidget, "focus_in_event",
2505 G_CALLBACK (gtk_window_focus_in_callback), this);
2506 g_signal_connect_after (m_focusWidget, "focus_out_event",
2507 G_CALLBACK (gtk_window_focus_out_callback), this);
2508 }
2509 }
2510
2511 if ( !AcceptsFocusFromKeyboard() )
2512 {
2513 SetCanFocus(false);
2514
2515 g_signal_connect(m_widget, "focus",
2516 G_CALLBACK(wx_window_focus_callback), this);
2517 }
2518
2519 // connect to the various key and mouse handlers
2520
2521 GtkWidget *connect_widget = GetConnectWidget();
2522
2523 ConnectWidget( connect_widget );
2524
2525 /* We cannot set colours, fonts and cursors before the widget has
2526 been realized, so we do this directly after realization */
2527 g_signal_connect (connect_widget, "realize",
2528 G_CALLBACK (gtk_window_realized_callback), this);
2529
2530 if (m_wxwindow)
2531 {
2532 // Catch native resize events
2533 g_signal_connect (m_wxwindow, "size_allocate",
2534 G_CALLBACK (gtk_window_size_callback), this);
2535 #ifdef __WXGTK210__
2536 if (!gtk_check_version(2,8,0))
2537 {
2538 // Make sure we can notify the app when mouse capture is lost
2539 g_signal_connect (m_wxwindow, "grab_broken_event",
2540 G_CALLBACK (gtk_window_grab_broken), this);
2541 }
2542 #endif
2543 }
2544
2545 if ( connect_widget != m_wxwindow )
2546 {
2547 #ifdef __WXGTK210__
2548 if (!gtk_check_version(2,8,0))
2549 {
2550 // Make sure we can notify app code when mouse capture is lost
2551 g_signal_connect (connect_widget, "grab_broken_event",
2552 G_CALLBACK (gtk_window_grab_broken), this);
2553 }
2554 #endif
2555 }
2556
2557 #if wxUSE_COMBOBOX
2558 if (GTK_IS_COMBO(m_widget))
2559 {
2560 GtkCombo *gcombo = GTK_COMBO(m_widget);
2561
2562 g_signal_connect (gcombo->entry, "size_request",
2563 G_CALLBACK (wxgtk_combo_size_request_callback),
2564 this);
2565 } else
2566 #endif // wxUSE_COMBOBOX
2567 #ifdef GTK_IS_FILE_CHOOSER_BUTTON
2568 if (!gtk_check_version(2,6,0) && GTK_IS_FILE_CHOOSER_BUTTON(m_widget))
2569 {
2570 // If we connect to the "size_request" signal of a GtkFileChooserButton
2571 // then that control won't be sized properly when placed inside sizers
2572 // (this can be tested removing this elseif and running XRC or WIDGETS samples)
2573 // FIXME: what should be done here ?
2574 } else
2575 #endif
2576 if ( !IsTopLevel() ) // top level windows use their own callback
2577 {
2578 // This is needed if we want to add our windows into native
2579 // GTK controls, such as the toolbar. With this callback, the
2580 // toolbar gets to know the correct size (the one set by the
2581 // programmer). Sadly, it misbehaves for wxComboBox.
2582 g_signal_connect (m_widget, "size_request",
2583 G_CALLBACK (wxgtk_window_size_request_callback),
2584 this);
2585 }
2586
2587 InheritAttributes();
2588
2589 m_hasVMT = true;
2590
2591 SetLayoutDirection(wxLayout_Default);
2592
2593 // unless the window was created initially hidden (i.e. Hide() had been
2594 // called before Create()), we should show it at GTK+ level as well
2595 if ( IsShown() )
2596 gtk_widget_show( m_widget );
2597 }
2598
2599 void wxWindowGTK::ConnectWidget( GtkWidget *widget )
2600 {
2601 g_signal_connect (widget, "key_press_event",
2602 G_CALLBACK (gtk_window_key_press_callback), this);
2603 g_signal_connect (widget, "key_release_event",
2604 G_CALLBACK (gtk_window_key_release_callback), this);
2605 g_signal_connect (widget, "button_press_event",
2606 G_CALLBACK (gtk_window_button_press_callback), this);
2607 g_signal_connect (widget, "button_release_event",
2608 G_CALLBACK (gtk_window_button_release_callback), this);
2609 g_signal_connect (widget, "motion_notify_event",
2610 G_CALLBACK (gtk_window_motion_notify_callback), this);
2611 g_signal_connect (widget, "scroll_event",
2612 G_CALLBACK (window_scroll_event), this);
2613 g_signal_connect (widget, "popup_menu",
2614 G_CALLBACK (wxgtk_window_popup_menu_callback), this);
2615 g_signal_connect (widget, "enter_notify_event",
2616 G_CALLBACK (gtk_window_enter_callback), this);
2617 g_signal_connect (widget, "leave_notify_event",
2618 G_CALLBACK (gtk_window_leave_callback), this);
2619 }
2620
2621 bool wxWindowGTK::Destroy()
2622 {
2623 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2624
2625 m_hasVMT = false;
2626
2627 return wxWindowBase::Destroy();
2628 }
2629
2630 void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
2631 {
2632 // inform the parent to perform the move
2633 gtk_pizza_set_size( GTK_PIZZA(m_parent->m_wxwindow), m_widget, x, y, width, height );
2634
2635 }
2636
2637 void wxWindowGTK::ConstrainSize()
2638 {
2639 #ifdef __WXGPE__
2640 // GPE's window manager doesn't like size hints at all, esp. when the user
2641 // has to use the virtual keyboard, so don't constrain size there
2642 if (!IsTopLevel())
2643 #endif
2644 {
2645 const wxSize minSize = GetMinSize();
2646 const wxSize maxSize = GetMaxSize();
2647 if (minSize.x > 0 && m_width < minSize.x) m_width = minSize.x;
2648 if (minSize.y > 0 && m_height < minSize.y) m_height = minSize.y;
2649 if (maxSize.x > 0 && m_width > maxSize.x) m_width = maxSize.x;
2650 if (maxSize.y > 0 && m_height > maxSize.y) m_height = maxSize.y;
2651 }
2652 }
2653
2654 void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
2655 {
2656 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2657 wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") );
2658
2659 if (m_resizing) return; /* I don't like recursions */
2660 m_resizing = true;
2661
2662 int currentX, currentY;
2663 GetPosition(&currentX, &currentY);
2664 if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2665 x = currentX;
2666 if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2667 y = currentY;
2668 AdjustForParentClientOrigin(x, y, sizeFlags);
2669
2670 // calculate the best size if we should auto size the window
2671 if ( ((sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1) ||
2672 ((sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1) )
2673 {
2674 const wxSize sizeBest = GetBestSize();
2675 if ( (sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1 )
2676 width = sizeBest.x;
2677 if ( (sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1 )
2678 height = sizeBest.y;
2679 }
2680
2681 if (width != -1)
2682 m_width = width;
2683 if (height != -1)
2684 m_height = height;
2685
2686 ConstrainSize();
2687
2688 #if wxUSE_TOOLBAR_NATIVE
2689 if (wxDynamicCast(GetParent(), wxToolBar))
2690 {
2691 // don't take the x,y values, they're wrong because toolbar sets them
2692 GtkWidget *widget = m_widget;
2693 gtk_widget_set_size_request (widget, m_width, m_height);
2694 }
2695 else
2696 #endif
2697 if (m_parent->m_wxwindow == NULL) // i.e. wxNotebook
2698 {
2699 // don't set the size for children of wxNotebook, just take the values.
2700 m_x = x;
2701 m_y = y;
2702 m_width = width;
2703 m_height = height;
2704 }
2705 else
2706 {
2707 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
2708 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
2709 {
2710 if (x != -1) m_x = x + gtk_pizza_get_xoffset( pizza );
2711 if (y != -1) m_y = y + gtk_pizza_get_yoffset( pizza );
2712 }
2713 else
2714 {
2715 m_x = x + gtk_pizza_get_xoffset( pizza );
2716 m_y = y + gtk_pizza_get_yoffset( pizza );
2717 }
2718
2719 int left_border = 0;
2720 int right_border = 0;
2721 int top_border = 0;
2722 int bottom_border = 0;
2723
2724 /* the default button has a border around it */
2725 if (GTK_WIDGET_CAN_DEFAULT(m_widget))
2726 {
2727 GtkBorder *default_border = NULL;
2728 gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
2729 if (default_border)
2730 {
2731 left_border += default_border->left;
2732 right_border += default_border->right;
2733 top_border += default_border->top;
2734 bottom_border += default_border->bottom;
2735 gtk_border_free( default_border );
2736 }
2737 }
2738
2739 DoMoveWindow( m_x - left_border,
2740 m_y - top_border,
2741 m_width+left_border+right_border,
2742 m_height+top_border+bottom_border );
2743 }
2744
2745 if (m_hasScrolling)
2746 {
2747 /* Sometimes the client area changes size without the
2748 whole windows's size changing, but if the whole
2749 windows's size doesn't change, no wxSizeEvent will
2750 normally be sent. Here we add an extra test if
2751 the client test has been changed and this will
2752 be used then. */
2753 GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
2754 }
2755
2756 /*
2757 wxPrintf( "OnSize sent from " );
2758 if (GetClassInfo() && GetClassInfo()->GetClassName())
2759 wxPrintf( GetClassInfo()->GetClassName() );
2760 wxPrintf( " %d %d %d %d\n", (int)m_x, (int)m_y, (int)m_width, (int)m_height );
2761 */
2762
2763 if (!m_nativeSizeEvent)
2764 {
2765 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
2766 event.SetEventObject( this );
2767 GetEventHandler()->ProcessEvent( event );
2768 }
2769
2770 m_resizing = false;
2771 }
2772
2773 bool wxWindowGTK::GtkShowFromOnIdle()
2774 {
2775 if (IsShown() && m_showOnIdle && !GTK_WIDGET_VISIBLE (m_widget))
2776 {
2777 GtkAllocation alloc;
2778 alloc.x = m_x;
2779 alloc.y = m_y;
2780 alloc.width = m_width;
2781 alloc.height = m_height;
2782 gtk_widget_size_allocate( m_widget, &alloc );
2783 gtk_widget_show( m_widget );
2784 wxShowEvent eventShow(GetId(), true);
2785 eventShow.SetEventObject(this);
2786 GetEventHandler()->ProcessEvent(eventShow);
2787 m_showOnIdle = false;
2788 return true;
2789 }
2790
2791 return false;
2792 }
2793
2794 void wxWindowGTK::OnInternalIdle()
2795 {
2796 // Check if we have to show window now
2797 if (GtkShowFromOnIdle()) return;
2798
2799 if ( m_dirtyTabOrder )
2800 {
2801 m_dirtyTabOrder = false;
2802 RealizeTabOrder();
2803 }
2804
2805 // Update style if the window was not yet realized
2806 // and SetBackgroundStyle(wxBG_STYLE_CUSTOM) was called
2807 if (m_needsStyleChange)
2808 {
2809 SetBackgroundStyle(GetBackgroundStyle());
2810 m_needsStyleChange = false;
2811 }
2812
2813 wxCursor cursor = m_cursor;
2814 if (g_globalCursor.Ok()) cursor = g_globalCursor;
2815
2816 if (cursor.Ok())
2817 {
2818 /* I now set the cursor anew in every OnInternalIdle call
2819 as setting the cursor in a parent window also effects the
2820 windows above so that checking for the current cursor is
2821 not possible. */
2822
2823 if (m_wxwindow && (m_wxwindow != m_widget))
2824 {
2825 GdkWindow *window = GTK_PIZZA(m_wxwindow)->bin_window;
2826 if (window)
2827 gdk_window_set_cursor( window, cursor.GetCursor() );
2828
2829 if (!g_globalCursor.Ok())
2830 cursor = *wxSTANDARD_CURSOR;
2831
2832 window = m_widget->window;
2833 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
2834 gdk_window_set_cursor( window, cursor.GetCursor() );
2835
2836 }
2837 else if ( m_widget )
2838 {
2839 GdkWindow *window = m_widget->window;
2840 if ( window && !GTK_WIDGET_NO_WINDOW(m_widget) )
2841 gdk_window_set_cursor( window, cursor.GetCursor() );
2842 }
2843 }
2844
2845 if (wxUpdateUIEvent::CanUpdate(this))
2846 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
2847 }
2848
2849 void wxWindowGTK::DoGetSize( int *width, int *height ) const
2850 {
2851 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2852
2853 if (width) (*width) = m_width;
2854 if (height) (*height) = m_height;
2855 }
2856
2857 void wxWindowGTK::DoSetClientSize( int width, int height )
2858 {
2859 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2860
2861 const wxSize size = GetSize();
2862 const wxSize clientSize = GetClientSize();
2863 SetSize(width + (size.x - clientSize.x), height + (size.y - clientSize.y));
2864 }
2865
2866 void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
2867 {
2868 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2869
2870 int w = m_width;
2871 int h = m_height;
2872
2873 if (m_wxwindow)
2874 {
2875 int dw = 0;
2876 int dh = 0;
2877
2878 if (m_hasScrolling)
2879 GetScrollbarWidth(m_widget, dw, dh);
2880
2881 const int border = GTK_CONTAINER(m_wxwindow)->border_width;
2882 dw += 2 * border;
2883 dh += 2 * border;
2884
2885 w -= dw;
2886 h -= dh;
2887 if (w < 0)
2888 w = 0;
2889 if (h < 0)
2890 h = 0;
2891 }
2892
2893 if (width) *width = w;
2894 if (height) *height = h;
2895 }
2896
2897 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
2898 {
2899 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2900
2901 int dx = 0;
2902 int dy = 0;
2903 if (m_parent && m_parent->m_wxwindow)
2904 {
2905 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
2906 dx = gtk_pizza_get_xoffset( pizza );
2907 dy = gtk_pizza_get_yoffset( pizza );
2908 }
2909
2910 if (m_x == -1 && m_y == -1)
2911 {
2912 GdkWindow *source = (GdkWindow *) NULL;
2913 if (m_wxwindow)
2914 source = GTK_PIZZA(m_wxwindow)->bin_window;
2915 else
2916 source = m_widget->window;
2917
2918 if (source)
2919 {
2920 int org_x = 0;
2921 int org_y = 0;
2922 gdk_window_get_origin( source, &org_x, &org_y );
2923
2924 if (m_parent)
2925 m_parent->ScreenToClient(&org_x, &org_y);
2926
2927 wx_const_cast(wxWindowGTK*, this)->m_x = org_x;
2928 wx_const_cast(wxWindowGTK*, this)->m_y = org_y;
2929 }
2930 }
2931
2932 if (x) (*x) = m_x - dx;
2933 if (y) (*y) = m_y - dy;
2934 }
2935
2936 void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
2937 {
2938 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2939
2940 if (!m_widget->window) return;
2941
2942 GdkWindow *source = (GdkWindow *) NULL;
2943 if (m_wxwindow)
2944 source = GTK_PIZZA(m_wxwindow)->bin_window;
2945 else
2946 source = m_widget->window;
2947
2948 int org_x = 0;
2949 int org_y = 0;
2950 gdk_window_get_origin( source, &org_x, &org_y );
2951
2952 if (!m_wxwindow)
2953 {
2954 if (GTK_WIDGET_NO_WINDOW (m_widget))
2955 {
2956 org_x += m_widget->allocation.x;
2957 org_y += m_widget->allocation.y;
2958 }
2959 }
2960
2961
2962 if (x)
2963 {
2964 if (GetLayoutDirection() == wxLayout_RightToLeft)
2965 *x = (GetClientSize().x - *x) + org_x;
2966 else
2967 *x += org_x;
2968 }
2969
2970 if (y) *y += org_y;
2971 }
2972
2973 void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
2974 {
2975 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2976
2977 if (!m_widget->window) return;
2978
2979 GdkWindow *source = (GdkWindow *) NULL;
2980 if (m_wxwindow)
2981 source = GTK_PIZZA(m_wxwindow)->bin_window;
2982 else
2983 source = m_widget->window;
2984
2985 int org_x = 0;
2986 int org_y = 0;
2987 gdk_window_get_origin( source, &org_x, &org_y );
2988
2989 if (!m_wxwindow)
2990 {
2991 if (GTK_WIDGET_NO_WINDOW (m_widget))
2992 {
2993 org_x += m_widget->allocation.x;
2994 org_y += m_widget->allocation.y;
2995 }
2996 }
2997
2998 if (x)
2999 {
3000 if (GetLayoutDirection() == wxLayout_RightToLeft)
3001 *x = (GetClientSize().x - *x) - org_x;
3002 else
3003 *x -= org_x;
3004 }
3005 if (y) *y -= org_y;
3006 }
3007
3008 bool wxWindowGTK::Show( bool show )
3009 {
3010 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3011
3012 if (!wxWindowBase::Show(show))
3013 {
3014 // nothing to do
3015 return false;
3016 }
3017
3018 if (show)
3019 {
3020 if (!m_showOnIdle)
3021 {
3022 gtk_widget_show( m_widget );
3023 wxShowEvent eventShow(GetId(), show);
3024 eventShow.SetEventObject(this);
3025 GetEventHandler()->ProcessEvent(eventShow);
3026 }
3027 }
3028 else
3029 {
3030 gtk_widget_hide( m_widget );
3031 wxShowEvent eventShow(GetId(), show);
3032 eventShow.SetEventObject(this);
3033 GetEventHandler()->ProcessEvent(eventShow);
3034 }
3035
3036 return true;
3037 }
3038
3039 void wxWindowGTK::DoEnable( bool enable )
3040 {
3041 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3042
3043 gtk_widget_set_sensitive( m_widget, enable );
3044 if (m_wxwindow && (m_wxwindow != m_widget))
3045 gtk_widget_set_sensitive( m_wxwindow, enable );
3046 }
3047
3048 int wxWindowGTK::GetCharHeight() const
3049 {
3050 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
3051
3052 wxFont font = GetFont();
3053 wxCHECK_MSG( font.Ok(), 12, wxT("invalid font") );
3054
3055 PangoContext *context = NULL;
3056 if (m_widget)
3057 context = gtk_widget_get_pango_context( m_widget );
3058
3059 if (!context)
3060 return 0;
3061
3062 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3063 PangoLayout *layout = pango_layout_new(context);
3064 pango_layout_set_font_description(layout, desc);
3065 pango_layout_set_text(layout, "H", 1);
3066 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3067
3068 PangoRectangle rect;
3069 pango_layout_line_get_extents(line, NULL, &rect);
3070
3071 g_object_unref (layout);
3072
3073 return (int) PANGO_PIXELS(rect.height);
3074 }
3075
3076 int wxWindowGTK::GetCharWidth() const
3077 {
3078 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
3079
3080 wxFont font = GetFont();
3081 wxCHECK_MSG( font.Ok(), 8, wxT("invalid font") );
3082
3083 PangoContext *context = NULL;
3084 if (m_widget)
3085 context = gtk_widget_get_pango_context( m_widget );
3086
3087 if (!context)
3088 return 0;
3089
3090 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3091 PangoLayout *layout = pango_layout_new(context);
3092 pango_layout_set_font_description(layout, desc);
3093 pango_layout_set_text(layout, "g", 1);
3094 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3095
3096 PangoRectangle rect;
3097 pango_layout_line_get_extents(line, NULL, &rect);
3098
3099 g_object_unref (layout);
3100
3101 return (int) PANGO_PIXELS(rect.width);
3102 }
3103
3104 void wxWindowGTK::GetTextExtent( const wxString& string,
3105 int *x,
3106 int *y,
3107 int *descent,
3108 int *externalLeading,
3109 const wxFont *theFont ) const
3110 {
3111 wxFont fontToUse = theFont ? *theFont : GetFont();
3112
3113 wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") );
3114
3115 if (string.empty())
3116 {
3117 if (x) (*x) = 0;
3118 if (y) (*y) = 0;
3119 return;
3120 }
3121
3122 PangoContext *context = NULL;
3123 if (m_widget)
3124 context = gtk_widget_get_pango_context( m_widget );
3125
3126 if (!context)
3127 {
3128 if (x) (*x) = 0;
3129 if (y) (*y) = 0;
3130 return;
3131 }
3132
3133 PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
3134 PangoLayout *layout = pango_layout_new(context);
3135 pango_layout_set_font_description(layout, desc);
3136 {
3137 const wxCharBuffer data = wxGTK_CONV( string );
3138 if ( data )
3139 pango_layout_set_text(layout, data, strlen(data));
3140 }
3141
3142 PangoRectangle rect;
3143 pango_layout_get_extents(layout, NULL, &rect);
3144
3145 if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width);
3146 if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height);
3147 if (descent)
3148 {
3149 PangoLayoutIter *iter = pango_layout_get_iter(layout);
3150 int baseline = pango_layout_iter_get_baseline(iter);
3151 pango_layout_iter_free(iter);
3152 *descent = *y - PANGO_PIXELS(baseline);
3153 }
3154 if (externalLeading) (*externalLeading) = 0; // ??
3155
3156 g_object_unref (layout);
3157 }
3158
3159 bool wxWindowGTK::GTKSetDelayedFocusIfNeeded()
3160 {
3161 if ( g_delayedFocus == this )
3162 {
3163 if ( GTK_WIDGET_REALIZED(m_widget) )
3164 {
3165 gtk_widget_grab_focus(m_widget);
3166 g_delayedFocus = NULL;
3167
3168 return true;
3169 }
3170 }
3171
3172 return false;
3173 }
3174
3175 void wxWindowGTK::SetFocusIgnoringChildren()
3176 {
3177 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3178 if ( m_hasFocus )
3179 {
3180 // don't do anything if we already have focus
3181 return;
3182 }
3183
3184 if (m_wxwindow)
3185 {
3186 if (!GTK_WIDGET_CAN_FOCUS(m_wxwindow))
3187 GTK_WIDGET_SET_FLAGS(m_wxwindow, GTK_CAN_FOCUS);
3188 }
3189
3190 wxWindowGTK::SetFocus();
3191 }
3192
3193 void wxWindowGTK::SetFocus()
3194 {
3195 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3196 if ( m_hasFocus )
3197 {
3198 // don't do anything if we already have focus
3199 return;
3200 }
3201
3202 if (m_wxwindow)
3203 {
3204 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
3205 {
3206 gtk_widget_grab_focus (m_wxwindow);
3207 }
3208 }
3209 else if (m_widget)
3210 {
3211 if (GTK_IS_CONTAINER(m_widget))
3212 {
3213 #if wxUSE_RADIOBTN
3214 if (IsKindOf(CLASSINFO(wxRadioButton)))
3215 {
3216 gtk_widget_grab_focus (m_widget);
3217 return;
3218 }
3219 #endif // wxUSE_RADIOBTN
3220
3221 gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
3222 }
3223 else
3224 if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
3225 {
3226
3227 if (!GTK_WIDGET_REALIZED(m_widget))
3228 {
3229 // we can't set the focus to the widget now so we remember that
3230 // it should be focused and will do it later, during the idle
3231 // time, as soon as we can
3232 wxLogTrace(TRACE_FOCUS,
3233 _T("Delaying setting focus to %s(%s)"),
3234 GetClassInfo()->GetClassName(), GetLabel().c_str());
3235
3236 g_delayedFocus = this;
3237 }
3238 else
3239 {
3240 wxLogTrace(TRACE_FOCUS,
3241 _T("Setting focus to %s(%s)"),
3242 GetClassInfo()->GetClassName(), GetLabel().c_str());
3243
3244 gtk_widget_grab_focus (m_widget);
3245 }
3246 }
3247 else
3248 {
3249 wxLogTrace(TRACE_FOCUS,
3250 _T("Can't set focus to %s(%s)"),
3251 GetClassInfo()->GetClassName(), GetLabel().c_str());
3252 }
3253 }
3254 }
3255
3256 void wxWindowGTK::SetCanFocus(bool canFocus)
3257 {
3258 if ( canFocus )
3259 GTK_WIDGET_SET_FLAGS(m_widget, GTK_CAN_FOCUS);
3260 else
3261 GTK_WIDGET_UNSET_FLAGS(m_widget, GTK_CAN_FOCUS);
3262
3263 if ( m_wxwindow && (m_widget != m_wxwindow) )
3264 {
3265 if ( canFocus )
3266 GTK_WIDGET_SET_FLAGS(m_wxwindow, GTK_CAN_FOCUS);
3267 else
3268 GTK_WIDGET_UNSET_FLAGS(m_wxwindow, GTK_CAN_FOCUS);
3269 }
3270 }
3271
3272 bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
3273 {
3274 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3275
3276 wxWindowGTK *oldParent = m_parent,
3277 *newParent = (wxWindowGTK *)newParentBase;
3278
3279 wxASSERT( GTK_IS_WIDGET(m_widget) );
3280
3281 if ( !wxWindowBase::Reparent(newParent) )
3282 return false;
3283
3284 wxASSERT( GTK_IS_WIDGET(m_widget) );
3285
3286 /* prevent GTK from deleting the widget arbitrarily */
3287 gtk_widget_ref( m_widget );
3288
3289 if (oldParent)
3290 {
3291 gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
3292 }
3293
3294 wxASSERT( GTK_IS_WIDGET(m_widget) );
3295
3296 if (newParent)
3297 {
3298 if (GTK_WIDGET_VISIBLE (newParent->m_widget))
3299 {
3300 m_showOnIdle = true;
3301 gtk_widget_hide( m_widget );
3302 }
3303
3304 /* insert GTK representation */
3305 (*(newParent->m_insertCallback))(newParent, this);
3306 }
3307
3308 /* reverse: prevent GTK from deleting the widget arbitrarily */
3309 gtk_widget_unref( m_widget );
3310
3311 SetLayoutDirection(wxLayout_Default);
3312
3313 return true;
3314 }
3315
3316 void wxWindowGTK::DoAddChild(wxWindowGTK *child)
3317 {
3318 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
3319 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
3320
3321 /* add to list */
3322 AddChild( child );
3323
3324 /* insert GTK representation */
3325 (*m_insertCallback)(this, child);
3326 }
3327
3328 void wxWindowGTK::AddChild(wxWindowBase *child)
3329 {
3330 wxWindowBase::AddChild(child);
3331 m_dirtyTabOrder = true;
3332 wxTheApp->WakeUpIdle();
3333 }
3334
3335 void wxWindowGTK::RemoveChild(wxWindowBase *child)
3336 {
3337 wxWindowBase::RemoveChild(child);
3338 m_dirtyTabOrder = true;
3339 wxTheApp->WakeUpIdle();
3340 }
3341
3342 /* static */
3343 wxLayoutDirection wxWindowGTK::GTKGetLayout(GtkWidget *widget)
3344 {
3345 return gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL
3346 ? wxLayout_RightToLeft
3347 : wxLayout_LeftToRight;
3348 }
3349
3350 /* static */
3351 void wxWindowGTK::GTKSetLayout(GtkWidget *widget, wxLayoutDirection dir)
3352 {
3353 wxASSERT_MSG( dir != wxLayout_Default, _T("invalid layout direction") );
3354
3355 gtk_widget_set_direction(widget,
3356 dir == wxLayout_RightToLeft ? GTK_TEXT_DIR_RTL
3357 : GTK_TEXT_DIR_LTR);
3358 }
3359
3360 wxLayoutDirection wxWindowGTK::GetLayoutDirection() const
3361 {
3362 return GTKGetLayout(m_widget);
3363 }
3364
3365 void wxWindowGTK::SetLayoutDirection(wxLayoutDirection dir)
3366 {
3367 if ( dir == wxLayout_Default )
3368 {
3369 const wxWindow *const parent = GetParent();
3370 if ( parent )
3371 {
3372 // inherit layout from parent.
3373 dir = parent->GetLayoutDirection();
3374 }
3375 else // no parent, use global default layout
3376 {
3377 dir = wxTheApp->GetLayoutDirection();
3378 }
3379 }
3380
3381 if ( dir == wxLayout_Default )
3382 return;
3383
3384 GTKSetLayout(m_widget, dir);
3385
3386 if (m_wxwindow && (m_wxwindow != m_widget))
3387 GTKSetLayout(m_wxwindow, dir);
3388 }
3389
3390 wxCoord
3391 wxWindowGTK::AdjustForLayoutDirection(wxCoord x,
3392 wxCoord WXUNUSED(width),
3393 wxCoord WXUNUSED(widthTotal)) const
3394 {
3395 // We now mirrors the coordinates of RTL windows in GtkPizza
3396 return x;
3397 }
3398
3399 void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, MoveKind move)
3400 {
3401 wxWindowBase::DoMoveInTabOrder(win, move);
3402 m_dirtyTabOrder = true;
3403 wxTheApp->WakeUpIdle();
3404 }
3405
3406 bool wxWindowGTK::DoNavigateIn(int flags)
3407 {
3408 if ( flags & wxNavigationKeyEvent::WinChange )
3409 {
3410 wxFAIL_MSG( _T("not implemented") );
3411
3412 return false;
3413 }
3414 else // navigate inside the container
3415 {
3416 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
3417 wxCHECK_MSG( parent, false, _T("every window must have a TLW parent") );
3418
3419 GtkDirectionType dir;
3420 dir = flags & wxNavigationKeyEvent::IsForward ? GTK_DIR_TAB_FORWARD
3421 : GTK_DIR_TAB_BACKWARD;
3422
3423 gboolean rc;
3424 g_signal_emit_by_name(parent->m_widget, "focus", dir, &rc);
3425
3426 return rc == TRUE;
3427 }
3428 }
3429
3430 bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3431 {
3432 // none needed by default
3433 return false;
3434 }
3435
3436 void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget* WXUNUSED(w))
3437 {
3438 // nothing to do by default since none is needed
3439 }
3440
3441 void wxWindowGTK::RealizeTabOrder()
3442 {
3443 if (m_wxwindow)
3444 {
3445 if ( !m_children.empty() )
3446 {
3447 // we don't only construct the correct focus chain but also use
3448 // this opportunity to update the mnemonic widgets for the widgets
3449 // that need them
3450
3451 GList *chain = NULL;
3452 wxWindowGTK* mnemonicWindow = NULL;
3453
3454 for ( wxWindowList::const_iterator i = m_children.begin();
3455 i != m_children.end();
3456 ++i )
3457 {
3458 wxWindowGTK *win = *i;
3459
3460 if ( mnemonicWindow )
3461 {
3462 if ( win->AcceptsFocusFromKeyboard() )
3463 {
3464 // wxComboBox et al. needs to focus on on a different
3465 // widget than m_widget, so if the main widget isn't
3466 // focusable try the connect widget
3467 GtkWidget* w = win->m_widget;
3468 if ( !GTK_WIDGET_CAN_FOCUS(w) )
3469 {
3470 w = win->GetConnectWidget();
3471 if ( !GTK_WIDGET_CAN_FOCUS(w) )
3472 w = NULL;
3473 }
3474
3475 if ( w )
3476 {
3477 mnemonicWindow->GTKWidgetDoSetMnemonic(w);
3478 mnemonicWindow = NULL;
3479 }
3480 }
3481 }
3482 else if ( win->GTKWidgetNeedsMnemonic() )
3483 {
3484 mnemonicWindow = win;
3485 }
3486
3487 chain = g_list_prepend(chain, win->m_widget);
3488 }
3489
3490 chain = g_list_reverse(chain);
3491
3492 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain);
3493 g_list_free(chain);
3494 }
3495 else // no children
3496 {
3497 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow));
3498 }
3499 }
3500 }
3501
3502 void wxWindowGTK::Raise()
3503 {
3504 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3505
3506 if (m_wxwindow && m_wxwindow->window)
3507 {
3508 gdk_window_raise( m_wxwindow->window );
3509 }
3510 else if (m_widget->window)
3511 {
3512 gdk_window_raise( m_widget->window );
3513 }
3514 }
3515
3516 void wxWindowGTK::Lower()
3517 {
3518 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3519
3520 if (m_wxwindow && m_wxwindow->window)
3521 {
3522 gdk_window_lower( m_wxwindow->window );
3523 }
3524 else if (m_widget->window)
3525 {
3526 gdk_window_lower( m_widget->window );
3527 }
3528 }
3529
3530 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
3531 {
3532 if ( !wxWindowBase::SetCursor(cursor.Ok() ? cursor : *wxSTANDARD_CURSOR) )
3533 return false;
3534
3535 GTKUpdateCursor();
3536
3537 return true;
3538 }
3539
3540 void wxWindowGTK::GTKUpdateCursor()
3541 {
3542 wxCursor cursor(g_globalCursor.Ok() ? g_globalCursor : GetCursor());
3543 if ( cursor.Ok() )
3544 {
3545 wxArrayGdkWindows windowsThis;
3546 GdkWindow * const winThis = GTKGetWindow(windowsThis);
3547 if ( winThis )
3548 {
3549 gdk_window_set_cursor(winThis, cursor.GetCursor());
3550 }
3551 else
3552 {
3553 const size_t count = windowsThis.size();
3554 for ( size_t n = 0; n < count; n++ )
3555 {
3556 GdkWindow *win = windowsThis[n];
3557 if ( !win )
3558 {
3559 wxFAIL_MSG(_T("NULL window returned by GTKGetWindow()?"));
3560 continue;
3561 }
3562
3563 gdk_window_set_cursor(win, cursor.GetCursor());
3564 }
3565 }
3566 }
3567 }
3568
3569 void wxWindowGTK::WarpPointer( int x, int y )
3570 {
3571 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3572
3573 // We provide this function ourselves as it is
3574 // missing in GDK (top of this file).
3575
3576 GdkWindow *window = (GdkWindow*) NULL;
3577 if (m_wxwindow)
3578 window = GTK_PIZZA(m_wxwindow)->bin_window;
3579 else
3580 window = GetConnectWidget()->window;
3581
3582 if (window)
3583 gdk_window_warp_pointer( window, x, y );
3584 }
3585
3586 wxWindowGTK::ScrollDir wxWindowGTK::ScrollDirFromRange(GtkRange *range) const
3587 {
3588 // find the scrollbar which generated the event
3589 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
3590 {
3591 if ( range == m_scrollBar[dir] )
3592 return (ScrollDir)dir;
3593 }
3594
3595 wxFAIL_MSG( _T("event from unknown scrollbar received") );
3596
3597 return ScrollDir_Max;
3598 }
3599
3600 bool wxWindowGTK::DoScrollByUnits(ScrollDir dir, ScrollUnit unit, int units)
3601 {
3602 bool changed = false;
3603 GtkRange* range = m_scrollBar[dir];
3604 if ( range && units )
3605 {
3606 GtkAdjustment* adj = range->adjustment;
3607 gdouble inc = unit == ScrollUnit_Line ? adj->step_increment
3608 : adj->page_increment;
3609
3610 const int posOld = int(adj->value + 0.5);
3611 gtk_range_set_value(range, posOld + units*inc);
3612
3613 changed = int(adj->value + 0.5) != posOld;
3614 }
3615
3616 return changed;
3617 }
3618
3619 bool wxWindowGTK::ScrollLines(int lines)
3620 {
3621 return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Line, lines);
3622 }
3623
3624 bool wxWindowGTK::ScrollPages(int pages)
3625 {
3626 return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Page, pages);
3627 }
3628
3629 void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
3630 {
3631 if (!m_widget)
3632 return;
3633 if (!m_widget->window)
3634 return;
3635
3636 if (m_wxwindow)
3637 {
3638 if (!GTK_PIZZA(m_wxwindow)->bin_window) return;
3639
3640 GdkRectangle gdk_rect,
3641 *p;
3642 if (rect)
3643 {
3644 gdk_rect.x = rect->x;
3645 gdk_rect.y = rect->y;
3646 gdk_rect.width = rect->width;
3647 gdk_rect.height = rect->height;
3648 if (GetLayoutDirection() == wxLayout_RightToLeft)
3649 gdk_rect.x = GetClientSize().x - gdk_rect.x - gdk_rect.width;
3650
3651 p = &gdk_rect;
3652 }
3653 else // invalidate everything
3654 {
3655 p = NULL;
3656 }
3657
3658 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, p, TRUE );
3659 }
3660 }
3661
3662 void wxWindowGTK::Update()
3663 {
3664 GtkUpdate();
3665
3666 // when we call Update() we really want to update the window immediately on
3667 // screen, even if it means flushing the entire queue and hence slowing down
3668 // everything -- but it should still be done, it's just that Update() should
3669 // be called very rarely
3670 gdk_flush();
3671 }
3672
3673 void wxWindowGTK::GtkUpdate()
3674 {
3675 if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window)
3676 gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE );
3677 if (m_widget && m_widget->window && (m_wxwindow != m_widget))
3678 gdk_window_process_updates( m_widget->window, FALSE );
3679
3680 // for consistency with other platforms (and also because it's convenient
3681 // to be able to update an entire TLW by calling Update() only once), we
3682 // should also update all our children here
3683 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
3684 node;
3685 node = node->GetNext() )
3686 {
3687 node->GetData()->GtkUpdate();
3688 }
3689 }
3690
3691 bool wxWindowGTK::DoIsExposed( int x, int y ) const
3692 {
3693 return m_updateRegion.Contains(x, y) != wxOutRegion;
3694 }
3695
3696
3697 bool wxWindowGTK::DoIsExposed( int x, int y, int w, int h ) const
3698 {
3699 if (GetLayoutDirection() == wxLayout_RightToLeft)
3700 return m_updateRegion.Contains(x-w, y, w, h) != wxOutRegion;
3701 else
3702 return m_updateRegion.Contains(x, y, w, h) != wxOutRegion;
3703 }
3704
3705 void wxWindowGTK::GtkSendPaintEvents()
3706 {
3707 if (!m_wxwindow)
3708 {
3709 m_updateRegion.Clear();
3710 return;
3711 }
3712
3713 // Clip to paint region in wxClientDC
3714 m_clipPaintRegion = true;
3715
3716 m_nativeUpdateRegion = m_updateRegion;
3717
3718 if (GetLayoutDirection() == wxLayout_RightToLeft)
3719 {
3720 // Transform m_updateRegion under RTL
3721 m_updateRegion.Clear();
3722
3723 gint width;
3724 gdk_window_get_geometry( GTK_PIZZA(m_wxwindow)->bin_window,
3725 NULL, NULL, &width, NULL, NULL );
3726
3727 wxRegionIterator upd( m_nativeUpdateRegion );
3728 while (upd)
3729 {
3730 wxRect rect;
3731 rect.x = upd.GetX();
3732 rect.y = upd.GetY();
3733 rect.width = upd.GetWidth();
3734 rect.height = upd.GetHeight();
3735
3736 rect.x = width - rect.x - rect.width;
3737 m_updateRegion.Union( rect );
3738
3739 ++upd;
3740 }
3741 }
3742
3743 // widget to draw on
3744 GtkPizza *pizza = GTK_PIZZA (m_wxwindow);
3745
3746 if (GetThemeEnabled() && (GetBackgroundStyle() == wxBG_STYLE_SYSTEM))
3747 {
3748 // find ancestor from which to steal background
3749 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
3750 if (!parent)
3751 parent = (wxWindow*)this;
3752
3753 if (GTK_WIDGET_MAPPED(parent->m_widget))
3754 {
3755 wxRegionIterator upd( m_nativeUpdateRegion );
3756 while (upd)
3757 {
3758 GdkRectangle rect;
3759 rect.x = upd.GetX();
3760 rect.y = upd.GetY();
3761 rect.width = upd.GetWidth();
3762 rect.height = upd.GetHeight();
3763
3764 gtk_paint_flat_box( parent->m_widget->style,
3765 pizza->bin_window,
3766 (GtkStateType)GTK_WIDGET_STATE(m_wxwindow),
3767 GTK_SHADOW_NONE,
3768 &rect,
3769 parent->m_widget,
3770 (char *)"base",
3771 0, 0, -1, -1 );
3772
3773 ++upd;
3774 }
3775 }
3776 }
3777 else
3778 {
3779 wxWindowDC dc( (wxWindow*)this );
3780 dc.SetClippingRegion( m_updateRegion );
3781
3782 wxEraseEvent erase_event( GetId(), &dc );
3783 erase_event.SetEventObject( this );
3784
3785 GetEventHandler()->ProcessEvent(erase_event);
3786 }
3787
3788 wxNcPaintEvent nc_paint_event( GetId() );
3789 nc_paint_event.SetEventObject( this );
3790 GetEventHandler()->ProcessEvent( nc_paint_event );
3791
3792
3793 wxPaintEvent paint_event( GetId() );
3794 paint_event.SetEventObject( this );
3795 GetEventHandler()->ProcessEvent( paint_event );
3796
3797 m_clipPaintRegion = false;
3798
3799 m_updateRegion.Clear();
3800 m_nativeUpdateRegion.Clear();
3801 }
3802
3803 void wxWindowGTK::SetDoubleBuffered( bool on )
3804 {
3805 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3806
3807 if ( m_wxwindow )
3808 gtk_widget_set_double_buffered( m_wxwindow, on );
3809 }
3810
3811 bool wxWindowGTK::IsDoubleBuffered() const
3812 {
3813 return GTK_WIDGET_DOUBLE_BUFFERED( m_wxwindow );
3814 }
3815
3816 void wxWindowGTK::ClearBackground()
3817 {
3818 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3819 }
3820
3821 #if wxUSE_TOOLTIPS
3822 void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
3823 {
3824 wxWindowBase::DoSetToolTip(tip);
3825
3826 if (m_tooltip)
3827 m_tooltip->Apply( (wxWindow *)this );
3828 }
3829
3830 void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const gchar *tip )
3831 {
3832 gtk_tooltips_set_tip(tips, GetConnectWidget(), tip, NULL);
3833 }
3834 #endif // wxUSE_TOOLTIPS
3835
3836 bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
3837 {
3838 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3839
3840 if (!wxWindowBase::SetBackgroundColour(colour))
3841 return false;
3842
3843 if (colour.Ok())
3844 {
3845 // We need the pixel value e.g. for background clearing.
3846 m_backgroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
3847 }
3848
3849 // apply style change (forceStyle=true so that new style is applied
3850 // even if the bg colour changed from valid to wxNullColour)
3851 if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM)
3852 ApplyWidgetStyle(true);
3853
3854 return true;
3855 }
3856
3857 bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
3858 {
3859 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3860
3861 if (!wxWindowBase::SetForegroundColour(colour))
3862 {
3863 return false;
3864 }
3865
3866 if (colour.Ok())
3867 {
3868 // We need the pixel value e.g. for background clearing.
3869 m_foregroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
3870 }
3871
3872 // apply style change (forceStyle=true so that new style is applied
3873 // even if the bg colour changed from valid to wxNullColour):
3874 ApplyWidgetStyle(true);
3875
3876 return true;
3877 }
3878
3879 PangoContext *wxWindowGTK::GtkGetPangoDefaultContext()
3880 {
3881 return gtk_widget_get_pango_context( m_widget );
3882 }
3883
3884 GtkRcStyle *wxWindowGTK::CreateWidgetStyle(bool forceStyle)
3885 {
3886 // do we need to apply any changes at all?
3887 if ( !forceStyle &&
3888 !m_font.Ok() &&
3889 !m_foregroundColour.Ok() && !m_backgroundColour.Ok() )
3890 {
3891 return NULL;
3892 }
3893
3894 GtkRcStyle *style = gtk_rc_style_new();
3895
3896 if ( m_font.Ok() )
3897 {
3898 style->font_desc =
3899 pango_font_description_copy( m_font.GetNativeFontInfo()->description );
3900 }
3901
3902 int flagsNormal = 0,
3903 flagsPrelight = 0,
3904 flagsActive = 0,
3905 flagsInsensitive = 0;
3906
3907 if ( m_foregroundColour.Ok() )
3908 {
3909 const GdkColor *fg = m_foregroundColour.GetColor();
3910
3911 style->fg[GTK_STATE_NORMAL] =
3912 style->text[GTK_STATE_NORMAL] = *fg;
3913 flagsNormal |= GTK_RC_FG | GTK_RC_TEXT;
3914
3915 style->fg[GTK_STATE_PRELIGHT] =
3916 style->text[GTK_STATE_PRELIGHT] = *fg;
3917 flagsPrelight |= GTK_RC_FG | GTK_RC_TEXT;
3918
3919 style->fg[GTK_STATE_ACTIVE] =
3920 style->text[GTK_STATE_ACTIVE] = *fg;
3921 flagsActive |= GTK_RC_FG | GTK_RC_TEXT;
3922 }
3923
3924 if ( m_backgroundColour.Ok() )
3925 {
3926 const GdkColor *bg = m_backgroundColour.GetColor();
3927
3928 style->bg[GTK_STATE_NORMAL] =
3929 style->base[GTK_STATE_NORMAL] = *bg;
3930 flagsNormal |= GTK_RC_BG | GTK_RC_BASE;
3931
3932 style->bg[GTK_STATE_PRELIGHT] =
3933 style->base[GTK_STATE_PRELIGHT] = *bg;
3934 flagsPrelight |= GTK_RC_BG | GTK_RC_BASE;
3935
3936 style->bg[GTK_STATE_ACTIVE] =
3937 style->base[GTK_STATE_ACTIVE] = *bg;
3938 flagsActive |= GTK_RC_BG | GTK_RC_BASE;
3939
3940 style->bg[GTK_STATE_INSENSITIVE] =
3941 style->base[GTK_STATE_INSENSITIVE] = *bg;
3942 flagsInsensitive |= GTK_RC_BG | GTK_RC_BASE;
3943 }
3944
3945 style->color_flags[GTK_STATE_NORMAL] = (GtkRcFlags)flagsNormal;
3946 style->color_flags[GTK_STATE_PRELIGHT] = (GtkRcFlags)flagsPrelight;
3947 style->color_flags[GTK_STATE_ACTIVE] = (GtkRcFlags)flagsActive;
3948 style->color_flags[GTK_STATE_INSENSITIVE] = (GtkRcFlags)flagsInsensitive;
3949
3950 return style;
3951 }
3952
3953 void wxWindowGTK::ApplyWidgetStyle(bool forceStyle)
3954 {
3955 GtkRcStyle *style = CreateWidgetStyle(forceStyle);
3956 if ( style )
3957 {
3958 DoApplyWidgetStyle(style);
3959 gtk_rc_style_unref(style);
3960 }
3961
3962 // Style change may affect GTK+'s size calculation:
3963 InvalidateBestSize();
3964 }
3965
3966 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
3967 {
3968 if (m_wxwindow)
3969 gtk_widget_modify_style(m_wxwindow, style);
3970 else
3971 gtk_widget_modify_style(m_widget, style);
3972 }
3973
3974 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
3975 {
3976 wxWindowBase::SetBackgroundStyle(style);
3977
3978 if (style == wxBG_STYLE_CUSTOM)
3979 {
3980 GdkWindow *window;
3981 if ( m_wxwindow )
3982 {
3983 window = GTK_PIZZA(m_wxwindow)->bin_window;
3984 }
3985 else
3986 {
3987 GtkWidget * const w = GetConnectWidget();
3988 window = w ? w->window : NULL;
3989 }
3990
3991 if (window)
3992 {
3993 // Make sure GDK/X11 doesn't refresh the window
3994 // automatically.
3995 gdk_window_set_back_pixmap( window, None, False );
3996 #ifdef __X__
3997 Display* display = GDK_WINDOW_DISPLAY(window);
3998 XFlush(display);
3999 #endif
4000 m_needsStyleChange = false;
4001 }
4002 else // window not realized yet
4003 {
4004 // Do in OnIdle, because the window is not yet available
4005 m_needsStyleChange = true;
4006 }
4007
4008 // Don't apply widget style, or we get a grey background
4009 }
4010 else
4011 {
4012 // apply style change (forceStyle=true so that new style is applied
4013 // even if the bg colour changed from valid to wxNullColour):
4014 ApplyWidgetStyle(true);
4015 }
4016 return true;
4017 }
4018
4019 #if wxUSE_DRAG_AND_DROP
4020
4021 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
4022 {
4023 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4024
4025 GtkWidget *dnd_widget = GetConnectWidget();
4026
4027 if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
4028
4029 if (m_dropTarget) delete m_dropTarget;
4030 m_dropTarget = dropTarget;
4031
4032 if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
4033 }
4034
4035 #endif // wxUSE_DRAG_AND_DROP
4036
4037 GtkWidget* wxWindowGTK::GetConnectWidget()
4038 {
4039 GtkWidget *connect_widget = m_widget;
4040 if (m_wxwindow) connect_widget = m_wxwindow;
4041
4042 return connect_widget;
4043 }
4044
4045 bool wxWindowGTK::GTKIsOwnWindow(GdkWindow *window) const
4046 {
4047 wxArrayGdkWindows windowsThis;
4048 GdkWindow * const winThis = GTKGetWindow(windowsThis);
4049
4050 return winThis ? window == winThis
4051 : windowsThis.Index(window) != wxNOT_FOUND;
4052 }
4053
4054 GdkWindow *wxWindowGTK::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
4055 {
4056 return m_wxwindow ? GTK_PIZZA(m_wxwindow)->bin_window : m_widget->window;
4057 }
4058
4059 bool wxWindowGTK::SetFont( const wxFont &font )
4060 {
4061 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
4062
4063 if (!wxWindowBase::SetFont(font))
4064 return false;
4065
4066 // apply style change (forceStyle=true so that new style is applied
4067 // even if the font changed from valid to wxNullFont):
4068 ApplyWidgetStyle(true);
4069
4070 return true;
4071 }
4072
4073 void wxWindowGTK::DoCaptureMouse()
4074 {
4075 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4076
4077 GdkWindow *window = (GdkWindow*) NULL;
4078 if (m_wxwindow)
4079 window = GTK_PIZZA(m_wxwindow)->bin_window;
4080 else
4081 window = GetConnectWidget()->window;
4082
4083 wxCHECK_RET( window, _T("CaptureMouse() failed") );
4084
4085 const wxCursor* cursor = &m_cursor;
4086 if (!cursor->Ok())
4087 cursor = wxSTANDARD_CURSOR;
4088
4089 gdk_pointer_grab( window, FALSE,
4090 (GdkEventMask)
4091 (GDK_BUTTON_PRESS_MASK |
4092 GDK_BUTTON_RELEASE_MASK |
4093 GDK_POINTER_MOTION_HINT_MASK |
4094 GDK_POINTER_MOTION_MASK),
4095 (GdkWindow *) NULL,
4096 cursor->GetCursor(),
4097 (guint32)GDK_CURRENT_TIME );
4098 g_captureWindow = this;
4099 g_captureWindowHasMouse = true;
4100 }
4101
4102 void wxWindowGTK::DoReleaseMouse()
4103 {
4104 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4105
4106 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
4107
4108 g_captureWindow = (wxWindowGTK*) NULL;
4109
4110 GdkWindow *window = (GdkWindow*) NULL;
4111 if (m_wxwindow)
4112 window = GTK_PIZZA(m_wxwindow)->bin_window;
4113 else
4114 window = GetConnectWidget()->window;
4115
4116 if (!window)
4117 return;
4118
4119 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
4120 }
4121
4122 void wxWindowGTK::GTKReleaseMouseAndNotify()
4123 {
4124 DoReleaseMouse();
4125 wxMouseCaptureLostEvent evt(GetId());
4126 evt.SetEventObject( this );
4127 GetEventHandler()->ProcessEvent( evt );
4128 }
4129
4130 /* static */
4131 wxWindow *wxWindowBase::GetCapture()
4132 {
4133 return (wxWindow *)g_captureWindow;
4134 }
4135
4136 bool wxWindowGTK::IsRetained() const
4137 {
4138 return false;
4139 }
4140
4141 void wxWindowGTK::SetScrollbar(int orient,
4142 int pos,
4143 int thumbVisible,
4144 int range,
4145 bool WXUNUSED(update))
4146 {
4147 const int dir = ScrollDirFromOrient(orient);
4148 GtkRange* const sb = m_scrollBar[dir];
4149 wxCHECK_RET( sb, _T("this window is not scrollable") );
4150
4151 if (range > 0)
4152 {
4153 m_hasScrolling = true;
4154 }
4155 else
4156 {
4157 // GtkRange requires upper > lower
4158 range =
4159 thumbVisible = 1;
4160 }
4161
4162 GtkAdjustment * const adj = sb->adjustment;
4163 adj->step_increment = 1;
4164 adj->page_increment =
4165 adj->page_size = thumbVisible;
4166 adj->value = pos;
4167
4168 g_signal_handlers_block_by_func(
4169 sb, (void*)gtk_scrollbar_value_changed, this);
4170
4171 gtk_range_set_range(sb, 0, range);
4172 m_scrollPos[dir] = sb->adjustment->value;
4173
4174 g_signal_handlers_unblock_by_func(
4175 sb, (void*)gtk_scrollbar_value_changed, this);
4176 }
4177
4178 void wxWindowGTK::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh))
4179 {
4180 const int dir = ScrollDirFromOrient(orient);
4181 GtkRange * const sb = m_scrollBar[dir];
4182 wxCHECK_RET( sb, _T("this window is not scrollable") );
4183
4184 // This check is more than an optimization. Without it, the slider
4185 // will not move smoothly while tracking when using wxScrollHelper.
4186 if (GetScrollPos(orient) != pos)
4187 {
4188 g_signal_handlers_block_by_func(
4189 sb, (void*)gtk_scrollbar_value_changed, this);
4190
4191 gtk_range_set_value(sb, pos);
4192 m_scrollPos[dir] = sb->adjustment->value;
4193
4194 g_signal_handlers_unblock_by_func(
4195 sb, (void*)gtk_scrollbar_value_changed, this);
4196 }
4197 }
4198
4199 int wxWindowGTK::GetScrollThumb(int orient) const
4200 {
4201 GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
4202 wxCHECK_MSG( sb, 0, _T("this window is not scrollable") );
4203
4204 return int(sb->adjustment->page_size);
4205 }
4206
4207 int wxWindowGTK::GetScrollPos( int orient ) const
4208 {
4209 GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
4210 wxCHECK_MSG( sb, 0, _T("this window is not scrollable") );
4211
4212 return int(sb->adjustment->value + 0.5);
4213 }
4214
4215 int wxWindowGTK::GetScrollRange( int orient ) const
4216 {
4217 GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
4218 wxCHECK_MSG( sb, 0, _T("this window is not scrollable") );
4219
4220 return int(sb->adjustment->upper);
4221 }
4222
4223 // Determine if increment is the same as +/-x, allowing for some small
4224 // difference due to possible inexactness in floating point arithmetic
4225 static inline bool IsScrollIncrement(double increment, double x)
4226 {
4227 wxASSERT(increment > 0);
4228 const double tolerance = 1.0 / 1024;
4229 return fabs(increment - fabs(x)) < tolerance;
4230 }
4231
4232 wxEventType wxWindowGTK::GetScrollEventType(GtkRange* range)
4233 {
4234 DEBUG_MAIN_THREAD
4235
4236 wxASSERT(range == m_scrollBar[0] || range == m_scrollBar[1]);
4237
4238 const int barIndex = range == m_scrollBar[1];
4239 GtkAdjustment* adj = range->adjustment;
4240
4241 const int value = int(adj->value + 0.5);
4242
4243 // save previous position
4244 const double oldPos = m_scrollPos[barIndex];
4245 // update current position
4246 m_scrollPos[barIndex] = adj->value;
4247 // If event should be ignored, or integral position has not changed
4248 if (!m_hasVMT || g_blockEventsOnDrag || value == int(oldPos + 0.5))
4249 {
4250 return wxEVT_NULL;
4251 }
4252
4253 wxEventType eventType = wxEVT_SCROLL_THUMBTRACK;
4254 if (!m_isScrolling)
4255 {
4256 // Difference from last change event
4257 const double diff = adj->value - oldPos;
4258 const bool isDown = diff > 0;
4259
4260 if (IsScrollIncrement(adj->step_increment, diff))
4261 {
4262 eventType = isDown ? wxEVT_SCROLL_LINEDOWN : wxEVT_SCROLL_LINEUP;
4263 }
4264 else if (IsScrollIncrement(adj->page_increment, diff))
4265 {
4266 eventType = isDown ? wxEVT_SCROLL_PAGEDOWN : wxEVT_SCROLL_PAGEUP;
4267 }
4268 else if (m_mouseButtonDown)
4269 {
4270 // Assume track event
4271 m_isScrolling = true;
4272 }
4273 }
4274 return eventType;
4275 }
4276
4277 void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
4278 {
4279 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4280
4281 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4282
4283 // No scrolling requested.
4284 if ((dx == 0) && (dy == 0)) return;
4285
4286 m_clipPaintRegion = true;
4287
4288 if (GetLayoutDirection() == wxLayout_RightToLeft)
4289 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), dx, -dy );
4290 else
4291 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy );
4292
4293 m_clipPaintRegion = false;
4294
4295 #if wxUSE_CARET
4296 bool restoreCaret = (GetCaret() != NULL && GetCaret()->IsVisible());
4297 if (restoreCaret)
4298 {
4299 wxRect caretRect(GetCaret()->GetPosition(), GetCaret()->GetSize());
4300 if (dx > 0)
4301 caretRect.width += dx;
4302 else
4303 {
4304 caretRect.x += dx; caretRect.width -= dx;
4305 }
4306 if (dy > 0)
4307 caretRect.height += dy;
4308 else
4309 {
4310 caretRect.y += dy; caretRect.height -= dy;
4311 }
4312
4313 RefreshRect(caretRect);
4314 }
4315 #endif // wxUSE_CARET
4316 }
4317
4318 void wxWindowGTK::GtkScrolledWindowSetBorder(GtkWidget* w, int wxstyle)
4319 {
4320 //RN: Note that static controls usually have no border on gtk, so maybe
4321 //it makes sense to treat that as simply no border at the wx level
4322 //as well...
4323 if (!(wxstyle & wxNO_BORDER) && !(wxstyle & wxBORDER_STATIC))
4324 {
4325 GtkShadowType gtkstyle;
4326
4327 if(wxstyle & wxBORDER_RAISED)
4328 gtkstyle = GTK_SHADOW_OUT;
4329 else if (wxstyle & wxBORDER_SUNKEN)
4330 gtkstyle = GTK_SHADOW_IN;
4331 else if (wxstyle & wxBORDER_DOUBLE)
4332 gtkstyle = GTK_SHADOW_ETCHED_IN;
4333 else //default
4334 gtkstyle = GTK_SHADOW_IN;
4335
4336 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w),
4337 gtkstyle );
4338 }
4339 }
4340
4341 void wxWindowGTK::SetWindowStyleFlag( long style )
4342 {
4343 // Updates the internal variable. NB: Now m_windowStyle bits carry the _new_ style values already
4344 wxWindowBase::SetWindowStyleFlag(style);
4345 }
4346
4347 // Find the wxWindow at the current mouse position, also returning the mouse
4348 // position.
4349 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4350 {
4351 pt = wxGetMousePosition();
4352 wxWindow* found = wxFindWindowAtPoint(pt);
4353 return found;
4354 }
4355
4356 // Get the current mouse position.
4357 wxPoint wxGetMousePosition()
4358 {
4359 /* This crashes when used within wxHelpContext,
4360 so we have to use the X-specific implementation below.
4361 gint x, y;
4362 GdkModifierType *mask;
4363 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4364
4365 return wxPoint(x, y);
4366 */
4367
4368 int x, y;
4369 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
4370
4371 Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY();
4372 Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
4373 Window rootReturn, childReturn;
4374 int rootX, rootY, winX, winY;
4375 unsigned int maskReturn;
4376
4377 XQueryPointer (display,
4378 rootWindow,
4379 &rootReturn,
4380 &childReturn,
4381 &rootX, &rootY, &winX, &winY, &maskReturn);
4382 return wxPoint(rootX, rootY);
4383
4384 }
4385
4386 // Needed for implementing e.g. combobox on wxGTK within a modal dialog.
4387 void wxAddGrab(wxWindow* window)
4388 {
4389 gtk_grab_add( (GtkWidget*) window->GetHandle() );
4390 }
4391
4392 void wxRemoveGrab(wxWindow* window)
4393 {
4394 gtk_grab_remove( (GtkWidget*) window->GetHandle() );
4395 }