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