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