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