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(¤tX, ¤tY);
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