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