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