]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/window.cpp
Add wxTranslations::GetTranslatedString().
[wxWidgets.git] / src / gtk / window.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
faa94f3e 2// Name: src/gtk/window.cpp
9a33c3ef 3// Purpose: wxWindowGTK implementation
c801d85f 4// Author: Robert Roebling
01111366 5// Copyright: (c) 1998 Robert Roebling, Julian Smart
65571936 6// Licence: wxWindows licence
c801d85f
KB
7/////////////////////////////////////////////////////////////////////////////
8
14f355c2
VS
9// For compilers that support precompilation, includes "wx.h".
10#include "wx/wxprec.h"
11
d02af7bb
JJ
12#ifdef __VMS
13#define XWarpPointer XWARPPOINTER
14#endif
15
c801d85f 16#include "wx/window.h"
88a7a4e1
WS
17
18#ifndef WX_PRECOMP
e4db172a 19 #include "wx/log.h"
670f9935 20 #include "wx/app.h"
cca410b3 21 #include "wx/toplevel.h"
ed4b0fdc 22 #include "wx/dcclient.h"
3b3dc801 23 #include "wx/menu.h"
9eddec69 24 #include "wx/settings.h"
246c5004 25 #include "wx/msgdlg.h"
18680f86 26 #include "wx/math.h"
88a7a4e1
WS
27#endif
28
bfeeb7f3
PC
29#include "wx/dnd.h"
30#include "wx/tooltip.h"
31#include "wx/caret.h"
48d011c8 32#include "wx/fontutil.h"
8ab7b4c5 33#include "wx/sysopt.h"
9dc44eff
PC
34#ifdef __WXGTK3__
35 #include "wx/gtk/dc.h"
36#endif
b4071e91 37
fab591c5 38#include <ctype.h>
c801d85f 39
9dc44eff 40#include <gtk/gtk.h>
9e691f46 41#include "wx/gtk/private.h"
9dc44eff 42#include "wx/gtk/private/gtk2-compat.h"
4a99d597 43#include "wx/gtk/private/event.h"
9dc44eff 44#include "wx/gtk/private/win_gtk.h"
8cd79b7a 45#include "wx/private/textmeasure.h"
4a99d597 46using namespace wxGTKImpl;
14b44999 47
3b81515c 48#ifdef GDK_WINDOWING_X11
3ac8d3bc 49#include <gdk/gdkx.h>
25cc08c0 50#include "wx/x11/private/wrapxkb.h"
3b81515c
VZ
51#else
52typedef guint KeySym;
53#endif
6bc8a1c8 54
14b44999 55#include <gdk/gdkkeysyms.h>
9dc44eff 56#ifdef __WXGTK3__
14b44999
VS
57#include <gdk/gdkkeysyms-compat.h>
58#endif
59
14421681
VZ
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
868a2826
RR
64//-----------------------------------------------------------------------------
65// documentation on internals
66//-----------------------------------------------------------------------------
67
68/*
69 I have been asked several times about writing some documentation about
77ffb593 70 the GTK port of wxWidgets, especially its internal structures. Obviously,
868a2826 71 you cannot understand wxGTK without knowing a little about the GTK, but
47d67540 72 some more information about what the wxWindow, which is the base class
868a2826 73 for all other window classes, does seems required as well.
47d67540 74
30760ce7
RR
75 I)
76
868a2826 77 What does wxWindow do? It contains the common interface for the following
e380f72b 78 jobs of its descendants:
47d67540 79
868a2826 80 1) Define the rudimentary behaviour common to all window classes, such as
e380f72b
RR
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.
868a2826
RR
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
e380f72b 86 classes which do not display a native GTK widget. To name them, these
868a2826 87 classes are wxPanel, wxScrolledWindow, wxDialog, wxFrame. The MDI frame-
47d67540 88 work classes are a special case and are handled a bit differently from
e380f72b 89 the rest. The same holds true for the wxNotebook class.
47d67540 90
868a2826
RR
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.
47d67540 94
e380f72b
RR
95 4) Provide the entire mechanism for scrolling widgets. This actual inter-
96 face for this is usually in wxScrolledWindow, but the GTK implementation
868a2826 97 is in this class.
47d67540 98
868a2826
RR
99 5) A multitude of helper or extra methods for special purposes, such as
100 Drag'n'Drop, managing validators etc.
47d67540 101
30760ce7
RR
102 6) Display a border (sunken, raised, simple or none).
103
77ffb593 104 Normally one might expect, that one wxWidgets window would always correspond
6a17b868 105 to one GTK widget. Under GTK, there is no such all-round widget that has all
868a2826
RR
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
77ffb593 111 represent a client area in the sense of wxWidgets capable to do the jobs
868a2826
RR
112 2), 3) and 4). I have written this class and it resides in win_gtk.c of
113 this directory.
47d67540 114
868a2826 115 All windows must have a widget, with which they interact with other under-
e380f72b 116 lying GTK widgets. It is this widget, e.g. that has to be resized etc and
90e572f1 117 the wxWindow class has a member variable called m_widget which holds a
e380f72b
RR
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
f8e045e2 120 wxStaticText class handles only a GtkLabel widget a pointer to which you
e380f72b 121 can find in m_widget (defined in wxWindow)
8bbe427f 122
e380f72b 123 When the class has a client area for drawing into and for containing children
08f53168
PC
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.
8bbe427f
VZ
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
08f53168 130 the type wxPizza.
47d67540 131
868a2826
RR
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-
08f53168 136 Window and a m_wxwindow field that is pointer to a wxPizza and this
868a2826 137 one is (in the GTK sense) a child of the GtkScrolledWindow.
47d67540 138
868a2826 139 If the m_wxwindow field is set, then all input to this widget is inter-
77ffb593 140 cepted and sent to the wxWidgets class. If not, all input to the widget
868a2826 141 that gets pointed to by m_widget gets intercepted and sent to the class.
148cd9b6 142
30760ce7 143 II)
148cd9b6 144
77ffb593 145 The design of scrolling in wxWidgets is markedly different from that offered
30760ce7
RR
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
77ffb593 148 the window. In wxWidgets, the scrollbar will only emit an event, send this
30760ce7 149 to (normally) a wxScrolledWindow and that class will call ScrollWindow()
08f53168 150 which actually moves the window and its sub-windows. Note that wxPizza
77ffb593 151 memorizes how much it has been scrolled but that wxWidgets forgets this
30760ce7 152 so that the two coordinates systems have to be kept in synch. This is done
08f53168 153 in various places using the pizza->m_scroll_x and pizza->m_scroll_y values.
148cd9b6
VZ
154
155 III)
156
6a17b868 157 Singularly the most broken code in GTK is the code that is supposed to
30760ce7
RR
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
90e572f1 164 the disadvantage that windows might get size events before the GTK widget
30760ce7 165 actually has the reported size. This doesn't normally pose any problem, but
90e572f1 166 the OpenGL drawing routines rely on correct behaviour. Therefore, I have
30760ce7
RR
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
90e572f1 169 window that is used for OpenGL output really has that size (as reported by
30760ce7
RR
170 GTK).
171
172 IV)
148cd9b6 173
30760ce7 174 If someone at some point of time feels the immense desire to have a look at,
90e572f1
MR
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
30760ce7 178 own windowless widgets. Beware.
148cd9b6 179
30760ce7 180 V)
148cd9b6 181
30760ce7
RR
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
c2246a38
RR
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.
30760ce7
RR
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
90e572f1 188 point to the GdkWindow of the parent widget (-> "window-less widget") and
30760ce7 189 that the two obviously have very different meanings.
868a2826
RR
190*/
191
f03fc89f
VZ
192//-----------------------------------------------------------------------------
193// data
194//-----------------------------------------------------------------------------
195
b541538f
PC
196// Don't allow event propagation during drag
197bool g_blockEventsOnDrag;
198// Don't allow mouse event propagation during scroll
199bool g_blockEventsOnScroll;
238d735d 200extern wxCursor g_globalCursor;
f68586e5 201
1e6feb95
VZ
202// mouse capture state: the window which has it and if the mouse is currently
203// inside it
d3b9f782 204static wxWindowGTK *g_captureWindow = NULL;
0a164d4c 205static bool g_captureWindowHasMouse = false;
1e6feb95 206
22f43cb5
VS
207// The window that currently has focus:
208static 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:
211static wxWindowGTK *gs_pendingFocus = NULL;
1e6feb95 212
bd2e08d0
VS
213// the window that has deferred focus-out event pending, if any (see
214// GTKAddDeferredFocusOut() for details)
215static wxWindowGTK *gs_deferredFocusOut = NULL;
d7fa7eaa 216
5e513780
RR
217// global variables because GTK+ DnD want to have the
218// mouse event that caused it
d3b9f782 219GdkEvent *g_lastMouseEvent = NULL;
5e513780 220int g_lastButtonNumber = 0;
8f9850dd 221
2e563988
RR
222//-----------------------------------------------------------------------------
223// debug
224//-----------------------------------------------------------------------------
225
6cad4f1b 226// the trace mask used for the focus debugging messages
9a83f860 227#define TRACE_FOCUS wxT("focus")
6cad4f1b 228
723a83c9
VZ
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.
232const 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
c801d85f 240//-----------------------------------------------------------------------------
9dc44eff 241// "expose_event"/"draw" from m_wxwindow
c801d85f
KB
242//-----------------------------------------------------------------------------
243
865bb325 244extern "C" {
9dc44eff
PC
245#ifdef __WXGTK3__
246static 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__
254static gboolean expose_event(GtkWidget*, GdkEventExpose* gdk_event, wxWindow* win)
47d67540 255{
f089940f 256 if (gdk_event->window == win->GTKGetDrawingWindow())
9dc44eff
PC
257 win->GTKSendPaintEvents(gdk_event->region);
258
259 return false;
b6fa52db 260}
9dc44eff 261#endif // !__WXGTK3__
865bb325 262}
b6fa52db 263
75f661bb 264#ifndef __WXUNIVERSAL__
08f53168 265//-----------------------------------------------------------------------------
9dc44eff 266// "expose_event"/"draw" from m_wxwindow->parent, for drawing border
08f53168
PC
267//-----------------------------------------------------------------------------
268
08f53168
PC
269extern "C" {
270static gboolean
9dc44eff
PC
271#ifdef __WXGTK3__
272draw_border(GtkWidget*, cairo_t* cr, wxWindow* win)
273#else
274draw_border(GtkWidget* widget, GdkEventExpose* gdk_event, wxWindow* win)
275#endif
08f53168 276{
9dc44eff
PC
277#ifdef __WXGTK3__
278 if (!gtk_cairo_should_draw_window(cr, gtk_widget_get_parent_window(win->m_wxwindow)))
279#else
f089940f 280 if (gdk_event->window != gtk_widget_get_parent_window(win->m_wxwindow))
9dc44eff 281#endif
75f661bb
PC
282 return false;
283
6e7cf3bd
PC
284 if (!win->IsShown())
285 return false;
286
989d151c
PC
287 GtkAllocation alloc;
288 gtk_widget_get_allocation(win->m_wxwindow, &alloc);
75f661bb
PC
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)
08f53168
PC
295 return false;
296
08f53168
PC
297 if (win->HasFlag(wxBORDER_SIMPLE))
298 {
9dc44eff
PC
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
830910cc 308 gdk_draw_rectangle(gdk_event->window,
08f09504 309 gtk_widget_get_style(widget)->black_gc, false, x, y, w - 1, h - 1);
9dc44eff 310#endif
08f53168 311 }
9dc44eff 312 else if (win->HasFlag(wxBORDER_RAISED | wxBORDER_SUNKEN | wxBORDER_THEME))
08f53168 313 {
9dc44eff
PC
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__
08f53168
PC
324 GtkShadowType shadow = GTK_SHADOW_IN;
325 if (win->HasFlag(wxBORDER_RAISED))
326 shadow = GTK_SHADOW_OUT;
03eaa52a 327
2c48aec5 328 GtkStyle* style;
03eaa52a 329 const char* detail;
2c48aec5
PC
330 if (win->HasFlag(wxHSCROLL | wxVSCROLL))
331 {
332 style = gtk_widget_get_style(wxGTKPrivate::GetTreeWidget());
03eaa52a 333 detail = "viewport";
2c48aec5
PC
334 }
335 else
336 {
337 style = gtk_widget_get_style(wxGTKPrivate::GetEntryWidget());
338 detail = "entry";
339 }
03eaa52a 340
4f4f76de
PC
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 };
08f53168 344 gtk_paint_shadow(
2c48aec5
PC
345 style, gdk_event->window, GTK_STATE_NORMAL,
346 shadow, &clipRect, widget, detail, x, y, w, h);
9dc44eff 347#endif // !__WXGTK3__
08f53168 348 }
75f661bb
PC
349 return false;
350}
351}
352
353//-----------------------------------------------------------------------------
354// "parent_set" from m_wxwindow
355//-----------------------------------------------------------------------------
08f53168 356
75f661bb
PC
357extern "C" {
358static void
cb0c51ac 359parent_set(GtkWidget* widget, GtkWidget* old_parent, wxWindow* win)
75f661bb
PC
360{
361 if (old_parent)
362 {
363 g_signal_handlers_disconnect_by_func(
9dc44eff 364 old_parent, (void*)draw_border, win);
75f661bb 365 }
08f09504
PC
366 GtkWidget* parent = gtk_widget_get_parent(widget);
367 if (parent)
75f661bb 368 {
9dc44eff
PC
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
75f661bb 374 }
08f53168
PC
375}
376}
377#endif // !__WXUNIVERSAL__
378
c801d85f 379//-----------------------------------------------------------------------------
b292e2f5 380// "key_press_event" from any window
c801d85f 381//-----------------------------------------------------------------------------
c801d85f 382
74710601 383// set WXTRACE to this to see the key event codes on the console
9a83f860 384#define TRACE_KEYS wxT("keyevent")
f17393f1 385
1c6896d7
VZ
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
393static 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
faa94f3e 504 key_code = WXK_PAGEUP;
1c6896d7
VZ
505 break;
506
507 case GDK_Next: // == GDK_Page_Down
faa94f3e 508 key_code = WXK_PAGEDOWN;
1c6896d7
VZ
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:
2a230426 535 key_code = (isChar ? '0' : int(WXK_NUMPAD0)) + keysym - GDK_KP_0;
1c6896d7
VZ
536 break;
537
538 case GDK_KP_Space:
2a230426 539 key_code = isChar ? ' ' : int(WXK_NUMPAD_SPACE);
1c6896d7
VZ
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
faa94f3e 587 key_code = isChar ? WXK_PAGEUP : WXK_NUMPAD_PAGEUP;
1c6896d7
VZ
588 break;
589
590 case GDK_KP_Next: // == GDK_KP_Page_Down
faa94f3e 591 key_code = isChar ? WXK_PAGEDOWN : WXK_NUMPAD_PAGEDOWN;
1c6896d7
VZ
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:
2a230426 611 key_code = isChar ? '=' : int(WXK_NUMPAD_EQUAL);
1c6896d7
VZ
612 break;
613
614 case GDK_KP_Multiply:
2a230426 615 key_code = isChar ? '*' : int(WXK_NUMPAD_MULTIPLY);
1c6896d7
VZ
616 break;
617
618 case GDK_KP_Add:
2a230426 619 key_code = isChar ? '+' : int(WXK_NUMPAD_ADD);
1c6896d7
VZ
620 break;
621
622 case GDK_KP_Separator:
623 // FIXME: what is this?
2a230426 624 key_code = isChar ? '.' : int(WXK_NUMPAD_SEPARATOR);
1c6896d7
VZ
625 break;
626
627 case GDK_KP_Subtract:
2a230426 628 key_code = isChar ? '-' : int(WXK_NUMPAD_SUBTRACT);
1c6896d7
VZ
629 break;
630
631 case GDK_KP_Decimal:
2a230426 632 key_code = isChar ? '.' : int(WXK_NUMPAD_DECIMAL);
1c6896d7
VZ
633 break;
634
635 case GDK_KP_Divide:
2a230426 636 key_code = isChar ? '/' : int(WXK_NUMPAD_DIVIDE);
1c6896d7
VZ
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
663static inline bool wxIsAsciiKeysym(KeySym ks)
664{
665 return ks < 256;
666}
667
a3c15d89
VS
668static void wxFillOtherKeyEventFields(wxKeyEvent& event,
669 wxWindowGTK *win,
670 GdkEventKey *gdk_event)
671{
a3c15d89 672 event.SetTimestamp( gdk_event->time );
cfa8c7d6 673 event.SetId(win->GetId());
d0fb62a6 674
a3c15d89
VS
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;
87ec008f 678 event.m_metaDown = (gdk_event->state & GDK_META_MASK) != 0;
d0fb62a6 679
82213cb0
VZ
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
d0fb62a6
VZ
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
a3c15d89 732 event.m_rawCode = (wxUint32) gdk_event->keyval;
5995a84f 733 event.m_rawFlags = gdk_event->hardware_keycode;
d0fb62a6 734
a3c15d89
VS
735 event.SetEventObject( win );
736}
737
738
74710601
VZ
739static bool
740wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
741 wxWindowGTK *win,
742 GdkEventKey *gdk_event)
47d67540 743{
1c6896d7
VZ
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;
0a62b197 756
a0bb0df5 757 wxLogTrace(TRACE_KEYS, wxT("Key %s event: keysym = %lu"),
9a83f860
VZ
758 event.GetEventType() == wxEVT_KEY_UP ? wxT("release")
759 : wxT("press"),
a0bb0df5 760 static_cast<unsigned long>(keysym));
0a62b197 761
0a164d4c 762 long key_code = wxTranslateKeySymToWXKey(keysym, false /* !isChar */);
1c6896d7
VZ
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 }
a2053b27 776
3b81515c 777#ifdef GDK_WINDOWING_X11
1c6896d7 778 // we want to always get the same key code when the same key is
90e572f1 779 // pressed regardless of the state of the modifiers, i.e. on a
1c6896d7
VZ
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);
0a62b197 788
9a83f860 789 wxLogTrace(TRACE_KEYS, wxT("\t-> keycode %d"), keycode);
0a62b197 790
25cc08c0
VZ
791#ifdef HAVE_X11_XKBLIB_H
792 KeySym keysymNormalized = XkbKeycodeToKeysym(dpy, keycode, 0, 0);
793#else
1c6896d7 794 KeySym keysymNormalized = XKeycodeToKeysym(dpy, keycode, 0);
25cc08c0 795#endif
1c6896d7
VZ
796
797 // use the normalized, i.e. lower register, keysym if we've
798 // got one
799 key_code = keysymNormalized ? keysymNormalized : keysym;
3b81515c
VZ
800#else
801 key_code = keysym;
802#endif
1c6896d7
VZ
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
9a83f860 835 wxLogTrace(TRACE_KEYS, wxT("\t-> wxKeyCode %ld"), key_code);
c801d85f 836
74710601 837 // sending unknown key events doesn't really make sense
1c6896d7 838 if ( !key_code )
0a164d4c 839 return false;
3d6f7261 840
276f883f
VZ
841 event.m_keyCode = key_code;
842
7333c0ef 843#if wxUSE_UNICODE
adba5bab 844 event.m_uniChar = gdk_keyval_to_unicode(key_code);
7333c0ef
VZ
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
1c6896d7 854 // now fill all the other fields
a3c15d89 855 wxFillOtherKeyEventFields(event, win, gdk_event);
0a164d4c 856
0a164d4c 857 return true;
74710601
VZ
858}
859
7c5e6fc6 860
2933f70a
VZ
861namespace
862{
863
f47a3591
VZ
864// Send wxEVT_CHAR_HOOK event to the parent of the window and return true only
865// if it was processed (and not skipped).
866bool SendCharHookEvent(const wxKeyEvent& event, wxWindow *win)
2933f70a 867{
3a95f73c
VZ
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)
5c16a699
VZ
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 )
2933f70a 873 {
3a95f73c 874 wxKeyEvent eventCharHook(wxEVT_CHAR_HOOK, event);
4cf1a9bf
VZ
875 if ( win->HandleWindowEvent(eventCharHook)
876 && !event.IsNextEventAllowed() )
3a95f73c 877 return true;
2933f70a
VZ
878 }
879
f47a3591 880 return false;
2933f70a
VZ
881}
882
64c502e8
VZ
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
887void 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
2933f70a
VZ
918} // anonymous namespace
919
14037106
PC
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
865bb325 931extern "C" {
7f7beb1d 932static gboolean
3591d10f 933gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget),
7f7beb1d
MR
934 GdkEventKey *gdk_event,
935 wxWindow *win )
74710601 936{
1c6896d7
VZ
937 if (g_blockEventsOnDrag)
938 return FALSE;
f1272160 939
14037106
PC
940 wxPROCESS_EVENT_ONCE(GdkEventKey, gdk_event);
941
f1272160
RR
942 wxKeyEvent event( wxEVT_KEY_DOWN );
943 bool ret = false;
944 bool return_after_IM = false;
945
84dc821c
JS
946 if( wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
947 {
f47a3591
VZ
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
aedc06e3
VZ
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 {
ce7fe42e 963 wxCommandEvent menu_event( wxEVT_MENU, command );
aedc06e3
VZ
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):
ce7fe42e 971 wxCommandEvent button_event( wxEVT_BUTTON, command );
aedc06e3
VZ
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 );
84dc821c
JS
986 }
987 else
f1272160
RR
988 {
989 // Return after IM processing as we cannot do
990 // anything with it anyhow.
991 return_after_IM = true;
992 }
993
afa98731 994 if ( !ret )
f6fca1f8 995 {
afa98731
VZ
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;
9cbe96d0 1000
f1272160
RR
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.
0a164d4c 1003 // we should send the key_down event anyway.
afa98731
VZ
1004 const int intercepted_by_IM = win->GTKIMFilterKeypress(gdk_event);
1005
1006 win->m_imKeyEvent = NULL;
1007
1008 if ( intercepted_by_IM )
f1272160 1009 {
9a83f860 1010 wxLogTrace(TRACE_KEYS, wxT("Key event intercepted by IM"));
7f7beb1d 1011 return TRUE;
f1272160 1012 }
f6fca1f8 1013 }
68567a96 1014
f1272160 1015 if (return_after_IM)
7f7beb1d 1016 return FALSE;
68567a96 1017
1ec3a984
RR
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.
2dde25b9 1020 if (!ret)
d728dd40 1021 {
1c6896d7 1022 KeySym keysym = gdk_event->keyval;
36025bcc 1023 // Find key code for EVT_CHAR and EVT_CHAR_HOOK events
d50905f7 1024 long key_code = wxTranslateKeySymToWXKey(keysym, true /* isChar */);
36025bcc 1025 if ( !key_code )
1c6896d7 1026 {
0b187670 1027 if ( wxIsAsciiKeysym(keysym) )
1ec3a984 1028 {
36025bcc
VS
1029 // ASCII key
1030 key_code = (unsigned char)keysym;
1031 }
0b187670
RR
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 }
36025bcc 1037 }
7c5e6fc6 1038
36025bcc
VS
1039 if ( key_code )
1040 {
f47a3591
VZ
1041 wxKeyEvent eventChar(wxEVT_CHAR, event);
1042
9a83f860 1043 wxLogTrace(TRACE_KEYS, wxT("Char event: %ld"), key_code);
7c5e6fc6 1044
f47a3591 1045 eventChar.m_keyCode = key_code;
d50905f7
VZ
1046#if wxUSE_UNICODE
1047 eventChar.m_uniChar = gdk_keyval_to_unicode(key_code);
1048#endif // wxUSE_UNICODE
7c5e6fc6 1049
64c502e8 1050 AdjustCharEventKeyCodes(eventChar);
1dabdced 1051
f47a3591 1052 ret = win->HandleWindowEvent(eventChar);
f17393f1 1053 }
d728dd40 1054 }
4d3ab2a0 1055
c7b2e494 1056 return ret;
2b5f62a0 1057}
865bb325 1058}
2b5f62a0 1059
afa98731
VZ
1060int wxWindowGTK::GTKIMFilterKeypress(GdkEventKey* event) const
1061{
1062 return m_imContext ? gtk_im_context_filter_keypress(m_imContext, event)
1063 : FALSE;
1064}
1065
865bb325 1066extern "C" {
7f7beb1d 1067static void
e4161a2a 1068gtk_wxwindow_commit_cb (GtkIMContext * WXUNUSED(context),
7f7beb1d
MR
1069 const gchar *str,
1070 wxWindow *window)
b2c35774
VZ
1071{
1072 // Ignore the return value here, it doesn't matter for the "commit" signal.
1073 window->GTKDoInsertTextFromIM(str);
1074}
1075}
1076
1077bool wxWindowGTK::GTKDoInsertTextFromIM(const char* str)
2b5f62a0 1078{
f47a3591 1079 wxKeyEvent event( wxEVT_CHAR );
2b5f62a0 1080
a3c15d89
VS
1081 // take modifiers, cursor position, timestamp etc. from the last
1082 // key_press_event that was fed into Input Method:
b2c35774 1083 if ( m_imKeyEvent )
a3c15d89 1084 {
b2c35774 1085 wxFillOtherKeyEventFields(event, this, m_imKeyEvent);
a3c15d89 1086 }
28c513cb
RR
1087 else
1088 {
b2c35774 1089 event.SetEventObject(this);
28c513cb 1090 }
a3c15d89 1091
6256849f
VS
1092 const wxString data(wxGTK_CONV_BACK_SYS(str));
1093 if( data.empty() )
b2c35774 1094 return false;
7c5e6fc6 1095
b2c35774 1096 bool processed = false;
6256849f 1097 for( wxString::const_iterator pstr = data.begin(); pstr != data.end(); ++pstr )
7c5e6fc6 1098 {
5bfaca1b
VS
1099#if wxUSE_UNICODE
1100 event.m_uniChar = *pstr;
f6fca1f8 1101 // Backward compatible for ISO-8859-1
5bfaca1b 1102 event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0;
9a83f860 1103 wxLogTrace(TRACE_KEYS, wxT("IM sent character '%c'"), event.m_uniChar);
5bfaca1b 1104#else
6256849f 1105 event.m_keyCode = (char)*pstr;
5bfaca1b 1106#endif // wxUSE_UNICODE
1dabdced 1107
64c502e8 1108 AdjustCharEventKeyCodes(event);
1dabdced 1109
b2c35774
VZ
1110 if ( HandleWindowEvent(event) )
1111 processed = true;
2b5f62a0 1112 }
b2c35774
VZ
1113
1114 return processed;
2b5f62a0 1115}
b2c35774
VZ
1116
1117bool 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);
865bb325 1126}
2b5f62a0
VZ
1127
1128
b666df2c
RR
1129//-----------------------------------------------------------------------------
1130// "key_release_event" from any window
1131//-----------------------------------------------------------------------------
1132
865bb325 1133extern "C" {
7f7beb1d 1134static gboolean
e4161a2a 1135gtk_window_key_release_callback( GtkWidget * WXUNUSED(widget),
7f7beb1d
MR
1136 GdkEventKey *gdk_event,
1137 wxWindowGTK *win )
b666df2c 1138{
74710601
VZ
1139 if (g_blockEventsOnDrag)
1140 return FALSE;
b666df2c 1141
14037106
PC
1142 wxPROCESS_EVENT_ONCE(GdkEventKey, gdk_event);
1143
b666df2c 1144 wxKeyEvent event( wxEVT_KEY_UP );
74710601 1145 if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
b666df2c 1146 {
90e572f1 1147 // unknown key pressed, ignore (the event would be useless anyhow)
74710601 1148 return FALSE;
b666df2c
RR
1149 }
1150
97687291 1151 return win->GTKProcessEvent(event);
b666df2c 1152}
865bb325 1153}
b666df2c 1154
c5f9d156
VS
1155// ============================================================================
1156// the mouse events
1157// ============================================================================
1158
d1f2ac45
VZ
1159// ----------------------------------------------------------------------------
1160// mouse event processing helpers
1161// ----------------------------------------------------------------------------
1162
2daa0ce9
VZ
1163static 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
1e6feb95 1169
1a8caf94
RR
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))
2daa0ce9 1181 {
1a8caf94
RR
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;
2daa0ce9 1192 }
e7cda1c3
VZ
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 }
2daa0ce9
VZ
1207}
1208
58072456 1209// find the window to send the mouse event to
d1f2ac45
VZ
1210static
1211wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
1212{
7d4909b2
RR
1213 wxCoord xx = x;
1214 wxCoord yy = y;
1215
d1f2ac45
VZ
1216 if (win->m_wxwindow)
1217 {
08f53168
PC
1218 wxPizza* pizza = WX_PIZZA(win->m_wxwindow);
1219 xx += pizza->m_scroll_x;
1220 yy += pizza->m_scroll_y;
d1f2ac45
VZ
1221 }
1222
222ed1d6 1223 wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
d1f2ac45
VZ
1224 while (node)
1225 {
34670bda 1226 wxWindow* child = static_cast<wxWindow*>(node->GetData());
d1f2ac45 1227
b1d4dd7a 1228 node = node->GetNext();
d1f2ac45
VZ
1229 if (!child->IsShown())
1230 continue;
1231
71ead4bf 1232 if (child->GTKIsTransparentForMouse())
d1f2ac45
VZ
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;
7408cf7f 1238 int yy2 = child->m_y + child->m_height;
d1f2ac45
VZ
1239
1240 // left
7d4909b2 1241 if (((xx >= xx1) && (xx <= xx1+10) && (yy >= yy1) && (yy <= yy2)) ||
d1f2ac45 1242 // right
7d4909b2 1243 ((xx >= xx2-10) && (xx <= xx2) && (yy >= yy1) && (yy <= yy2)) ||
d1f2ac45 1244 // top
7d4909b2 1245 ((xx >= xx1) && (xx <= xx2) && (yy >= yy1) && (yy <= yy1+10)) ||
d1f2ac45 1246 // bottom
7d4909b2 1247 ((xx >= xx1) && (xx <= xx2) && (yy >= yy2-1) && (yy <= yy2)))
d1f2ac45
VZ
1248 {
1249 win = child;
1250 x -= child->m_x;
1251 y -= child->m_y;
1252 break;
1253 }
1254
1255 }
1256 else
1257 {
d3b9f782 1258 if ((child->m_wxwindow == NULL) &&
58072456 1259 win->IsClientAreaChild(child) &&
7d4909b2
RR
1260 (child->m_x <= xx) &&
1261 (child->m_y <= yy) &&
af3653dd
RR
1262 (child->m_x+child->m_width >= xx) &&
1263 (child->m_y+child->m_height >= yy))
d1f2ac45
VZ
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
ef5c70f9
VZ
1276// ----------------------------------------------------------------------------
1277// common event handlers helpers
1278// ----------------------------------------------------------------------------
1279
97687291
VZ
1280bool wxWindowGTK::GTKProcessEvent(wxEvent& event) const
1281{
1282 // nothing special at this level
937013e0 1283 return HandleWindowEvent(event);
97687291
VZ
1284}
1285
02bad830
VZ
1286bool wxWindowGTK::GTKShouldIgnoreEvent() const
1287{
8ab75332 1288 return g_blockEventsOnDrag;
02bad830
VZ
1289}
1290
5478f221 1291int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny *event) const
ef5c70f9 1292{
ef5c70f9 1293 if (g_blockEventsOnDrag)
5478f221 1294 return TRUE;
ef5c70f9 1295 if (g_blockEventsOnScroll)
5478f221 1296 return TRUE;
ef5c70f9
VZ
1297
1298 if (!GTKIsOwnWindow(event->window))
5478f221 1299 return FALSE;
ef5c70f9 1300
5478f221 1301 return -1;
ef5c70f9
VZ
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) \
5478f221 1308 static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win) \
ef5c70f9
VZ
1309 { \
1310 return win->GTKCallbackCommonPrologue((GdkEventAny *)event); \
1311 }
1312
1313wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventButton)
1314wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion)
1315wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing)
1316
1317#undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD
1318
5478f221
VZ
1319#define wxCOMMON_CALLBACK_PROLOGUE(event, win) \
1320 const int rc = wxGtkCallbackCommonPrologue(event, win); \
1321 if ( rc != -1 ) \
1322 return rc
1323
ef5c70f9
VZ
1324// all event handlers must have C linkage as they're called from GTK+ C code
1325extern "C"
1326{
1327
c801d85f 1328//-----------------------------------------------------------------------------
2f2aa628
RR
1329// "button_press_event"
1330//-----------------------------------------------------------------------------
c801d85f 1331
7f7beb1d 1332static gboolean
9dc44eff 1333gtk_window_button_press_callback( GtkWidget* WXUNUSED_IN_GTK3(widget),
7f7beb1d
MR
1334 GdkEventButton *gdk_event,
1335 wxWindowGTK *win )
903f689b 1336{
14037106
PC
1337 wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
1338
5478f221 1339 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
034be888 1340
8f9850dd
RR
1341 g_lastButtonNumber = gdk_event->button;
1342
a4cb8587
PC
1343 wxEventType event_type;
1344 wxEventType down;
1345 wxEventType dclick;
1346 switch (gdk_event->button)
2b5f62a0 1347 {
a4cb8587
PC
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)
2b5f62a0 1379 {
a4cb8587
PC
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 }
2b5f62a0 1391 }
a4cb8587
PC
1392 break;
1393 case GDK_2BUTTON_PRESS:
1394 event_type = dclick;
1395#ifndef __WXGTK3__
1396 if (gdk_event->button >= 1 && gdk_event->button <= 3)
2b5f62a0 1397 {
a4cb8587
PC
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;
2b5f62a0 1404 }
9dc44eff 1405#endif // !__WXGTK3__
a4cb8587
PC
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;
2daa0ce9
VZ
1416 }
1417
8f9850dd 1418 g_lastMouseEvent = (GdkEvent*) gdk_event;
fcb29b23 1419
f5e27805 1420 wxMouseEvent event( event_type );
c5f9d156 1421 InitMouseEvent( win, event, gdk_event );
47d67540 1422
2daa0ce9 1423 AdjustEventButtonState(event);
94633ad9 1424
90e572f1
MR
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
d1f2ac45
VZ
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);
ff8bfdbb 1430
ccb9fa0d
RD
1431 // reset the event object and id in case win changed.
1432 event.SetEventObject( win );
1433 event.SetId( win->GetId() );
670f9935 1434
fcb29b23 1435 bool ret = win->GTKProcessEvent( event );
5e513780 1436 g_lastMouseEvent = NULL;
fcb29b23
VZ
1437 if ( ret )
1438 return TRUE;
47d67540 1439
78cd9c69 1440 if ((event_type == wxEVT_LEFT_DOWN) && !win->IsOfStandardClass() &&
22f43cb5 1441 (gs_currentFocus != win) /* && win->IsFocusable() */)
8622e8cd 1442 {
c7bfb76a 1443 win->SetFocus();
8622e8cd
RR
1444 }
1445
ac103441
RR
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);
97687291 1460 return win->GTKProcessEvent(evtCtx);
ac103441
RR
1461 }
1462
034be888 1463 return FALSE;
362c6693 1464}
c801d85f
KB
1465
1466//-----------------------------------------------------------------------------
97b3455a 1467// "button_release_event"
2f2aa628 1468//-----------------------------------------------------------------------------
c801d85f 1469
7f7beb1d 1470static gboolean
71ead4bf 1471gtk_window_button_release_callback( GtkWidget *WXUNUSED(widget),
7f7beb1d
MR
1472 GdkEventButton *gdk_event,
1473 wxWindowGTK *win )
47d67540 1474{
14037106
PC
1475 wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
1476
5478f221 1477 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
47d67540 1478
8f9850dd
RR
1479 g_lastButtonNumber = 0;
1480
f5e27805 1481 wxEventType event_type = wxEVT_NULL;
47d67540 1482
f5e27805
RR
1483 switch (gdk_event->button)
1484 {
2b5f62a0
VZ
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
e7cda1c3
VZ
1497 case 8:
1498 event_type = wxEVT_AUX1_UP;
1499 break;
1500
1501 case 9:
1502 event_type = wxEVT_AUX2_UP;
1503 break;
1504
2b5f62a0 1505 default:
6a17b868 1506 // unknown button, don't process
2b5f62a0 1507 return FALSE;
f5e27805 1508 }
47d67540 1509
8f9850dd 1510 g_lastMouseEvent = (GdkEvent*) gdk_event;
fcb29b23 1511
f5e27805 1512 wxMouseEvent event( event_type );
c5f9d156 1513 InitMouseEvent( win, event, gdk_event );
f5e27805 1514
2daa0ce9
VZ
1515 AdjustEventButtonState(event);
1516
d1f2ac45
VZ
1517 if ( !g_captureWindow )
1518 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
47d67540 1519
ccb9fa0d
RD
1520 // reset the event object and id in case win changed.
1521 event.SetEventObject( win );
1522 event.SetId( win->GetId() );
1523
e6cac2df 1524 bool ret = win->GTKProcessEvent(event);
f4322df6 1525
e6cac2df 1526 g_lastMouseEvent = NULL;
f4322df6 1527
e6cac2df 1528 return ret;
362c6693 1529}
c801d85f
KB
1530
1531//-----------------------------------------------------------------------------
2f2aa628
RR
1532// "motion_notify_event"
1533//-----------------------------------------------------------------------------
c801d85f 1534
7f7beb1d 1535static gboolean
e4161a2a 1536gtk_window_motion_notify_callback( GtkWidget * WXUNUSED(widget),
7f7beb1d
MR
1537 GdkEventMotion *gdk_event,
1538 wxWindowGTK *win )
47d67540 1539{
14037106
PC
1540 wxPROCESS_EVENT_ONCE(GdkEventMotion, gdk_event);
1541
5478f221 1542 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
034be888 1543
ff8bfdbb 1544 if (gdk_event->is_hint)
aae24d21 1545 {
f7a11f8c
RR
1546 int x = 0;
1547 int y = 0;
6bfcc4ec
PC
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
f7a11f8c
RR
1553 gdk_event->x = x;
1554 gdk_event->y = y;
aae24d21 1555 }
ff8bfdbb 1556
8f9850dd
RR
1557 g_lastMouseEvent = (GdkEvent*) gdk_event;
1558
e380f72b 1559 wxMouseEvent event( wxEVT_MOTION );
c5f9d156 1560 InitMouseEvent(win, event, gdk_event);
e380f72b 1561
50382578 1562 if ( g_captureWindow )
2f2aa628 1563 {
6a17b868 1564 // synthesise a mouse enter or leave event if needed
6bfcc4ec
PC
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
7c5e6fc6 1571 // This seems to be necessary and actually been added to
50382578
RR
1572 // GDK itself in version 2.0.X
1573 gdk_flush();
7c5e6fc6 1574
1e6feb95
VZ
1575 bool hasMouse = winUnderMouse == gdk_event->window;
1576 if ( hasMouse != g_captureWindowHasMouse )
1577 {
1578 // the mouse changed window
1579 g_captureWindowHasMouse = hasMouse;
1580
17a1ebd1
VZ
1581 wxMouseEvent eventM(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW
1582 : wxEVT_LEAVE_WINDOW);
1583 InitMouseEvent(win, eventM, gdk_event);
1584 eventM.SetEventObject(win);
97687291 1585 win->GTKProcessEvent(eventM);
1e6feb95
VZ
1586 }
1587 }
1588 else // no capture
1589 {
d1f2ac45 1590 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
ccb9fa0d
RD
1591
1592 // reset the event object and id in case win changed.
1593 event.SetEventObject( win );
1594 event.SetId( win->GetId() );
2f2aa628 1595 }
47d67540 1596
38f69be1
RR
1597 if ( !g_captureWindow )
1598 {
1599 wxSetCursorEvent cevent( event.m_x, event.m_y );
97687291 1600 if (win->GTKProcessEvent( cevent ))
38f69be1 1601 {
ec1e0c66 1602 win->SetCursor( cevent.GetCursor() );
38f69be1
RR
1603 }
1604 }
2e1f5012 1605
5e513780 1606 bool ret = win->GTKProcessEvent(event);
fcb29b23 1607
5e513780
RR
1608 g_lastMouseEvent = NULL;
1609
1610 return ret;
362c6693 1611}
c801d85f 1612
557c9f5b 1613//-----------------------------------------------------------------------------
40e5ebbf 1614// "scroll_event" (mouse wheel event)
557c9f5b
JS
1615//-----------------------------------------------------------------------------
1616
53f1a5b1
PC
1617// Compute lines/columns per action the same way as private GTK+ function
1618// _gtk_range_get_wheel_delta()
1619static inline int GetWheelScrollActionDelta(GtkRange* range)
7f251559 1620{
8a1554ed 1621 int delta = 3;
53f1a5b1 1622 if (range)
7f251559 1623 {
08f09504 1624 GtkAdjustment* adj = gtk_range_get_adjustment(range);
53f1a5b1 1625 const double page_size = gtk_adjustment_get_page_size(adj);
8a1554ed 1626 delta = wxRound(pow(page_size, 2.0 / 3.0));
7f251559 1627 }
8a1554ed 1628 return delta;
7f251559
RR
1629}
1630
7f7beb1d 1631static gboolean
76e4be8e 1632window_scroll_event(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
557c9f5b 1633{
76e4be8e 1634 wxMouseEvent event(wxEVT_MOUSEWHEEL);
b4e43132 1635 InitMouseEvent(win, event, gdk_event);
c53c7fad 1636
60773911 1637 event.m_wheelDelta = 120;
4eeceb15 1638
8a1554ed
PC
1639#if GTK_CHECK_VERSION(3,4,0)
1640 if (gdk_event->direction == GDK_SCROLL_SMOOTH)
1641 {
1642 bool processed_x = false;
1643 if (gdk_event->delta_x)
1644 {
1645 event.m_wheelAxis = wxMOUSE_WHEEL_HORIZONTAL;
1646 event.m_wheelRotation = int(event.m_wheelDelta * gdk_event->delta_x);
1647 GtkRange* range = win->m_scrollBar[wxWindow::ScrollDir_Horz];
1648 event.m_linesPerAction = GetWheelScrollActionDelta(range);
1649 event.m_columnsPerAction = event.m_linesPerAction;
1650 processed_x = win->GTKProcessEvent(event);
1651 }
1652 bool processed_y = false;
1653 if (gdk_event->delta_y)
1654 {
1655 event.m_wheelAxis = wxMOUSE_WHEEL_VERTICAL;
1656 event.m_wheelRotation = int(event.m_wheelDelta * gdk_event->delta_y);
1657 GtkRange* range = win->m_scrollBar[wxWindow::ScrollDir_Vert];
1658 event.m_linesPerAction = GetWheelScrollActionDelta(range);
1659 event.m_columnsPerAction = event.m_linesPerAction;
1660 processed_y = win->GTKProcessEvent(event);
1661 }
1662 return processed_x || processed_y;
1663 }
1664#endif // GTK_CHECK_VERSION(3,4,0)
6ba718d7
VZ
1665 GtkRange *range;
1666 switch (gdk_event->direction)
1667 {
1668 case GDK_SCROLL_UP:
1669 case GDK_SCROLL_DOWN:
1670 range = win->m_scrollBar[wxWindow::ScrollDir_Vert];
53f1a5b1 1671 event.m_wheelAxis = wxMOUSE_WHEEL_VERTICAL;
6ba718d7 1672 break;
6ba718d7
VZ
1673 case GDK_SCROLL_LEFT:
1674 case GDK_SCROLL_RIGHT:
1675 range = win->m_scrollBar[wxWindow::ScrollDir_Horz];
53f1a5b1 1676 event.m_wheelAxis = wxMOUSE_WHEEL_HORIZONTAL;
6ba718d7 1677 break;
6ba718d7
VZ
1678 default:
1679 return false;
1680 }
7f251559 1681
53f1a5b1
PC
1682 event.m_wheelRotation = event.m_wheelDelta;
1683 if (gdk_event->direction == GDK_SCROLL_UP ||
1684 gdk_event->direction == GDK_SCROLL_LEFT)
7f251559 1685 {
53f1a5b1 1686 event.m_wheelRotation = -event.m_wheelRotation;
7f251559 1687 }
53f1a5b1
PC
1688 event.m_linesPerAction = GetWheelScrollActionDelta(range);
1689 event.m_columnsPerAction = event.m_linesPerAction;
7f251559 1690
53f1a5b1 1691 return win->GTKProcessEvent(event);
557c9f5b 1692}
ac103441 1693
8a1554ed
PC
1694#if GTK_CHECK_VERSION(3,4,0)
1695static gboolean
1696hscrollbar_scroll_event(GtkWidget* widget, GdkEventScroll* gdk_event, wxWindow* win)
1697{
1698 GdkEventScroll event2;
1699 if (gdk_event->direction == GDK_SCROLL_SMOOTH && gdk_event->delta_x == 0)
1700 {
1701 memcpy(&event2, gdk_event, sizeof(event2));
1702 event2.delta_x = event2.delta_y;
1703 event2.delta_y = 0;
1704 gdk_event = &event2;
1705 }
1706 return window_scroll_event(widget, gdk_event, win);
1707}
1708
1709static gboolean
1710vscrollbar_scroll_event(GtkWidget* widget, GdkEventScroll* gdk_event, wxWindow* win)
1711{
1712 GdkEventScroll event2;
1713 if (gdk_event->direction == GDK_SCROLL_SMOOTH && gdk_event->delta_y == 0)
1714 {
1715 memcpy(&event2, gdk_event, sizeof(event2));
1716 event2.delta_y = event2.delta_x;
1717 event2.delta_x = 0;
1718 gdk_event = &event2;
1719 }
1720 return window_scroll_event(widget, gdk_event, win);
1721}
1722#endif // GTK_CHECK_VERSION(3,4,0)
1723
ac103441
RR
1724//-----------------------------------------------------------------------------
1725// "popup-menu"
1726//-----------------------------------------------------------------------------
ef5c70f9 1727
ac103441
RR
1728static gboolean wxgtk_window_popup_menu_callback(GtkWidget*, wxWindowGTK* win)
1729{
ac9d38d8 1730 wxContextMenuEvent event(wxEVT_CONTEXT_MENU, win->GetId(), wxPoint(-1, -1));
ac103441 1731 event.SetEventObject(win);
97687291 1732 return win->GTKProcessEvent(event);
ac103441 1733}
557c9f5b 1734
c801d85f 1735//-----------------------------------------------------------------------------
2f2aa628
RR
1736// "focus_in_event"
1737//-----------------------------------------------------------------------------
c801d85f 1738
7f7beb1d 1739static gboolean
e4161a2a 1740gtk_window_focus_in_callback( GtkWidget * WXUNUSED(widget),
7f7beb1d 1741 GdkEventFocus *WXUNUSED(event),
bd2e08d0 1742 wxWindowGTK *win )
c801d85f 1743{
bd2e08d0 1744 return win->GTKHandleFocusIn();
362c6693 1745}
c801d85f
KB
1746
1747//-----------------------------------------------------------------------------
2f2aa628
RR
1748// "focus_out_event"
1749//-----------------------------------------------------------------------------
c801d85f 1750
7f7beb1d 1751static gboolean
e4161a2a
VZ
1752gtk_window_focus_out_callback( GtkWidget * WXUNUSED(widget),
1753 GdkEventFocus * WXUNUSED(gdk_event),
7f7beb1d 1754 wxWindowGTK *win )
c801d85f 1755{
bd2e08d0 1756 return win->GTKHandleFocusOut();
362c6693 1757}
c801d85f 1758
bd2e08d0
VS
1759//-----------------------------------------------------------------------------
1760// "focus"
1761//-----------------------------------------------------------------------------
1762
28e88942
VZ
1763static gboolean
1764wx_window_focus_callback(GtkWidget *widget,
e4161a2a 1765 GtkDirectionType WXUNUSED(direction),
28e88942
VZ
1766 wxWindowGTK *win)
1767{
08f53168 1768 // the default handler for focus signal in GtkScrolledWindow sets
28e88942
VZ
1769 // focus to the window itself even if it doesn't accept focus, i.e. has no
1770 // GTK_CAN_FOCUS in its style -- work around this by forcibly preventing
1771 // the signal from reaching gtk_scrolled_window_focus() if we don't have
1772 // any children which might accept focus (we know we don't accept the focus
1773 // ourselves as this signal is only connected in this case)
1774 if ( win->GetChildren().empty() )
1775 g_signal_stop_emission_by_name(widget, "focus");
1776
1777 // we didn't change the focus
1778 return FALSE;
1779}
1780
b4071e91
RR
1781//-----------------------------------------------------------------------------
1782// "enter_notify_event"
1783//-----------------------------------------------------------------------------
1784
f3a5f83a 1785static gboolean
4acce087 1786gtk_window_enter_callback( GtkWidget*,
f3a5f83a
MR
1787 GdkEventCrossing *gdk_event,
1788 wxWindowGTK *win )
b4071e91 1789{
5478f221 1790 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
47d67540 1791
7f5f144a
RR
1792 // Event was emitted after a grab
1793 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
7c5e6fc6 1794
edc1d330 1795 wxMouseEvent event( wxEVT_ENTER_WINDOW );
c5f9d156 1796 InitMouseEvent(win, event, gdk_event);
ff8bfdbb 1797
38f69be1
RR
1798 if ( !g_captureWindow )
1799 {
1800 wxSetCursorEvent cevent( event.m_x, event.m_y );
97687291 1801 if (win->GTKProcessEvent( cevent ))
38f69be1 1802 {
ec1e0c66 1803 win->SetCursor( cevent.GetCursor() );
38f69be1
RR
1804 }
1805 }
2e1f5012 1806
97687291 1807 return win->GTKProcessEvent(event);
b4071e91 1808}
47d67540 1809
b4071e91
RR
1810//-----------------------------------------------------------------------------
1811// "leave_notify_event"
1812//-----------------------------------------------------------------------------
1813
f3a5f83a 1814static gboolean
4acce087 1815gtk_window_leave_callback( GtkWidget*,
f3a5f83a
MR
1816 GdkEventCrossing *gdk_event,
1817 wxWindowGTK *win )
b4071e91 1818{
5478f221 1819 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
b292e2f5 1820
7f5f144a
RR
1821 // Event was emitted after an ungrab
1822 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
7c5e6fc6 1823
e380f72b 1824 wxMouseEvent event( wxEVT_LEAVE_WINDOW );
b4e43132 1825 InitMouseEvent(win, event, gdk_event);
ff8bfdbb 1826
97687291 1827 return win->GTKProcessEvent(event);
b4071e91 1828}
47d67540 1829
c801d85f 1830//-----------------------------------------------------------------------------
add7cadd 1831// "value_changed" from scrollbar
2f2aa628 1832//-----------------------------------------------------------------------------
c801d85f 1833
add7cadd
PC
1834static void
1835gtk_scrollbar_value_changed(GtkRange* range, wxWindow* win)
47d67540 1836{
71ead4bf 1837 wxEventType eventType = win->GTKGetScrollEventType(range);
add7cadd
PC
1838 if (eventType != wxEVT_NULL)
1839 {
1840 // Convert scroll event type to scrollwin event type
1841 eventType += wxEVT_SCROLLWIN_TOP - wxEVT_SCROLL_TOP;
22c9b211
VZ
1842
1843 // find the scrollbar which generated the event
1844 wxWindowGTK::ScrollDir dir = win->ScrollDirFromRange(range);
35510e48 1845
22c9b211 1846 // generate the corresponding wx event
bfeeb7f3 1847 const int orient = wxWindow::OrientFromScrollDir(dir);
add7cadd
PC
1848 wxScrollWinEvent event(eventType, win->GetScrollPos(orient), orient);
1849 event.SetEventObject(win);
22c9b211 1850
97687291 1851 win->GTKProcessEvent(event);
add7cadd 1852 }
362c6693 1853}
c801d85f 1854
cb43b372
RR
1855//-----------------------------------------------------------------------------
1856// "button_press_event" from scrollbar
1857//-----------------------------------------------------------------------------
1858
7f7beb1d 1859static gboolean
add7cadd 1860gtk_scrollbar_button_press_event(GtkRange*, GdkEventButton*, wxWindow* win)
cb43b372 1861{
0a164d4c 1862 g_blockEventsOnScroll = true;
add7cadd 1863 win->m_mouseButtonDown = true;
9e691f46 1864
add7cadd 1865 return false;
cb43b372
RR
1866}
1867
c918b2cd
PC
1868//-----------------------------------------------------------------------------
1869// "event_after" from scrollbar
1870//-----------------------------------------------------------------------------
1871
c918b2cd
PC
1872static void
1873gtk_scrollbar_event_after(GtkRange* range, GdkEvent* event, wxWindow* win)
1874{
1875 if (event->type == GDK_BUTTON_RELEASE)
1876 {
1877 g_signal_handlers_block_by_func(range, (void*)gtk_scrollbar_event_after, win);
1878
bfeeb7f3 1879 const int orient = wxWindow::OrientFromScrollDir(
22c9b211 1880 win->ScrollDirFromRange(range));
74ab5f5b
VZ
1881 wxScrollWinEvent evt(wxEVT_SCROLLWIN_THUMBRELEASE,
1882 win->GetScrollPos(orient), orient);
1883 evt.SetEventObject(win);
1884 win->GTKProcessEvent(evt);
c918b2cd
PC
1885 }
1886}
c918b2cd 1887
cb43b372
RR
1888//-----------------------------------------------------------------------------
1889// "button_release_event" from scrollbar
1890//-----------------------------------------------------------------------------
1891
7f7beb1d 1892static gboolean
add7cadd 1893gtk_scrollbar_button_release_event(GtkRange* range, GdkEventButton*, wxWindow* win)
cb43b372 1894{
0a164d4c 1895 g_blockEventsOnScroll = false;
add7cadd
PC
1896 win->m_mouseButtonDown = false;
1897 // If thumb tracking
2a23d363 1898 if (win->m_isScrolling)
88413fec 1899 {
add7cadd 1900 win->m_isScrolling = false;
c918b2cd 1901 // Hook up handler to send thumb release event after this emission is finished.
8ea30e36
PC
1902 // To allow setting scroll position from event handler, sending event must
1903 // be deferred until after the GtkRange handler for this signal has run
c918b2cd 1904 g_signal_handlers_unblock_by_func(range, (void*)gtk_scrollbar_event_after, win);
88413fec
RR
1905 }
1906
add7cadd 1907 return false;
cb43b372 1908}
ca298c88 1909
a2053b27
RR
1910//-----------------------------------------------------------------------------
1911// "realize" from m_widget
1912//-----------------------------------------------------------------------------
1913
7f7beb1d 1914static void
514b0e13 1915gtk_window_realized_callback(GtkWidget* WXUNUSED(widget), wxWindowGTK* win)
a2053b27 1916{
612515af
VZ
1917 win->GTKHandleRealized();
1918}
1919
b79395c5 1920//-----------------------------------------------------------------------------
cca410b3 1921// "size_allocate" from m_wxwindow or m_widget
b79395c5
RR
1922//-----------------------------------------------------------------------------
1923
cca410b3
PC
1924static void
1925size_allocate(GtkWidget*, GtkAllocation* alloc, wxWindow* win)
8f75cb6c 1926{
cca410b3
PC
1927 int w = alloc->width;
1928 int h = alloc->height;
1929 if (win->m_wxwindow)
5b8a521e 1930 {
9dc44eff
PC
1931 GtkBorder border;
1932 WX_PIZZA(win->m_wxwindow)->get_border(border);
1933 w -= border.left + border.right;
1934 h -= border.top + border.bottom;
cca410b3
PC
1935 if (w < 0) w = 0;
1936 if (h < 0) h = 0;
1937 }
3ffa8d8c
PC
1938 GtkAllocation a;
1939 gtk_widget_get_allocation(win->m_widget, &a);
1940 // update position for widgets in native containers, such as wxToolBar
cb41a69b
PC
1941 if (!WX_IS_PIZZA(gtk_widget_get_parent(win->m_widget)))
1942 {
1943 win->m_x = a.x;
1944 win->m_y = a.y;
1945 }
b0d053c1
PC
1946 win->m_useCachedClientSize = true;
1947 if (win->m_clientWidth != w || win->m_clientHeight != h)
cca410b3 1948 {
b0d053c1
PC
1949 win->m_clientWidth = w;
1950 win->m_clientHeight = h;
cca410b3
PC
1951 // this callback can be connected to m_wxwindow,
1952 // so always get size from m_widget->allocation
989d151c
PC
1953 win->m_width = a.width;
1954 win->m_height = a.height;
cca410b3
PC
1955 if (!win->m_nativeSizeEvent)
1956 {
1957 wxSizeEvent event(win->GetSize(), win->GetId());
1958 event.SetEventObject(win);
1959 win->GTKProcessEvent(event);
1960 }
5b8a521e 1961 }
8f75cb6c
RR
1962}
1963
7738af59 1964//-----------------------------------------------------------------------------
78cd9c69 1965// "grab_broken"
7738af59
VZ
1966//-----------------------------------------------------------------------------
1967
db885f2a
PC
1968#if GTK_CHECK_VERSION(2, 8, 0)
1969static gboolean
1970gtk_window_grab_broken( GtkWidget*,
7738af59
VZ
1971 GdkEventGrabBroken *event,
1972 wxWindow *win )
1973{
1974 // Mouse capture has been lost involuntarily, notify the application
db885f2a 1975 if(!event->keyboard && wxWindow::GetCapture() == win)
7738af59
VZ
1976 {
1977 wxMouseCaptureLostEvent evt( win->GetId() );
1978 evt.SetEventObject( win );
937013e0 1979 win->HandleWindowEvent( evt );
7738af59 1980 }
db885f2a 1981 return false;
7738af59 1982}
4b859ff4 1983#endif
7738af59 1984
013151c7 1985//-----------------------------------------------------------------------------
9dc44eff 1986// "style_set"/"style_updated"
013151c7
JS
1987//-----------------------------------------------------------------------------
1988
9dc44eff
PC
1989#ifdef __WXGTK3__
1990static void style_updated(GtkWidget*, wxWindow* win)
1991#else
1992static void style_updated(GtkWidget*, GtkStyle*, wxWindow* win)
1993#endif
013151c7 1994{
b0d053c1
PC
1995 wxSysColourChangedEvent event;
1996 event.SetEventObject(win);
1997 win->GTKProcessEvent(event);
013151c7
JS
1998}
1999
9dc44eff 2000//-----------------------------------------------------------------------------
603e7f6d 2001// "unrealize"
9dc44eff
PC
2002//-----------------------------------------------------------------------------
2003
2004static void unrealize(GtkWidget*, wxWindow* win)
2005{
603e7f6d 2006 win->GTKHandleUnrealize();
9dc44eff
PC
2007}
2008
ef5c70f9
VZ
2009} // extern "C"
2010
f0b87ef9
PC
2011void wxWindowGTK::GTKHandleRealized()
2012{
603e7f6d
PC
2013 if (IsFrozen())
2014 DoFreeze();
2015
b387025d
PC
2016 GdkWindow* const window = GTKGetDrawingWindow();
2017
afa98731 2018 if (m_imContext)
f0b87ef9
PC
2019 {
2020 gtk_im_context_set_client_window
2021 (
afa98731 2022 m_imContext,
b387025d 2023 window ? window
f0b87ef9
PC
2024 : gtk_widget_get_window(m_widget)
2025 );
2026 }
2027
14421681
VZ
2028 // Use composited window if background is transparent, if supported.
2029 if (m_backgroundStyle == wxBG_STYLE_TRANSPARENT)
2030 {
2031#if wxGTK_HAS_COMPOSITING_SUPPORT
2032 if (IsTransparentBackgroundSupported())
2033 {
14421681
VZ
2034 if (window)
2035 gdk_window_set_composited(window, true);
2036 }
2037 else
2038#endif // wxGTK_HAS_COMPOSITING_SUPPORT
2039 {
2040 // We revert to erase mode if transparency is not supported
2041 m_backgroundStyle = wxBG_STYLE_ERASE;
2042 }
2043 }
2044
b387025d
PC
2045#ifndef __WXGTK3__
2046 if (window && (
2047 m_backgroundStyle == wxBG_STYLE_PAINT ||
2048 m_backgroundStyle == wxBG_STYLE_TRANSPARENT))
f0b87ef9 2049 {
b387025d 2050 gdk_window_set_back_pixmap(window, NULL, false);
f0b87ef9 2051 }
b387025d 2052#endif
f0b87ef9 2053
514b0e13 2054 wxWindowCreateEvent event(static_cast<wxWindow*>(this));
f0b87ef9
PC
2055 event.SetEventObject( this );
2056 GTKProcessEvent( event );
2057
2058 GTKUpdateCursor(true, false);
9dc44eff 2059
b0d053c1 2060 if (m_wxwindow && IsTopLevel())
9dc44eff
PC
2061 {
2062 // attaching to style changed signal after realization avoids initial
2063 // changes we don't care about
c38373bd 2064 const gchar *detailed_signal =
9dc44eff 2065#ifdef __WXGTK3__
c38373bd 2066 "style_updated";
9dc44eff 2067#else
c38373bd 2068 "style_set";
9dc44eff 2069#endif
c38373bd
VZ
2070 g_signal_connect(m_wxwindow,
2071 detailed_signal,
9dc44eff
PC
2072 G_CALLBACK(style_updated), this);
2073 }
f0b87ef9
PC
2074}
2075
603e7f6d
PC
2076void wxWindowGTK::GTKHandleUnrealize()
2077{
2078 // unrealizing a frozen window seems to have some lingering effect
2079 // preventing updates to the affected area
2080 if (IsFrozen())
2081 DoThaw();
2082
2083 if (m_wxwindow)
2084 {
afa98731
VZ
2085 if (m_imContext)
2086 gtk_im_context_set_client_window(m_imContext, NULL);
603e7f6d 2087
b0d053c1
PC
2088 if (IsTopLevel())
2089 {
2090 g_signal_handlers_disconnect_by_func(
2091 m_wxwindow, (void*)style_updated, this);
2092 }
603e7f6d
PC
2093 }
2094}
2095
ef5c70f9
VZ
2096// ----------------------------------------------------------------------------
2097// this wxWindowBase function is implemented here (in platform-specific file)
2098// because it is static and so couldn't be made virtual
2099// ----------------------------------------------------------------------------
2100
2101wxWindow *wxWindowBase::DoFindFocus()
2102{
4ec2eca1 2103#if wxUSE_MENUS
39258ad7
VZ
2104 // For compatibility with wxMSW, pretend that showing a popup menu doesn't
2105 // change the focus and that it remains on the window showing it, even
2106 // though the real focus does change in GTK.
2107 extern wxMenu *wxCurrentPopupMenu;
2108 if ( wxCurrentPopupMenu )
2109 return wxCurrentPopupMenu->GetInvokingWindow();
4ec2eca1 2110#endif // wxUSE_MENUS
39258ad7 2111
22f43cb5 2112 wxWindowGTK *focus = gs_pendingFocus ? gs_pendingFocus : gs_currentFocus;
ef5c70f9 2113 // the cast is necessary when we compile in wxUniversal mode
5c33522f 2114 return static_cast<wxWindow*>(focus);
865bb325 2115}
63081513 2116
48200154 2117void wxWindowGTK::AddChildGTK(wxWindowGTK* child)
6ca41e57 2118{
9a33c3ef 2119 wxASSERT_MSG(m_wxwindow, "Cannot add a child to a window without a client area");
03647350 2120
9a33c3ef
FM
2121 // the window might have been scrolled already, we
2122 // have to adapt the position
48200154 2123 wxPizza* pizza = WX_PIZZA(m_wxwindow);
08f53168
PC
2124 child->m_x += pizza->m_scroll_x;
2125 child->m_y += pizza->m_scroll_y;
148cd9b6 2126
3b7067a0
PC
2127 pizza->put(child->m_widget,
2128 child->m_x, child->m_y, child->m_width, child->m_height);
6ca41e57
RR
2129}
2130
bbe0af5b
RR
2131//-----------------------------------------------------------------------------
2132// global functions
2133//-----------------------------------------------------------------------------
2134
1e6feb95 2135wxWindow *wxGetActiveWindow()
bbe0af5b 2136{
6cad4f1b 2137 return wxWindow::FindFocus();
bbe0af5b
RR
2138}
2139
7dd40b6f 2140
a7130edb
VZ
2141// Under Unix this is implemented using X11 functions in utilsx11.cpp but we
2142// need to have this function under Windows too, so provide at least a stub.
a73251a8 2143#ifndef GDK_WINDOWING_X11
a7130edb
VZ
2144bool wxGetKeyState(wxKeyCode WXUNUSED(key))
2145{
2146 wxFAIL_MSG(wxS("Not implemented under Windows"));
2147 return false;
2148}
2149#endif // __WINDOWS__
2150
6bfcc4ec 2151static GdkDisplay* GetDisplay()
9dc44eff
PC
2152{
2153 wxWindow* tlw = NULL;
2154 if (!wxTopLevelWindows.empty())
2155 tlw = wxTopLevelWindows.front();
2156 GdkDisplay* display;
2157 if (tlw && tlw->m_widget)
2158 display = gtk_widget_get_display(tlw->m_widget);
2159 else
2160 display = gdk_display_get_default();
6bfcc4ec 2161 return display;
9dc44eff
PC
2162}
2163
7dd40b6f
RD
2164wxMouseState wxGetMouseState()
2165{
2166 wxMouseState ms;
2167
2168 gint x;
2169 gint y;
2170 GdkModifierType mask;
2171
6bfcc4ec
PC
2172 GdkDisplay* display = GetDisplay();
2173#ifdef __WXGTK3__
2174 GdkDeviceManager* manager = gdk_display_get_device_manager(display);
2175 GdkDevice* device = gdk_device_manager_get_client_pointer(manager);
2176 GdkScreen* screen;
2177 gdk_device_get_position(device, &screen, &x, &y);
2178 GdkWindow* window = gdk_screen_get_root_window(screen);
2179 gdk_device_get_state(device, window, NULL, &mask);
2180#else
2181 gdk_display_get_pointer(display, NULL, &x, &y, &mask);
2182#endif
7dd40b6f
RD
2183
2184 ms.SetX(x);
2185 ms.SetY(y);
c4021a79
PC
2186 ms.SetLeftDown((mask & GDK_BUTTON1_MASK) != 0);
2187 ms.SetMiddleDown((mask & GDK_BUTTON2_MASK) != 0);
2188 ms.SetRightDown((mask & GDK_BUTTON3_MASK) != 0);
e7cda1c3 2189 // see the comment in InitMouseEvent()
c4021a79
PC
2190 ms.SetAux1Down((mask & GDK_BUTTON4_MASK) != 0);
2191 ms.SetAux2Down((mask & GDK_BUTTON5_MASK) != 0);
2192
2193 ms.SetControlDown((mask & GDK_CONTROL_MASK) != 0);
2194 ms.SetShiftDown((mask & GDK_SHIFT_MASK) != 0);
2195 ms.SetAltDown((mask & GDK_MOD1_MASK) != 0);
2196 ms.SetMetaDown((mask & GDK_META_MASK) != 0);
3d257b8d 2197
7dd40b6f
RD
2198 return ms;
2199}
3d257b8d 2200
c801d85f 2201//-----------------------------------------------------------------------------
1e6feb95 2202// wxWindowGTK
c801d85f
KB
2203//-----------------------------------------------------------------------------
2204
6522713c
VZ
2205// in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2206// method
1e6feb95 2207#ifdef __WXUNIVERSAL__
6522713c 2208 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK, wxWindowBase)
28953245 2209#endif // __WXUNIVERSAL__
c801d85f 2210
1e6feb95 2211void wxWindowGTK::Init()
c801d85f 2212{
f03fc89f 2213 // GTK specific
d3b9f782
VZ
2214 m_widget = NULL;
2215 m_wxwindow = NULL;
2216 m_focusWidget = NULL;
8bbe427f 2217
f03fc89f 2218 // position/size
a2053b27
RR
2219 m_x = 0;
2220 m_y = 0;
2221 m_width = 0;
e380f72b 2222 m_height = 0;
8bbe427f 2223
c6212a0c 2224 m_showOnIdle = false;
148cd9b6 2225
0a164d4c
WS
2226 m_noExpose = false;
2227 m_nativeSizeEvent = false;
9dc44eff
PC
2228#ifdef __WXGTK3__
2229 m_paintContext = NULL;
80a77e3c 2230 m_styleProvider = NULL;
9dc44eff 2231#endif
94633ad9 2232
0a164d4c 2233 m_isScrolling = false;
add7cadd 2234 m_mouseButtonDown = false;
add7cadd 2235
22c9b211
VZ
2236 // initialize scrolling stuff
2237 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2238 {
2239 m_scrollBar[dir] = NULL;
2240 m_scrollPos[dir] = 0;
22c9b211 2241 }
f03fc89f 2242
b0d053c1
PC
2243 m_clientWidth =
2244 m_clientHeight = 0;
2245 m_useCachedClientSize = false;
8bbe427f 2246
0a164d4c 2247 m_clipPaintRegion = false;
b6fa52db 2248
5e014a0c 2249 m_cursor = *wxSTANDARD_CURSOR;
2daa0ce9 2250
afa98731
VZ
2251 m_imContext = NULL;
2252 m_imKeyEvent = NULL;
2253
a589495e 2254 m_dirtyTabOrder = false;
362c6693 2255}
c801d85f 2256
1e6feb95 2257wxWindowGTK::wxWindowGTK()
68995f26
VZ
2258{
2259 Init();
2260}
2261
1e6feb95
VZ
2262wxWindowGTK::wxWindowGTK( wxWindow *parent,
2263 wxWindowID id,
2264 const wxPoint &pos,
2265 const wxSize &size,
2266 long style,
2267 const wxString &name )
6ca41e57 2268{
68995f26
VZ
2269 Init();
2270
e380f72b 2271 Create( parent, id, pos, size, style, name );
6ca41e57 2272}
8bbe427f 2273
992ea406
VZ
2274void wxWindowGTK::GTKCreateScrolledWindowWith(GtkWidget* view)
2275{
2276 wxASSERT_MSG( HasFlag(wxHSCROLL) || HasFlag(wxVSCROLL),
2277 wxS("Must not be called if scrolling is not needed.") );
2278
2279 m_widget = gtk_scrolled_window_new( NULL, NULL );
2280
2281 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
2282
2283 // There is a conflict with default bindings at GTK+
2284 // level between scrolled windows and notebooks both of which want to use
2285 // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
2286 // direction and notebooks for changing pages -- we decide that if we don't
2287 // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
2288 // means we can get working keyboard navigation in notebooks
2289 if ( !HasFlag(wxHSCROLL) )
2290 {
2291 GtkBindingSet *
2292 bindings = gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget));
2293 if ( bindings )
2294 {
2295 gtk_binding_entry_remove(bindings, GDK_Page_Up, GDK_CONTROL_MASK);
2296 gtk_binding_entry_remove(bindings, GDK_Page_Down, GDK_CONTROL_MASK);
2297 }
2298 }
2299
d8ba3099
VZ
2300 // If wx[HV]SCROLL is not given, the corresponding scrollbar is not shown
2301 // at all. Otherwise it may be shown only on demand (default) or always, if
2302 // the wxALWAYS_SHOW_SB is specified.
2303 GtkPolicyType horzPolicy = HasFlag(wxHSCROLL)
2304 ? HasFlag(wxALWAYS_SHOW_SB)
2305 ? GTK_POLICY_ALWAYS
2306 : GTK_POLICY_AUTOMATIC
2307 : GTK_POLICY_NEVER;
2308 GtkPolicyType vertPolicy = HasFlag(wxVSCROLL)
2309 ? HasFlag(wxALWAYS_SHOW_SB)
2310 ? GTK_POLICY_ALWAYS
2311 : GTK_POLICY_AUTOMATIC
2312 : GTK_POLICY_NEVER;
2313 gtk_scrolled_window_set_policy( scrolledWindow, horzPolicy, vertPolicy );
992ea406
VZ
2314
2315 m_scrollBar[ScrollDir_Horz] = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(scrolledWindow));
2316 m_scrollBar[ScrollDir_Vert] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(scrolledWindow));
2317 if (GetLayoutDirection() == wxLayout_RightToLeft)
2318 gtk_range_set_inverted( m_scrollBar[ScrollDir_Horz], TRUE );
2319
2320 gtk_container_add( GTK_CONTAINER(m_widget), view );
2321
2322 // connect various scroll-related events
2323 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2324 {
2325 // these handlers block mouse events to any window during scrolling
2326 // such as motion events and prevent GTK and wxWidgets from fighting
2327 // over where the slider should be
2328 g_signal_connect(m_scrollBar[dir], "button_press_event",
2329 G_CALLBACK(gtk_scrollbar_button_press_event), this);
2330 g_signal_connect(m_scrollBar[dir], "button_release_event",
2331 G_CALLBACK(gtk_scrollbar_button_release_event), this);
2332
2333 gulong handler_id = g_signal_connect(m_scrollBar[dir], "event_after",
2334 G_CALLBACK(gtk_scrollbar_event_after), this);
2335 g_signal_handler_block(m_scrollBar[dir], handler_id);
2336
2337 // these handlers get notified when scrollbar slider moves
2338 g_signal_connect_after(m_scrollBar[dir], "value_changed",
2339 G_CALLBACK(gtk_scrollbar_value_changed), this);
2340 }
2341
2342 gtk_widget_show( view );
2343}
2344
1e6feb95
VZ
2345bool wxWindowGTK::Create( wxWindow *parent,
2346 wxWindowID id,
2347 const wxPoint &pos,
2348 const wxSize &size,
2349 long style,
2350 const wxString &name )
c801d85f 2351{
e5048854
JS
2352 // Get default border
2353 wxBorder border = GetBorder(style);
9800347d 2354
e5048854
JS
2355 style &= ~wxBORDER_MASK;
2356 style |= border;
ac9d38d8 2357
4dcaf11a
RR
2358 if (!PreCreation( parent, pos, size ) ||
2359 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
2360 {
1e6feb95 2361 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
0a164d4c 2362 return false;
4dcaf11a 2363 }
47d67540 2364
7da4a9cf
RR
2365 // We should accept the native look
2366#if 0
2367 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
2368 scroll_class->scrollbar_spacing = 0;
2369#endif
2370
e5048854 2371
0f52f610 2372 m_wxwindow = wxPizza::New(m_windowStyle);
75f661bb
PC
2373#ifndef __WXUNIVERSAL__
2374 if (HasFlag(wxPizza::BORDER_STYLES))
2375 {
2376 g_signal_connect(m_wxwindow, "parent_set",
2377 G_CALLBACK(parent_set), this);
2378 }
2379#endif
3e09bcfd 2380 if (!HasFlag(wxHSCROLL) && !HasFlag(wxVSCROLL))
c9192212 2381 m_widget = m_wxwindow;
6552c7af 2382 else
992ea406 2383 GTKCreateScrolledWindowWith(m_wxwindow);
9ff9d30c 2384 g_object_ref(m_widget);
47d67540 2385
f03fc89f
VZ
2386 if (m_parent)
2387 m_parent->DoAddChild( this );
94633ad9 2388
76fcf0f2 2389 m_focusWidget = m_wxwindow;
8bbe427f 2390
29901843
VS
2391 SetCanFocus(AcceptsFocus());
2392
e380f72b 2393 PostCreation();
8bbe427f 2394
0a164d4c 2395 return true;
362c6693 2396}
c801d85f 2397
8ab75332
PC
2398void wxWindowGTK::GTKDisconnect(void* instance)
2399{
2400 g_signal_handlers_disconnect_matched(instance,
2401 GSignalMatchType(G_SIGNAL_MATCH_DATA), 0, 0, NULL, NULL, this);
2402}
2403
1e6feb95 2404wxWindowGTK::~wxWindowGTK()
c801d85f 2405{
7de59551
RD
2406 SendDestroyEvent();
2407
22f43cb5
VS
2408 if (gs_currentFocus == this)
2409 gs_currentFocus = NULL;
2410 if (gs_pendingFocus == this)
2411 gs_pendingFocus = NULL;
44cd54c2 2412
bd2e08d0
VS
2413 if ( gs_deferredFocusOut == this )
2414 gs_deferredFocusOut = NULL;
3e679f01 2415
7df04680
VZ
2416 // Unlike the above cases, which can happen in normal circumstances, a
2417 // window shouldn't be destroyed while it still has capture, so even though
2418 // we still reset the global pointer to avoid leaving it dangling and
2419 // crashing afterwards, also complain about it.
2420 if ( g_captureWindow == this )
2421 {
2422 wxFAIL_MSG( wxS("Destroying window with mouse capture") );
2423 g_captureWindow = NULL;
2424 }
2425
eaa8686b
PC
2426 if (m_wxwindow)
2427 {
8ab75332 2428 GTKDisconnect(m_wxwindow);
eaa8686b
PC
2429 GtkWidget* parent = gtk_widget_get_parent(m_wxwindow);
2430 if (parent)
2431 GTKDisconnect(parent);
2432 }
2433 if (m_widget && m_widget != m_wxwindow)
2434 GTKDisconnect(m_widget);
47d67540 2435
02c3e53b
JS
2436 // destroy children before destroying this window itself
2437 DestroyChildren();
2438
f6551618 2439 // delete before the widgets to avoid a crash on solaris
afa98731
VZ
2440 if ( m_imContext )
2441 {
2442 g_object_unref(m_imContext);
2443 m_imContext = NULL;
2444 }
f6551618 2445
2f55b88c
PC
2446 // avoid problem with GTK+ 2.18 where a frozen window causes the whole
2447 // TLW to be frozen, and if the window is then destroyed, nothing ever
2448 // gets painted again
cec92136
PC
2449 while (IsFrozen())
2450 Thaw();
2f55b88c 2451
80a77e3c
PC
2452#ifdef __WXGTK3__
2453 if (m_styleProvider)
2454 g_object_unref(m_styleProvider);
2455#endif
2456
f03fc89f 2457 if (m_widget)
a2053b27 2458 {
9ff9d30c
PC
2459 // Note that gtk_widget_destroy() does not destroy the widget, it just
2460 // emits the "destroy" signal. The widget is not actually destroyed
2461 // until its reference count drops to zero.
2462 gtk_widget_destroy(m_widget);
2463 // Release our reference, should be the last one
2464 g_object_unref(m_widget);
2465 m_widget = NULL;
a2053b27 2466 }
9ff9d30c 2467 m_wxwindow = NULL;
362c6693 2468}
c801d85f 2469
1e6feb95 2470bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
c801d85f 2471{
e8375af8
VZ
2472 if ( GTKNeedsParent() )
2473 {
2474 wxCHECK_MSG( parent, false, wxT("Must have non-NULL parent") );
2475 }
8bbe427f 2476
a7c26d10
RD
2477 // Use either the given size, or the default if -1 is given.
2478 // See wxWindowBase for these functions.
3013a903 2479 m_width = WidthDefault(size.x) ;
f03fc89f 2480 m_height = HeightDefault(size.y);
8bbe427f 2481
b4261b20
PC
2482 if (pos != wxDefaultPosition)
2483 {
2484 m_x = pos.x;
2485 m_y = pos.y;
2486 }
8bbe427f 2487
0a164d4c 2488 return true;
c801d85f
KB
2489}
2490
1e6feb95 2491void wxWindowGTK::PostCreation()
c801d85f 2492{
82b978d7
RD
2493 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2494
14421681
VZ
2495#if wxGTK_HAS_COMPOSITING_SUPPORT
2496 // Set RGBA visual as soon as possible to minimize the possibility that
2497 // somebody uses the wrong one.
2498 if ( m_backgroundStyle == wxBG_STYLE_TRANSPARENT &&
2499 IsTransparentBackgroundSupported() )
2500 {
2501 GdkScreen *screen = gtk_widget_get_screen (m_widget);
9dc44eff
PC
2502#ifdef __WXGTK3__
2503 gtk_widget_set_visual(m_widget, gdk_screen_get_rgba_visual(screen));
2504#else
14421681
VZ
2505 GdkColormap *rgba_colormap = gdk_screen_get_rgba_colormap (screen);
2506
2507 if (rgba_colormap)
2508 gtk_widget_set_colormap(m_widget, rgba_colormap);
9dc44eff 2509#endif
14421681
VZ
2510 }
2511#endif // wxGTK_HAS_COMPOSITING_SUPPORT
2512
43a18898
RR
2513 if (m_wxwindow)
2514 {
147bc491 2515 if (!m_noExpose)
b02da6b1 2516 {
77ffb593 2517 // these get reported to wxWidgets -> wxPaintEvent
9dc44eff
PC
2518#ifdef __WXGTK3__
2519 g_signal_connect(m_wxwindow, "draw", G_CALLBACK(draw), this);
2520#else
2521 g_signal_connect(m_wxwindow, "expose_event", G_CALLBACK(expose_event), this);
2522#endif
147bc491 2523
847dfdb4 2524 if (GetLayoutDirection() == wxLayout_LeftToRight)
08f53168 2525 gtk_widget_set_redraw_on_allocate(m_wxwindow, HasFlag(wxFULL_REPAINT_ON_RESIZE));
93d23d8f 2526 }
2b5f62a0 2527
ed56a258 2528 // Create input method handler
afa98731 2529 m_imContext = gtk_im_multicontext_new();
ed56a258 2530
2b5f62a0 2531 // Cannot handle drawing preedited text yet
afa98731 2532 gtk_im_context_set_use_preedit( m_imContext, FALSE );
2b5f62a0 2533
afa98731 2534 g_signal_connect (m_imContext, "commit",
da210120 2535 G_CALLBACK (gtk_wxwindow_commit_cb), this);
43a18898 2536 }
47d67540 2537
76fcf0f2 2538 // focus handling
63081513 2539
06fda9e8
RR
2540 if (!GTK_IS_WINDOW(m_widget))
2541 {
2542 if (m_focusWidget == NULL)
2543 m_focusWidget = m_widget;
0a164d4c 2544
4c20ee63
RR
2545 if (m_wxwindow)
2546 {
2547 g_signal_connect (m_focusWidget, "focus_in_event",
9fa72bd2 2548 G_CALLBACK (gtk_window_focus_in_callback), this);
4c20ee63 2549 g_signal_connect (m_focusWidget, "focus_out_event",
f1e57cb9 2550 G_CALLBACK (gtk_window_focus_out_callback), this);
4c20ee63
RR
2551 }
2552 else
2553 {
2554 g_signal_connect_after (m_focusWidget, "focus_in_event",
2555 G_CALLBACK (gtk_window_focus_in_callback), this);
2556 g_signal_connect_after (m_focusWidget, "focus_out_event",
2557 G_CALLBACK (gtk_window_focus_out_callback), this);
2558 }
06fda9e8 2559 }
76fcf0f2 2560
28e88942
VZ
2561 if ( !AcceptsFocusFromKeyboard() )
2562 {
80332672 2563 SetCanFocus(false);
28e88942
VZ
2564
2565 g_signal_connect(m_widget, "focus",
2566 G_CALLBACK(wx_window_focus_callback), this);
2567 }
2568
76fcf0f2 2569 // connect to the various key and mouse handlers
63081513 2570
a2053b27 2571 GtkWidget *connect_widget = GetConnectWidget();
f03fc89f 2572
a2053b27 2573 ConnectWidget( connect_widget );
47d67540 2574
843101f7
VS
2575 // We cannot set colours, fonts and cursors before the widget has been
2576 // realized, so we do this directly after realization -- unless the widget
2577 // was in fact realized already.
2578 if ( gtk_widget_get_realized(connect_widget) )
2579 {
603e7f6d 2580 GTKHandleRealized();
843101f7
VS
2581 }
2582 else
2583 {
2584 g_signal_connect (connect_widget, "realize",
2585 G_CALLBACK (gtk_window_realized_callback), this);
2586 }
603e7f6d 2587 g_signal_connect(connect_widget, "unrealize", G_CALLBACK(unrealize), this);
2daa0ce9 2588
cca410b3
PC
2589 if (!IsTopLevel())
2590 {
2591 g_signal_connect(m_wxwindow ? m_wxwindow : m_widget, "size_allocate",
2592 G_CALLBACK(size_allocate), this);
2593 }
2594
db885f2a 2595#if GTK_CHECK_VERSION(2, 8, 0)
9dc44eff 2596#ifndef __WXGTK3__
bb8afd65 2597 if ( gtk_check_version(2,8,0) == NULL )
9dc44eff 2598#endif
bb8afd65
VZ
2599 {
2600 // Make sure we can notify the app when mouse capture is lost
2601 if ( m_wxwindow )
4b859ff4 2602 {
4b859ff4 2603 g_signal_connect (m_wxwindow, "grab_broken_event",
7738af59 2604 G_CALLBACK (gtk_window_grab_broken), this);
4b859ff4 2605 }
7738af59 2606
bb8afd65 2607 if ( connect_widget != m_wxwindow )
4b859ff4 2608 {
4b859ff4 2609 g_signal_connect (connect_widget, "grab_broken_event",
7738af59 2610 G_CALLBACK (gtk_window_grab_broken), this);
4b859ff4 2611 }
63081513 2612 }
bb8afd65 2613#endif // GTK+ >= 2.8
2daa0ce9 2614
3b7067a0
PC
2615 if (!WX_IS_PIZZA(gtk_widget_get_parent(m_widget)) && !GTK_IS_WINDOW(m_widget))
2616 gtk_widget_set_size_request(m_widget, m_width, m_height);
1e6feb95 2617
4116bc7e
PC
2618 // apply any font or color changes made before creation
2619 GTKApplyWidgetStyle();
2620
40bab631
VS
2621 InheritAttributes();
2622
978af864 2623 SetLayoutDirection(wxLayout_Default);
a433fbd5
VZ
2624
2625 // unless the window was created initially hidden (i.e. Hide() had been
2626 // called before Create()), we should show it at GTK+ level as well
2627 if ( IsShown() )
2628 gtk_widget_show( m_widget );
b4071e91
RR
2629}
2630
1da8e6e4
VZ
2631unsigned long
2632wxWindowGTK::GTKConnectWidget(const char *signal, wxGTKCallback callback)
a0c8bb73
VZ
2633{
2634 return g_signal_connect(m_widget, signal, callback, this);
2635}
2636
1e6feb95 2637void wxWindowGTK::ConnectWidget( GtkWidget *widget )
b4071e91 2638{
9fa72bd2
MR
2639 g_signal_connect (widget, "key_press_event",
2640 G_CALLBACK (gtk_window_key_press_callback), this);
2641 g_signal_connect (widget, "key_release_event",
2642 G_CALLBACK (gtk_window_key_release_callback), this);
2643 g_signal_connect (widget, "button_press_event",
2644 G_CALLBACK (gtk_window_button_press_callback), this);
2645 g_signal_connect (widget, "button_release_event",
2646 G_CALLBACK (gtk_window_button_release_callback), this);
2647 g_signal_connect (widget, "motion_notify_event",
2648 G_CALLBACK (gtk_window_motion_notify_callback), this);
35510e48 2649
9fa72bd2 2650 g_signal_connect (widget, "scroll_event",
76e4be8e 2651 G_CALLBACK (window_scroll_event), this);
8a1554ed
PC
2652 for (int i = 0; i < 2; i++)
2653 {
2654 GtkRange* range = m_scrollBar[i];
2655 if (range)
2656 {
2657#if GTK_CHECK_VERSION(3,4,0)
2658 GCallback cb = GCallback(i == ScrollDir_Horz
2659 ? hscrollbar_scroll_event
2660 : vscrollbar_scroll_event);
2661#else
2662 GCallback cb = GCallback(window_scroll_event);
2663#endif
2664 g_signal_connect(range, "scroll_event", cb, this);
2665 }
2666 }
35510e48 2667
9fa72bd2
MR
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);
362c6693 2674}
c801d85f 2675
3cc04de7
PC
2676static GSList* gs_queueResizeList;
2677
2678extern "C" {
2679static 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
1e6feb95 2697void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
23efdd02 2698{
401c8bc2 2699 gtk_widget_set_size_request(m_widget, width, height);
01a49fd4
PC
2700 GtkWidget* parent = gtk_widget_get_parent(m_widget);
2701 if (WX_IS_PIZZA(parent))
3b7067a0 2702 WX_PIZZA(parent)->move(m_widget, x, y, width, height);
3cc04de7
PC
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 }
23efdd02 2716}
2daa0ce9 2717
82008f15
PC
2718void 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
1e6feb95 2735void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
c801d85f 2736{
01a49fd4 2737 wxCHECK_RET(m_widget, "invalid window");
8bbe427f 2738
90dcb0d9
PC
2739 int scrollX = 0, scrollY = 0;
2740 GtkWidget* parent = gtk_widget_get_parent(m_widget);
2741 if (WX_IS_PIZZA(parent))
6d50fada 2742 {
90dcb0d9
PC
2743 wxPizza* pizza = WX_PIZZA(parent);
2744 scrollX = pizza->m_scroll_x;
2745 scrollY = pizza->m_scroll_y;
6d50fada 2746 }
90dcb0d9
PC
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;
a200c35e 2755
fe39b16a
RR
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
4d711190
PC
2767 if (width == -1)
2768 width = m_width;
2769 if (height == -1)
2770 height = m_height;
fe39b16a 2771
4d711190 2772 const bool sizeChange = m_width != width || m_height != height;
b0d053c1
PC
2773
2774 if (sizeChange)
2775 m_useCachedClientSize = false;
2776
4d711190 2777 if (sizeChange || m_x != x || m_y != y)
fb1585ae 2778 {
90dcb0d9
PC
2779 m_x = x;
2780 m_y = y;
4d711190
PC
2781 m_width = width;
2782 m_height = height;
47d67540 2783
863e0817 2784 /* the default button has a border around it */
fc9ab22a 2785 if (gtk_widget_get_can_default(m_widget))
c50f1fb9 2786 {
f893066b
RR
2787 GtkBorder *default_border = NULL;
2788 gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
2789 if (default_border)
863e0817 2790 {
0604cfd7
PC
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;
6cfdfed8 2795 gtk_border_free( default_border );
863e0817 2796 }
863e0817 2797 }
c50f1fb9 2798
0604cfd7 2799 DoMoveWindow(x, y, width, height);
54517652 2800 }
148cd9b6 2801
01a49fd4 2802 if ((sizeChange && !m_nativeSizeEvent) || (sizeFlags & wxSIZE_FORCE_EVENT))
5b8a521e 2803 {
cca410b3
PC
2804 // update these variables to keep size_allocate handler
2805 // from sending another size event for this change
b0d053c1 2806 DoGetClientSize(&m_clientWidth, &m_clientHeight);
6d693bb4 2807
e47e063a
RR
2808 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
2809 event.SetEventObject( this );
2810 HandleWindowEvent( event );
30760ce7 2811 }
362c6693 2812}
c801d85f 2813
71ead4bf 2814bool wxWindowGTK::GTKShowFromOnIdle()
9390a202 2815{
fc9ab22a 2816 if (IsShown() && m_showOnIdle && !gtk_widget_get_visible (m_widget))
f46ad98f
RR
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);
937013e0 2827 HandleWindowEvent(eventShow);
f46ad98f 2828 m_showOnIdle = false;
7317857d 2829 return true;
f46ad98f 2830 }
02761f6c 2831
7317857d
RR
2832 return false;
2833}
2834
2835void wxWindowGTK::OnInternalIdle()
2836{
bd2e08d0
VS
2837 if ( gs_deferredFocusOut )
2838 GTKHandleDeferredFocusOut();
2839
7317857d 2840 // Check if we have to show window now
71ead4bf 2841 if (GTKShowFromOnIdle()) return;
7317857d
RR
2842
2843 if ( m_dirtyTabOrder )
2844 {
2845 m_dirtyTabOrder = false;
2846 RealizeTabOrder();
2847 }
2848
6a50a2c4 2849 wxWindowBase::OnInternalIdle();
9390a202
RR
2850}
2851
1e6feb95 2852void wxWindowGTK::DoGetSize( int *width, int *height ) const
c801d85f 2853{
fb1585ae
RR
2854 if (width) (*width) = m_width;
2855 if (height) (*height) = m_height;
362c6693 2856}
c801d85f 2857
1e6feb95 2858void wxWindowGTK::DoSetClientSize( int width, int height )
c801d85f 2859{
82b978d7
RD
2860 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2861
949ff63e
PC
2862 const wxSize size = GetSize();
2863 const wxSize clientSize = GetClientSize();
2864 SetSize(width + (size.x - clientSize.x), height + (size.y - clientSize.y));
362c6693 2865}
c801d85f 2866
1e6feb95 2867void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
c801d85f 2868{
82b978d7 2869 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 2870
b0d053c1
PC
2871 if (m_useCachedClientSize)
2872 {
2873 if (width) *width = m_clientWidth;
2874 if (height) *height = m_clientHeight;
2875 return;
2876 }
2877
09bf8378
PC
2878 int w = m_width;
2879 int h = m_height;
2880
fcdd5335 2881 if ( m_wxwindow )
c801d85f 2882 {
8466fc74 2883 // if window is scrollable, account for scrollbars
fcdd5335 2884 if ( GTK_IS_SCROLLED_WINDOW(m_widget) )
8466fc74 2885 {
fcdd5335
VZ
2886 GtkPolicyType policy[ScrollDir_Max];
2887 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(m_widget),
2888 &policy[ScrollDir_Horz],
2889 &policy[ScrollDir_Vert]);
cbe6a493
PC
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 }
fcdd5335
VZ
2900
2901 for ( int i = 0; i < ScrollDir_Max; i++ )
8466fc74 2902 {
fcdd5335
VZ
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);
989d151c 2922 if (gtk_adjustment_get_upper(adj) <= gtk_adjustment_get_page_size(adj))
fcdd5335
VZ
2923 continue;
2924 }
2925
2926 GtkRequisition req;
9dc44eff
PC
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__
fcdd5335 2946 gtk_widget_size_request(GTK_WIDGET(range), &req);
8466fc74 2947 if (i == ScrollDir_Horz)
9dc44eff 2948 h -= req.height + scrollbar_spacing;
8466fc74 2949 else
9dc44eff
PC
2950 w -= req.width + scrollbar_spacing;
2951#endif // !__WXGTK3__
8466fc74
PC
2952 }
2953 }
09bf8378 2954
b3554952
VZ
2955 const wxSize sizeBorders = DoGetBorderSize();
2956 w -= sizeBorders.x;
2957 h -= sizeBorders.y;
9000c624 2958
69346023
PC
2959 if (w < 0)
2960 w = 0;
2961 if (h < 0)
2962 h = 0;
1ecc4d80 2963 }
1e6feb95 2964
09bf8378
PC
2965 if (width) *width = w;
2966 if (height) *height = h;
362c6693 2967}
c801d85f 2968
b3554952
VZ
2969wxSize wxWindowGTK::DoGetBorderSize() const
2970{
2971 if ( !m_wxwindow )
2972 return wxWindowBase::DoGetBorderSize();
2973
9dc44eff
PC
2974 GtkBorder border;
2975 WX_PIZZA(m_wxwindow)->get_border(border);
2976 return wxSize(border.left + border.right, border.top + border.bottom);
b3554952
VZ
2977}
2978
1e6feb95 2979void wxWindowGTK::DoGetPosition( int *x, int *y ) const
c801d85f 2980{
bf0c00c6
RR
2981 int dx = 0;
2982 int dy = 0;
b4261b20
PC
2983 GtkWidget* parent = NULL;
2984 if (m_widget)
2985 parent = gtk_widget_get_parent(m_widget);
2986 if (WX_IS_PIZZA(parent))
bf0c00c6 2987 {
b4261b20 2988 wxPizza* pizza = WX_PIZZA(parent);
08f53168
PC
2989 dx = pizza->m_scroll_x;
2990 dy = pizza->m_scroll_y;
bf0c00c6 2991 }
496beb3f
VS
2992 if (x) (*x) = m_x - dx;
2993 if (y) (*y) = m_y - dy;
362c6693 2994}
c801d85f 2995
1e6feb95 2996void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
c801d85f 2997{
82b978d7 2998 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 2999
989d151c 3000 if (gtk_widget_get_window(m_widget) == NULL) return;
a2053b27 3001
d3b9f782 3002 GdkWindow *source = NULL;
43a18898 3003 if (m_wxwindow)
989d151c 3004 source = gtk_widget_get_window(m_wxwindow);
43a18898 3005 else
989d151c 3006 source = gtk_widget_get_window(m_widget);
47d67540 3007
43a18898
RR
3008 int org_x = 0;
3009 int org_y = 0;
3010 gdk_window_get_origin( source, &org_x, &org_y );
c801d85f 3011
43a18898 3012 if (!m_wxwindow)
c801d85f 3013 {
fc9ab22a 3014 if (!gtk_widget_get_has_window(m_widget))
43a18898 3015 {
989d151c
PC
3016 GtkAllocation a;
3017 gtk_widget_get_allocation(m_widget, &a);
3018 org_x += a.x;
3019 org_y += a.y;
43a18898 3020 }
362c6693 3021 }
47d67540 3022
49e74855
RR
3023
3024 if (x)
3025 {
3026 if (GetLayoutDirection() == wxLayout_RightToLeft)
3027 *x = (GetClientSize().x - *x) + org_x;
fcb29b23 3028 else
49e74855
RR
3029 *x += org_x;
3030 }
fcb29b23 3031
43a18898 3032 if (y) *y += org_y;
362c6693 3033}
c801d85f 3034
1e6feb95 3035void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
c801d85f 3036{
82b978d7 3037 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 3038
989d151c 3039 if (!gtk_widget_get_realized(m_widget)) return;
a2053b27 3040
d3b9f782 3041 GdkWindow *source = NULL;
1ecc4d80 3042 if (m_wxwindow)
989d151c 3043 source = gtk_widget_get_window(m_wxwindow);
1ecc4d80 3044 else
989d151c 3045 source = gtk_widget_get_window(m_widget);
47d67540 3046
1ecc4d80
RR
3047 int org_x = 0;
3048 int org_y = 0;
3049 gdk_window_get_origin( source, &org_x, &org_y );
c801d85f 3050
1ecc4d80 3051 if (!m_wxwindow)
c801d85f 3052 {
fc9ab22a 3053 if (!gtk_widget_get_has_window(m_widget))
1ecc4d80 3054 {
989d151c
PC
3055 GtkAllocation a;
3056 gtk_widget_get_allocation(m_widget, &a);
3057 org_x += a.x;
3058 org_y += a.y;
1ecc4d80 3059 }
362c6693 3060 }
47d67540 3061
49e74855
RR
3062 if (x)
3063 {
3064 if (GetLayoutDirection() == wxLayout_RightToLeft)
3065 *x = (GetClientSize().x - *x) - org_x;
fcb29b23 3066 else
49e74855
RR
3067 *x -= org_x;
3068 }
1ecc4d80 3069 if (y) *y -= org_y;
362c6693 3070}
c801d85f 3071
1e6feb95 3072bool wxWindowGTK::Show( bool show )
c801d85f 3073{
3cc57044 3074 if ( !wxWindowBase::Show(show) )
739730ca
RR
3075 {
3076 // nothing to do
0a164d4c 3077 return false;
739730ca 3078 }
8bbe427f 3079
3cc57044
VZ
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 )
f46ad98f 3084 {
3cc57044
VZ
3085 wxASSERT_MSG( !show, "can't show invalid window" );
3086 return true;
f46ad98f 3087 }
3cc57044
VZ
3088
3089 if ( show )
f46ad98f 3090 {
3cc57044
VZ
3091 if ( m_showOnIdle )
3092 {
3093 // defer until later
3094 return true;
3095 }
3096
3097 gtk_widget_show(m_widget);
f46ad98f 3098 }
3cc57044
VZ
3099 else // hide
3100 {
3101 gtk_widget_hide(m_widget);
3102 }
3103
3104 wxShowEvent eventShow(GetId(), show);
3105 eventShow.SetEventObject(this);
3106 HandleWindowEvent(eventShow);
2b5f62a0 3107
0a164d4c 3108 return true;
362c6693 3109}
c801d85f 3110
47a8a4d5 3111void wxWindowGTK::DoEnable( bool enable )
fdca68a6 3112{
47a8a4d5 3113 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
1ecc4d80 3114
f03fc89f 3115 gtk_widget_set_sensitive( m_widget, enable );
6552c7af 3116 if (m_wxwindow && (m_wxwindow != m_widget))
f03fc89f 3117 gtk_widget_set_sensitive( m_wxwindow, enable );
362c6693 3118}
c801d85f 3119
1e6feb95 3120int wxWindowGTK::GetCharHeight() const
2f2aa628 3121{
82b978d7 3122 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
47d67540 3123
cc402e64 3124 wxFont font = GetFont();
a1b806b9 3125 wxCHECK_MSG( font.IsOk(), 12, wxT("invalid font") );
2f2aa628 3126
db885f2a 3127 PangoContext* context = gtk_widget_get_pango_context(m_widget);
bbd006c0
RR
3128
3129 if (!context)
3130 return 0;
3131
cc402e64 3132 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
bbd006c0
RR
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
3fe39b0c 3141 g_object_unref (layout);
7de59551 3142
f69e2009 3143 return (int) PANGO_PIXELS(rect.height);
362c6693 3144}
c801d85f 3145
1e6feb95 3146int wxWindowGTK::GetCharWidth() const
c33c4050 3147{
82b978d7 3148 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
47d67540 3149
cc402e64 3150 wxFont font = GetFont();
a1b806b9 3151 wxCHECK_MSG( font.IsOk(), 8, wxT("invalid font") );
47d67540 3152
db885f2a 3153 PangoContext* context = gtk_widget_get_pango_context(m_widget);
bbd006c0
RR
3154
3155 if (!context)
3156 return 0;
3157
cc402e64 3158 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
bbd006c0
RR
3159 PangoLayout *layout = pango_layout_new(context);
3160 pango_layout_set_font_description(layout, desc);
95c430aa 3161 pango_layout_set_text(layout, "g", 1);
bbd006c0
RR
3162 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3163
3164 PangoRectangle rect;
3165 pango_layout_line_get_extents(line, NULL, &rect);
3166
3fe39b0c 3167 g_object_unref (layout);
7de59551 3168
f69e2009 3169 return (int) PANGO_PIXELS(rect.width);
c33c4050
RR
3170}
3171
6de70470
VZ
3172void wxWindowGTK::DoGetTextExtent( const wxString& string,
3173 int *x,
3174 int *y,
3175 int *descent,
3176 int *externalLeading,
3177 const wxFont *theFont ) const
c33c4050 3178{
8cd79b7a
VZ
3179 // ensure we work with a valid font
3180 wxFont fontToUse;
3181 if ( !theFont || !theFont->IsOk() )
3182 fontToUse = GetFont();
3183 else
3184 fontToUse = *theFont;
47d67540 3185
a1b806b9 3186 wxCHECK_RET( fontToUse.IsOk(), wxT("invalid font") );
2b5f62a0 3187
8cd79b7a
VZ
3188 const wxWindow* win = static_cast<const wxWindow*>(this);
3189 wxTextMeasure txm(win, &fontToUse);
3190 txm.GetTextExtent(string, x, y, descent, externalLeading);
c33c4050
RR
3191}
3192
36a845fe
RR
3193void wxWindowGTK::GTKDisableFocusOutEvent()
3194{
3195 g_signal_handlers_block_by_func( m_focusWidget,
3196 (gpointer) gtk_window_focus_out_callback, this);
3197}
3198
3199void wxWindowGTK::GTKEnableFocusOutEvent()
3200{
3201 g_signal_handlers_unblock_by_func( m_focusWidget,
3202 (gpointer) gtk_window_focus_out_callback, this);
3203}
bd2e08d0
VS
3204
3205bool wxWindowGTK::GTKHandleFocusIn()
ef5c70f9 3206{
bd2e08d0
VS
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 )
ef5c70f9 3217 {
bd2e08d0
VS
3218 if ( GTKNeedsToFilterSameWindowFocus() &&
3219 gs_deferredFocusOut == this )
ef5c70f9 3220 {
bd2e08d0
VS
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;
ef5c70f9 3228 }
bd2e08d0
VS
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();
ef5c70f9
VZ
3234 }
3235
bd2e08d0
VS
3236
3237 wxLogTrace(TRACE_FOCUS,
3238 "handling focus_in event for %s(%p, %s)",
3239 GetClassInfo()->GetClassName(), this, GetLabel());
3240
afa98731
VZ
3241 if (m_imContext)
3242 gtk_im_context_focus_in(m_imContext);
bd2e08d0 3243
22f43cb5
VS
3244 gs_currentFocus = this;
3245 gs_pendingFocus = NULL;
bd2e08d0
VS
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.
b22bb1a0 3258 wxChildFocusEvent eventChildFocus(static_cast<wxWindow*>(this));
bd2e08d0
VS
3259 GTKProcessEvent(eventChildFocus);
3260
3261 wxFocusEvent eventFocus(wxEVT_SET_FOCUS, GetId());
3262 eventFocus.SetEventObject(this);
3263 GTKProcessEvent(eventFocus);
3264
3265 return retval;
ef5c70f9
VZ
3266}
3267
bd2e08d0 3268bool wxWindowGTK::GTKHandleFocusOut()
7cec1c9e 3269{
bd2e08d0
VS
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() )
7cec1c9e 3283 {
bd2e08d0
VS
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;
7cec1c9e
RR
3291 }
3292
bd2e08d0
VS
3293 GTKHandleFocusOutNoDeferring();
3294
3295 return retval;
3296}
3297
3298void wxWindowGTK::GTKHandleFocusOutNoDeferring()
3299{
3300 wxLogTrace(TRACE_FOCUS,
3301 "handling focus_out event for %s(%p, %s)",
3302 GetClassInfo()->GetClassName(), this, GetLabel());
3303
afa98731
VZ
3304 if (m_imContext)
3305 gtk_im_context_focus_out(m_imContext);
bd2e08d0 3306
22f43cb5 3307 if ( gs_currentFocus != this )
7cec1c9e 3308 {
22f43cb5 3309 // Something is terribly wrong, gs_currentFocus is out of sync with the
bd2e08d0
VS
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
22f43cb5 3314 // gs_currentFocus _should_ be NULL
bd2e08d0
VS
3315 //
3316 // * or it goes to another control, in which case focus-in event will
22f43cb5 3317 // follow immediately and it will set gs_currentFocus to the right
bd2e08d0
VS
3318 // value
3319 wxLogDebug("window %s(%p, %s) lost focus even though it didn't have it",
3320 GetClassInfo()->GetClassName(), this, GetLabel());
3321 }
22f43cb5 3322 gs_currentFocus = NULL;
db885f2a 3323
bd2e08d0
VS
3324#if wxUSE_CARET
3325 // caret needs to be informed about focus change
3326 wxCaret *caret = GetCaret();
3327 if ( caret )
3328 {
3329 caret->OnKillFocus();
354aa1e3 3330 }
bd2e08d0
VS
3331#endif // wxUSE_CARET
3332
3333 wxFocusEvent event( wxEVT_KILL_FOCUS, GetId() );
3334 event.SetEventObject( this );
9a237e2f 3335 event.SetWindow( FindFocus() );
bd2e08d0
VS
3336 GTKProcessEvent( event );
3337}
3338
3339/*static*/
3340void 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 )
c801d85f 3346 {
bd2e08d0
VS
3347 wxWindowGTK *win = gs_deferredFocusOut;
3348 gs_deferredFocusOut = NULL;
db885f2a 3349
bd2e08d0
VS
3350 wxLogTrace(TRACE_FOCUS,
3351 "processing deferred focus_out event for %s(%p, %s)",
3352 win->GetClassInfo()->GetClassName(), win, win->GetLabel());
fcb29b23 3353
bd2e08d0
VS
3354 win->GTKHandleFocusOutNoDeferring();
3355 }
3356}
0a164d4c 3357
bd2e08d0
VS
3358void wxWindowGTK::SetFocus()
3359{
3360 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
6aeb6f2a 3361
bd2e08d0
VS
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
4c51a665 3367 // has focus at the moment) takes effect only after the window is shown
bd2e08d0
VS
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.
22f43cb5 3374 gs_pendingFocus = this;
bd2e08d0
VS
3375
3376 GtkWidget *widget = m_wxwindow ? m_wxwindow : m_focusWidget;
3377
3378 if ( GTK_IS_CONTAINER(widget) &&
fc9ab22a 3379 !gtk_widget_get_can_focus(widget) )
bd2e08d0
VS
3380 {
3381 wxLogTrace(TRACE_FOCUS,
9a83f860 3382 wxT("Setting focus to a child of %s(%p, %s)"),
bd2e08d0
VS
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,
9a83f860 3389 wxT("Setting focus to %s(%p, %s)"),
bd2e08d0
VS
3390 GetClassInfo()->GetClassName(), this, GetLabel().c_str());
3391 gtk_widget_grab_focus(widget);
362c6693 3392 }
362c6693 3393}
c801d85f 3394
80332672
VZ
3395void wxWindowGTK::SetCanFocus(bool canFocus)
3396{
4ae5ada0
PC
3397 wxCHECK_RET(m_widget, "invalid window");
3398
3399 gtk_widget_set_can_focus(m_widget, canFocus);
80332672
VZ
3400
3401 if ( m_wxwindow && (m_widget != m_wxwindow) )
3402 {
fc9ab22a 3403 gtk_widget_set_can_focus(m_wxwindow, canFocus);
80332672
VZ
3404 }
3405}
3406
1e6feb95 3407bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
463c1fa1 3408{
0a164d4c 3409 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
c50f1fb9 3410
0edbdf6a 3411 wxWindowGTK * const newParent = (wxWindowGTK *)newParentBase;
a2053b27 3412
5fd11f09
RR
3413 wxASSERT( GTK_IS_WIDGET(m_widget) );
3414
f03fc89f 3415 if ( !wxWindowBase::Reparent(newParent) )
0a164d4c 3416 return false;
8bbe427f 3417
5fd11f09
RR
3418 wxASSERT( GTK_IS_WIDGET(m_widget) );
3419
0edbdf6a
VZ
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);
c50f1fb9 3425
5fd11f09
RR
3426 wxASSERT( GTK_IS_WIDGET(m_widget) );
3427
8ce63e9d
RR
3428 if (newParent)
3429 {
fc9ab22a 3430 if (gtk_widget_get_visible (newParent->m_widget))
f46ad98f
RR
3431 {
3432 m_showOnIdle = true;
3433 gtk_widget_hide( m_widget );
3434 }
8ce63e9d 3435 /* insert GTK representation */
48200154 3436 newParent->AddChildGTK(this);
8ce63e9d 3437 }
c50f1fb9 3438
978af864 3439 SetLayoutDirection(wxLayout_Default);
148cd9b6 3440
0a164d4c 3441 return true;
362c6693 3442}
c801d85f 3443
1e6feb95 3444void wxWindowGTK::DoAddChild(wxWindowGTK *child)
ddb6bc71 3445{
223d09f6 3446 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
223d09f6 3447 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
ddb6bc71 3448
ddb6bc71
RR
3449 /* add to list */
3450 AddChild( child );
c50f1fb9 3451
ddb6bc71 3452 /* insert GTK representation */
48200154 3453 AddChildGTK(child);
ddb6bc71
RR
3454}
3455
a589495e
VS
3456void wxWindowGTK::AddChild(wxWindowBase *child)
3457{
3458 wxWindowBase::AddChild(child);
3459 m_dirtyTabOrder = true;
a1abca32 3460 wxTheApp->WakeUpIdle();
a589495e
VS
3461}
3462
3463void wxWindowGTK::RemoveChild(wxWindowBase *child)
3464{
3465 wxWindowBase::RemoveChild(child);
3466 m_dirtyTabOrder = true;
a1abca32 3467 wxTheApp->WakeUpIdle();
a589495e 3468}
0a164d4c 3469
978af864
VZ
3470/* static */
3471wxLayoutDirection wxWindowGTK::GTKGetLayout(GtkWidget *widget)
3472{
10bd1f7d 3473 return gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL
978af864
VZ
3474 ? wxLayout_RightToLeft
3475 : wxLayout_LeftToRight;
3476}
3477
3478/* static */
3479void wxWindowGTK::GTKSetLayout(GtkWidget *widget, wxLayoutDirection dir)
3480{
9a83f860 3481 wxASSERT_MSG( dir != wxLayout_Default, wxT("invalid layout direction") );
978af864 3482
10bd1f7d 3483 gtk_widget_set_direction(widget,
978af864
VZ
3484 dir == wxLayout_RightToLeft ? GTK_TEXT_DIR_RTL
3485 : GTK_TEXT_DIR_LTR);
3486}
3487
3488wxLayoutDirection wxWindowGTK::GetLayoutDirection() const
3489{
3490 return GTKGetLayout(m_widget);
3491}
3492
3493void 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);
fcb29b23 3513
6552c7af 3514 if (m_wxwindow && (m_wxwindow != m_widget))
69597639
RR
3515 GTKSetLayout(m_wxwindow, dir);
3516}
3517
3518wxCoord
3519wxWindowGTK::AdjustForLayoutDirection(wxCoord x,
3520 wxCoord WXUNUSED(width),
3521 wxCoord WXUNUSED(widthTotal)) const
3522{
08f53168 3523 // We now mirror the coordinates of RTL windows in wxPizza
69597639 3524 return x;
978af864
VZ
3525}
3526
915bd4e4 3527void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, WindowOrder move)
a589495e
VS
3528{
3529 wxWindowBase::DoMoveInTabOrder(win, move);
3530 m_dirtyTabOrder = true;
a1abca32 3531 wxTheApp->WakeUpIdle();
a589495e
VS
3532}
3533
5644933f
VZ
3534bool wxWindowGTK::DoNavigateIn(int flags)
3535{
3536 if ( flags & wxNavigationKeyEvent::WinChange )
3537 {
9a83f860 3538 wxFAIL_MSG( wxT("not implemented") );
5644933f
VZ
3539
3540 return false;
3541 }
3542 else // navigate inside the container
3543 {
7a3ba5af 3544 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
9a83f860 3545 wxCHECK_MSG( parent, false, wxT("every window must have a TLW parent") );
5644933f
VZ
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
d5027818 3554 return rc != 0;
5644933f
VZ
3555 }
3556}
3557
2e1f5012
VZ
3558bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3559{
3560 // none needed by default
3561 return false;
3562}
3563
3564void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget* WXUNUSED(w))
3565{
3566 // nothing to do by default since none is needed
3567}
3568
a589495e
VS
3569void wxWindowGTK::RealizeTabOrder()
3570{
3571 if (m_wxwindow)
3572 {
12848fda 3573 if ( !m_children.empty() )
a589495e 3574 {
259858fc 3575 // we don't only construct the correct focus chain but also use
2e1f5012
VZ
3576 // this opportunity to update the mnemonic widgets for the widgets
3577 // that need them
259858fc 3578
a589495e 3579 GList *chain = NULL;
2e1f5012 3580 wxWindowGTK* mnemonicWindow = NULL;
0a164d4c 3581
12848fda
VZ
3582 for ( wxWindowList::const_iterator i = m_children.begin();
3583 i != m_children.end();
3584 ++i )
a589495e 3585 {
259858fc 3586 wxWindowGTK *win = *i;
2e1f5012 3587
a3edb930
VZ
3588 bool focusableFromKeyboard = win->AcceptsFocusFromKeyboard();
3589
2e1f5012 3590 if ( mnemonicWindow )
259858fc 3591 {
a3edb930 3592 if ( focusableFromKeyboard )
259858fc 3593 {
2e1f5012
VZ
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;
fc9ab22a 3598 if ( !gtk_widget_get_can_focus(w) )
2e1f5012
VZ
3599 {
3600 w = win->GetConnectWidget();
fc9ab22a 3601 if ( !gtk_widget_get_can_focus(w) )
2e1f5012
VZ
3602 w = NULL;
3603 }
3604
3605 if ( w )
3606 {
3607 mnemonicWindow->GTKWidgetDoSetMnemonic(w);
3608 mnemonicWindow = NULL;
3609 }
259858fc
VZ
3610 }
3611 }
2e1f5012 3612 else if ( win->GTKWidgetNeedsMnemonic() )
259858fc 3613 {
2e1f5012 3614 mnemonicWindow = win;
259858fc 3615 }
259858fc 3616
a3edb930
VZ
3617 if ( focusableFromKeyboard )
3618 chain = g_list_prepend(chain, win->m_widget);
a589495e 3619 }
0a164d4c 3620
a589495e 3621 chain = g_list_reverse(chain);
0a164d4c 3622
a589495e
VS
3623 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain);
3624 g_list_free(chain);
3625 }
12848fda 3626 else // no children
a589495e
VS
3627 {
3628 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow));
3629 }
3630 }
a589495e
VS
3631}
3632
1e6feb95 3633void wxWindowGTK::Raise()
362c6693 3634{
82b978d7
RD
3635 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3636
989d151c 3637 if (m_wxwindow && gtk_widget_get_window(m_wxwindow))
fdfb8475 3638 {
989d151c 3639 gdk_window_raise(gtk_widget_get_window(m_wxwindow));
fdfb8475 3640 }
989d151c 3641 else if (gtk_widget_get_window(m_widget))
fdfb8475 3642 {
989d151c 3643 gdk_window_raise(gtk_widget_get_window(m_widget));
fdfb8475 3644 }
362c6693
RR
3645}
3646
1e6feb95 3647void wxWindowGTK::Lower()
362c6693 3648{
82b978d7
RD
3649 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3650
989d151c 3651 if (m_wxwindow && gtk_widget_get_window(m_wxwindow))
fdfb8475 3652 {
989d151c 3653 gdk_window_lower(gtk_widget_get_window(m_wxwindow));
fdfb8475 3654 }
989d151c 3655 else if (gtk_widget_get_window(m_widget))
fdfb8475 3656 {
989d151c 3657 gdk_window_lower(gtk_widget_get_window(m_widget));
fdfb8475 3658 }
362c6693 3659}
c801d85f 3660
1e6feb95 3661bool wxWindowGTK::SetCursor( const wxCursor &cursor )
86b29a61 3662{
a1b806b9 3663 if ( !wxWindowBase::SetCursor(cursor.IsOk() ? cursor : *wxSTANDARD_CURSOR) )
2f262021 3664 return false;
f6bcfd97 3665
2f262021 3666 GTKUpdateCursor();
1e6feb95 3667
2f262021 3668 return true;
ef5c70f9
VZ
3669}
3670
c2246a38 3671void wxWindowGTK::GTKUpdateCursor(bool update_self /*=true*/, bool recurse /*=true*/)
ef5c70f9 3672{
c2246a38 3673 if (update_self)
ef5c70f9 3674 {
a1b806b9
DS
3675 wxCursor cursor(g_globalCursor.IsOk() ? g_globalCursor : GetCursor());
3676 if ( cursor.IsOk() )
ef5c70f9 3677 {
9c76b9af
RR
3678 wxArrayGdkWindows windowsThis;
3679 GdkWindow* window = GTKGetWindow(windowsThis);
3680 if (window)
3681 gdk_window_set_cursor( window, cursor.GetCursor() );
3682 else
ef5c70f9 3683 {
9c76b9af
RR
3684 const size_t count = windowsThis.size();
3685 for ( size_t n = 0; n < count; n++ )
5dd7eef7 3686 {
9c76b9af
RR
3687 GdkWindow *win = windowsThis[n];
3688 // It can be zero if the window has not been realized yet.
3689 if ( win )
c2246a38 3690 {
c2246a38
RR
3691 gdk_window_set_cursor(win, cursor.GetCursor());
3692 }
5dd7eef7 3693 }
c2246a38
RR
3694 }
3695 }
3696 }
3697
3698 if (recurse)
3699 {
3700 for (wxWindowList::iterator it = GetChildren().begin(); it != GetChildren().end(); ++it)
3701 {
3702 (*it)->GTKUpdateCursor( true );
ef5c70f9
VZ
3703 }
3704 }
362c6693 3705}
c801d85f 3706
1e6feb95 3707void wxWindowGTK::WarpPointer( int x, int y )
4f22cf8d 3708{
82b978d7
RD
3709 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3710
1ddb3b55
PC
3711 ClientToScreen(&x, &y);
3712 GdkDisplay* display = gtk_widget_get_display(m_widget);
3713 GdkScreen* screen = gtk_widget_get_screen(m_widget);
9dc44eff 3714#ifdef __WXGTK3__
1ddb3b55
PC
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
3b81515c 3718#ifdef GDK_WINDOWING_X11
1ddb3b55
PC
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
3b81515c 3724#endif
4f22cf8d
RR
3725}
3726
22c9b211 3727wxWindowGTK::ScrollDir wxWindowGTK::ScrollDirFromRange(GtkRange *range) const
0c131a5a 3728{
22c9b211
VZ
3729 // find the scrollbar which generated the event
3730 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
0c131a5a 3731 {
22c9b211
VZ
3732 if ( range == m_scrollBar[dir] )
3733 return (ScrollDir)dir;
0c131a5a 3734 }
22c9b211 3735
9a83f860 3736 wxFAIL_MSG( wxT("event from unknown scrollbar received") );
22c9b211
VZ
3737
3738 return ScrollDir_Max;
0c131a5a
VZ
3739}
3740
22c9b211 3741bool wxWindowGTK::DoScrollByUnits(ScrollDir dir, ScrollUnit unit, int units)
0c131a5a 3742{
add7cadd 3743 bool changed = false;
22c9b211
VZ
3744 GtkRange* range = m_scrollBar[dir];
3745 if ( range && units )
add7cadd 3746 {
08f09504 3747 GtkAdjustment* adj = gtk_range_get_adjustment(range);
989d151c
PC
3748 double inc = unit == ScrollUnit_Line ? gtk_adjustment_get_step_increment(adj)
3749 : gtk_adjustment_get_page_increment(adj);
22c9b211 3750
989d151c 3751 const int posOld = wxRound(gtk_adjustment_get_value(adj));
22c9b211
VZ
3752 gtk_range_set_value(range, posOld + units*inc);
3753
989d151c 3754 changed = wxRound(gtk_adjustment_get_value(adj)) != posOld;
add7cadd 3755 }
22c9b211 3756
add7cadd 3757 return changed;
0c131a5a 3758}
3013a903 3759
22c9b211
VZ
3760bool wxWindowGTK::ScrollLines(int lines)
3761{
3762 return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Line, lines);
3763}
3764
3765bool wxWindowGTK::ScrollPages(int pages)
3766{
3767 return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Page, pages);
3768}
3769
e4161a2a
VZ
3770void wxWindowGTK::Refresh(bool WXUNUSED(eraseBackground),
3771 const wxRect *rect)
c801d85f 3772{
c968ba80 3773 if (m_wxwindow)
4e5a4c69 3774 {
ffaaf107 3775 if (gtk_widget_get_mapped(m_wxwindow))
d0285203 3776 {
ffaaf107
PC
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);
d0285203 3787 }
4e5a4c69 3788 }
ffaaf107 3789 else if (m_widget)
c968ba80 3790 {
ffaaf107
PC
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 }
c968ba80 3798 }
362c6693 3799}
c801d85f 3800
beab25bd 3801void wxWindowGTK::Update()
010afced 3802{
fc9ab22a 3803 if (m_widget && gtk_widget_get_mapped(m_widget))
ab0c1a3c
PC
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);
010afced 3809
f089940f
PC
3810 GdkWindow* window = GTKGetDrawingWindow();
3811 if (window == NULL)
989d151c 3812 window = gtk_widget_get_window(m_widget);
f089940f 3813 gdk_window_process_updates(window, true);
03647350 3814
6cab4fca
PC
3815 // Flush again, but no need to wait for it to finish
3816 gdk_display_flush(display);
a67f1484 3817 }
beab25bd
RR
3818}
3819
657b4fd4 3820bool wxWindowGTK::DoIsExposed( int x, int y ) const
847dfdb4
RR
3821{
3822 return m_updateRegion.Contains(x, y) != wxOutRegion;
3823}
3824
657b4fd4 3825bool wxWindowGTK::DoIsExposed( int x, int y, int w, int h ) const
847dfdb4
RR
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
9dc44eff
PC
3833#ifdef __WXGTK3__
3834void wxWindowGTK::GTKSendPaintEvents(cairo_t* cr)
3835#else
3836void wxWindowGTK::GTKSendPaintEvents(const GdkRegion* region)
3837#endif
beab25bd 3838{
9dc44eff
PC
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);
ac900e6b
PC
3846#if wxGTK_HAS_COMPOSITING_SUPPORT
3847 cairo_t* cr = NULL;
3848#endif
9dc44eff 3849#endif // !__WXGTK3__
f90566f5 3850 // Clip to paint region in wxClientDC
0a164d4c 3851 m_clipPaintRegion = true;
fab591c5 3852
bcb614b3
RR
3853 m_nativeUpdateRegion = m_updateRegion;
3854
847dfdb4
RR
3855 if (GetLayoutDirection() == wxLayout_RightToLeft)
3856 {
bcb614b3
RR
3857 // Transform m_updateRegion under RTL
3858 m_updateRegion.Clear();
fcb29b23 3859
9dc44eff 3860 const int width = gdk_window_get_width(GTKGetDrawingWindow());
fcb29b23 3861
bcb614b3 3862 wxRegionIterator upd( m_nativeUpdateRegion );
847dfdb4
RR
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();
fcb29b23 3870
847dfdb4 3871 rect.x = width - rect.x - rect.width;
bcb614b3 3872 m_updateRegion.Union( rect );
fcb29b23 3873
847dfdb4
RR
3874 ++upd;
3875 }
3876 }
fcb29b23 3877
9c61c5b0 3878 switch ( GetBackgroundStyle() )
f90566f5 3879 {
14421681 3880 case wxBG_STYLE_TRANSPARENT:
ac900e6b
PC
3881#if wxGTK_HAS_COMPOSITING_SUPPORT
3882 if (IsTransparentBackgroundSupported())
14421681
VZ
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)
9dc44eff 3889#ifndef __WXGTK3__
ac900e6b
PC
3890 cr = gdk_cairo_create(m_wxwindow->window);
3891 gdk_cairo_region(cr, m_nativeUpdateRegion.GetRegion());
3892 cairo_clip(cr);
9dc44eff 3893#endif
ac900e6b
PC
3894 cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
3895 cairo_paint(cr);
3896 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
9dc44eff 3897#ifndef __WXGTK3__
ac900e6b 3898 cairo_surface_flush(cairo_get_target(cr));
9dc44eff 3899#endif
14421681 3900 }
ac900e6b
PC
3901#endif // wxGTK_HAS_COMPOSITING_SUPPORT
3902 break;
14421681 3903
9c61c5b0 3904 case wxBG_STYLE_ERASE:
822cf31c 3905 {
9dc44eff
PC
3906#ifdef __WXGTK3__
3907 wxGTKCairoDC dc(cr);
3908#else
9c61c5b0
VZ
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 }
9dc44eff 3921#endif // !__WXGTK3__
9c61c5b0
VZ
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 }
822cf31c 3930 }
9c61c5b0 3931 // fall through
b15ed747 3932
9c61c5b0
VZ
3933 case wxBG_STYLE_SYSTEM:
3934 if ( GetThemeEnabled() )
3935 {
9dc44eff
PC
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
9c61c5b0
VZ
3943 // find ancestor from which to steal background
3944 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
3945 if (!parent)
3946 parent = (wxWindow*)this;
9dc44eff
PC
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,
989d151c 3951 gtk_widget_get_state(m_wxwindow),
9c61c5b0
VZ
3952 GTK_SHADOW_NONE,
3953 &rect,
3954 parent->m_widget,
3955 (char *)"base",
9dc44eff
PC
3956 0, 0, w, h);
3957#endif // !__WXGTK3__
9c61c5b0
VZ
3958 }
3959 break;
8ab7b4c5 3960
9c61c5b0
VZ
3961 case wxBG_STYLE_PAINT:
3962 // nothing to do: window will be painted over in EVT_PAINT
3963 break;
b15ed747 3964
9c61c5b0
VZ
3965 default:
3966 wxFAIL_MSG( "unsupported background style" );
b15ed747 3967 }
03eaa52a 3968
beab25bd
RR
3969 wxNcPaintEvent nc_paint_event( GetId() );
3970 nc_paint_event.SetEventObject( this );
937013e0 3971 HandleWindowEvent( nc_paint_event );
03eaa52a 3972
beab25bd
RR
3973 wxPaintEvent paint_event( GetId() );
3974 paint_event.SetEventObject( this );
937013e0 3975 HandleWindowEvent( paint_event );
beab25bd 3976
ac900e6b
PC
3977#if wxGTK_HAS_COMPOSITING_SUPPORT
3978 if (IsTransparentBackgroundSupported())
14421681 3979 { // now composite children which need it
14421681
VZ
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 {
9dc44eff 3987#ifndef __WXGTK3__
ac900e6b
PC
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 }
9dc44eff 3994#endif // !__WXGTK3__
14421681 3995 GtkWidget *child = compositeChild->m_wxwindow;
ac900e6b
PC
3996 GtkAllocation alloc;
3997 gtk_widget_get_allocation(child, &alloc);
14421681
VZ
3998
3999 // The source data is the (composited) child
ac900e6b
PC
4000 gdk_cairo_set_source_window(
4001 cr, gtk_widget_get_window(child), alloc.x, alloc.y);
14421681 4002
ac900e6b 4003 cairo_paint(cr);
14421681
VZ
4004 }
4005 }
9dc44eff 4006#ifndef __WXGTK3__
ac900e6b
PC
4007 if (cr)
4008 cairo_destroy(cr);
9dc44eff 4009#endif
14421681 4010 }
ac900e6b 4011#endif // wxGTK_HAS_COMPOSITING_SUPPORT
14421681 4012
0a164d4c 4013 m_clipPaintRegion = false;
9dc44eff
PC
4014#ifdef __WXGTK3__
4015 m_paintContext = NULL;
4016#endif
c89f5c02 4017 m_updateRegion.Clear();
bcb614b3 4018 m_nativeUpdateRegion.Clear();
beab25bd
RR
4019}
4020
8e1a5bf9
VZ
4021void 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
2e992e06
VZ
4029bool wxWindowGTK::IsDoubleBuffered() const
4030{
d5027818 4031 return gtk_widget_get_double_buffered( m_wxwindow ) != 0;
2e992e06
VZ
4032}
4033
596f1d11 4034void wxWindowGTK::ClearBackground()
c801d85f 4035{
82b978d7 4036 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
362c6693 4037}
c801d85f 4038
ff8bfdbb 4039#if wxUSE_TOOLTIPS
1e6feb95 4040void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
b1170810 4041{
558a94bd 4042 if (m_tooltip != tip)
fb4b0165 4043 {
558a94bd
PC
4044 wxWindowBase::DoSetToolTip(tip);
4045
4046 if (m_tooltip)
4047 m_tooltip->GTKSetWindow(static_cast<wxWindow*>(this));
4048 else
4049 GTKApplyToolTip(NULL);
fb4b0165 4050 }
b1170810
RR
4051}
4052
558a94bd 4053void wxWindowGTK::GTKApplyToolTip(const char* tip)
b1170810 4054{
558a94bd 4055 wxToolTip::GTKApply(GetConnectWidget(), tip);
301cd871 4056}
ff8bfdbb 4057#endif // wxUSE_TOOLTIPS
b1170810 4058
1e6feb95 4059bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
c801d85f 4060{
739730ca 4061 if (!wxWindowBase::SetBackgroundColour(colour))
44dfb5ce 4062 return false;
c50f1fb9 4063
92ef38ee 4064 if (m_widget)
994bc575 4065 {
92ef38ee
PC
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 }
9dc44eff 4072#endif
ca298c88 4073
92ef38ee
PC
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 }
ea323db3 4078
5edef14e 4079 return true;
6de97a3b
RR
4080}
4081
1e6feb95 4082bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
6de97a3b 4083{
739730ca 4084 if (!wxWindowBase::SetForegroundColour(colour))
5edef14e 4085 return false;
0a164d4c 4086
92ef38ee 4087 if (m_widget)
ea323db3 4088 {
92ef38ee
PC
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 }
9dc44eff 4095#endif
f03fc89f 4096
92ef38ee
PC
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 }
5edef14e 4101
44dfb5ce 4102 return true;
58614078
RR
4103}
4104
496e7ec6 4105PangoContext *wxWindowGTK::GTKGetPangoDefaultContext()
2b5f62a0
VZ
4106{
4107 return gtk_widget_get_pango_context( m_widget );
4108}
0a164d4c 4109
9dc44eff 4110#ifndef __WXGTK3__
80a77e3c 4111GtkRcStyle* wxWindowGTK::GTKCreateWidgetStyle()
58614078 4112{
f40fdaa3 4113 GtkRcStyle *style = gtk_rc_style_new();
1ecc4d80 4114
a1b806b9 4115 if ( m_font.IsOk() )
db434467 4116 {
0a164d4c 4117 style->font_desc =
f40fdaa3 4118 pango_font_description_copy( m_font.GetNativeFontInfo()->description );
288059b2 4119 }
1ecc4d80 4120
532ae0f6
VZ
4121 int flagsNormal = 0,
4122 flagsPrelight = 0,
4123 flagsActive = 0,
4124 flagsInsensitive = 0;
4125
a1b806b9 4126 if ( m_foregroundColour.IsOk() )
1ecc4d80 4127 {
c6685317 4128 const GdkColor *fg = m_foregroundColour.GetColor();
0a164d4c 4129
532ae0f6
VZ
4130 style->fg[GTK_STATE_NORMAL] =
4131 style->text[GTK_STATE_NORMAL] = *fg;
4132 flagsNormal |= GTK_RC_FG | GTK_RC_TEXT;
0a164d4c 4133
532ae0f6
VZ
4134 style->fg[GTK_STATE_PRELIGHT] =
4135 style->text[GTK_STATE_PRELIGHT] = *fg;
4136 flagsPrelight |= GTK_RC_FG | GTK_RC_TEXT;
0a164d4c 4137
532ae0f6
VZ
4138 style->fg[GTK_STATE_ACTIVE] =
4139 style->text[GTK_STATE_ACTIVE] = *fg;
4140 flagsActive |= GTK_RC_FG | GTK_RC_TEXT;
1ecc4d80
RR
4141 }
4142
a1b806b9 4143 if ( m_backgroundColour.IsOk() )
1ecc4d80 4144 {
c6685317 4145 const GdkColor *bg = m_backgroundColour.GetColor();
5edef14e 4146
532ae0f6 4147 style->bg[GTK_STATE_NORMAL] =
5edef14e 4148 style->base[GTK_STATE_NORMAL] = *bg;
532ae0f6 4149 flagsNormal |= GTK_RC_BG | GTK_RC_BASE;
0a164d4c 4150
532ae0f6 4151 style->bg[GTK_STATE_PRELIGHT] =
5edef14e 4152 style->base[GTK_STATE_PRELIGHT] = *bg;
532ae0f6 4153 flagsPrelight |= GTK_RC_BG | GTK_RC_BASE;
0a164d4c 4154
532ae0f6 4155 style->bg[GTK_STATE_ACTIVE] =
5edef14e 4156 style->base[GTK_STATE_ACTIVE] = *bg;
532ae0f6 4157 flagsActive |= GTK_RC_BG | GTK_RC_BASE;
0a164d4c 4158
532ae0f6 4159 style->bg[GTK_STATE_INSENSITIVE] =
5edef14e 4160 style->base[GTK_STATE_INSENSITIVE] = *bg;
532ae0f6 4161 flagsInsensitive |= GTK_RC_BG | GTK_RC_BASE;
1ecc4d80 4162 }
0a164d4c 4163
532ae0f6
VZ
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
f40fdaa3 4169 return style;
a81258be 4170}
9dc44eff 4171#endif // !__WXGTK3__
a81258be 4172
80a77e3c 4173void wxWindowGTK::GTKApplyWidgetStyle(bool forceStyle)
a81258be 4174{
80a77e3c
PC
4175 if (forceStyle || m_font.IsOk() ||
4176 m_foregroundColour.IsOk() || m_backgroundColour.IsOk())
4177 {
9dc44eff 4178#ifdef __WXGTK3__
80a77e3c
PC
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);
9dc44eff 4196#else
80a77e3c 4197 GtkRcStyle* style = GTKCreateWidgetStyle();
7074ce35 4198 DoApplyWidgetStyle(style);
08f09504 4199 g_object_unref(style);
9dc44eff 4200#endif
80a77e3c 4201 }
6dd18972
VS
4202
4203 // Style change may affect GTK+'s size calculation:
4204 InvalidateBestSize();
6de97a3b
RR
4205}
4206
7074ce35
VS
4207void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
4208{
9dc44eff
PC
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())
0d013e46 4215 {
9dc44eff
PC
4216 unblock = true;
4217 g_signal_handlers_block_by_func(
4218 m_wxwindow, (void*)style_updated, this);
4219 }
013151c7 4220
9dc44eff 4221 GTKApplyStyle(widget, style);
0d013e46 4222
9dc44eff 4223 if (unblock)
0d013e46 4224 {
9dc44eff
PC
4225 g_signal_handlers_unblock_by_func(
4226 m_wxwindow, (void*)style_updated, this);
0d013e46 4227 }
7074ce35
VS
4228}
4229
9dc44eff
PC
4230void 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);
80a77e3c
PC
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 }
9dc44eff
PC
4258#else
4259 gtk_widget_modify_style(widget, style);
4260#endif
4261}
4262
c7382f91
JS
4263bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
4264{
14421681
VZ
4265 if (!wxWindowBase::SetBackgroundStyle(style))
4266 return false;
0a164d4c 4267
9dc44eff 4268#ifndef __WXGTK3__
14421681 4269 GdkWindow *window;
b387025d
PC
4270 if ((style == wxBG_STYLE_PAINT || style == wxBG_STYLE_TRANSPARENT) &&
4271 (window = GTKGetDrawingWindow()))
c7382f91 4272 {
b387025d 4273 gdk_window_set_back_pixmap(window, NULL, false);
c7382f91 4274 }
9dc44eff 4275#endif // !__WXGTK3__
9c61c5b0 4276
c7382f91
JS
4277 return true;
4278}
7074ce35 4279
14421681
VZ
4280bool wxWindowGTK::IsTransparentBackgroundSupported(wxString* reason) const
4281{
ac900e6b 4282#if wxGTK_HAS_COMPOSITING_SUPPORT
9dc44eff 4283#ifndef __WXGTK3__
14421681
VZ
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 }
9dc44eff 4295#endif // !__WXGTK3__
14421681
VZ
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;
ac900e6b 4314#else
14421681
VZ
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 }
14421681
VZ
4320
4321 return false;
ecdfd095 4322#endif // wxGTK_HAS_COMPOSITING_SUPPORT/!wxGTK_HAS_COMPOSITING_SUPPORT
14421681
VZ
4323}
4324
bcf7614c
PC
4325// ----------------------------------------------------------------------------
4326// Pop-up menu stuff
4327// ----------------------------------------------------------------------------
4328
4329#if wxUSE_MENUS_NATIVE
4330
edd6813c
PC
4331extern "C" {
4332static
bcf7614c
PC
4333void 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;
6bfcc4ec
PC
4340#ifdef __WXGTK3__
4341 gtk_widget_get_preferred_size(GTK_WIDGET(menu), &req, NULL);
4342#else
bcf7614c 4343 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &req);
6bfcc4ec 4344#endif
bcf7614c
PC
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}
edd6813c
PC
4355}
4356
bcf7614c
PC
4357bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
4358{
4359 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
4360
a1c6f069 4361 menu->UpdateUI();
bcf7614c
PC
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),
d3b9f782
VZ
4382 NULL, // parent menu shell
4383 NULL, // parent menu item
bcf7614c
PC
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
4c99fdfa
PC
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
bcf7614c
PC
4397 while (menu->m_popupShown)
4398 {
4399 gtk_main_iteration();
4400 }
4401
4402 return true;
4403}
4404
4405#endif // wxUSE_MENUS_NATIVE
4406
06cfab17 4407#if wxUSE_DRAG_AND_DROP
ac57418f 4408
1e6feb95 4409void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
c801d85f 4410{
82b978d7
RD
4411 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4412
1ecc4d80 4413 GtkWidget *dnd_widget = GetConnectWidget();
47d67540 4414
1fe91d70 4415 if (m_dropTarget) m_dropTarget->GtkUnregisterWidget( dnd_widget );
47d67540 4416
1ecc4d80
RR
4417 if (m_dropTarget) delete m_dropTarget;
4418 m_dropTarget = dropTarget;
47d67540 4419
1fe91d70 4420 if (m_dropTarget) m_dropTarget->GtkRegisterWidget( dnd_widget );
362c6693 4421}
c801d85f 4422
f03fc89f 4423#endif // wxUSE_DRAG_AND_DROP
ac57418f 4424
1e6feb95 4425GtkWidget* wxWindowGTK::GetConnectWidget()
e3e65dac 4426{
1ecc4d80
RR
4427 GtkWidget *connect_widget = m_widget;
4428 if (m_wxwindow) connect_widget = m_wxwindow;
47d67540 4429
1ecc4d80 4430 return connect_widget;
e3e65dac 4431}
47d67540 4432
ef5c70f9 4433bool wxWindowGTK::GTKIsOwnWindow(GdkWindow *window) const
903f689b 4434{
ef5c70f9
VZ
4435 wxArrayGdkWindows windowsThis;
4436 GdkWindow * const winThis = GTKGetWindow(windowsThis);
148cd9b6 4437
ef5c70f9
VZ
4438 return winThis ? window == winThis
4439 : windowsThis.Index(window) != wxNOT_FOUND;
4440}
4441
4442GdkWindow *wxWindowGTK::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
4443{
989d151c 4444 return m_wxwindow ? GTKGetDrawingWindow() : gtk_widget_get_window(m_widget);
903f689b
RR
4445}
4446
1e6feb95 4447bool wxWindowGTK::SetFont( const wxFont &font )
c801d85f 4448{
5edef14e
VS
4449 if (!wxWindowBase::SetFont(font))
4450 return false;
c801d85f 4451
92ef38ee
PC
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 }
5edef14e
VS
4458
4459 return true;
362c6693 4460}
c801d85f 4461
94633ad9 4462void wxWindowGTK::DoCaptureMouse()
c801d85f 4463{
82b978d7
RD
4464 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4465
d3b9f782 4466 GdkWindow *window = NULL;
b231914f 4467 if (m_wxwindow)
f089940f 4468 window = GTKGetDrawingWindow();
ed673c6a 4469 else
989d151c 4470 window = gtk_widget_get_window(GetConnectWidget());
148cd9b6 4471
9a83f860 4472 wxCHECK_RET( window, wxT("CaptureMouse() failed") );
c50f1fb9 4473
f516d986 4474 const wxCursor* cursor = &m_cursor;
a1b806b9 4475 if (!cursor->IsOk())
cca602ac
JS
4476 cursor = wxSTANDARD_CURSOR;
4477
6bfcc4ec
PC
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
ed673c6a 4491 gdk_pointer_grab( window, FALSE,
6bfcc4ec 4492 mask,
d3b9f782 4493 NULL,
cca602ac 4494 cursor->GetCursor(),
b02da6b1 4495 (guint32)GDK_CURRENT_TIME );
6bfcc4ec 4496#endif
b231914f 4497 g_captureWindow = this;
0a164d4c 4498 g_captureWindowHasMouse = true;
362c6693 4499}
c801d85f 4500
94633ad9 4501void wxWindowGTK::DoReleaseMouse()
c801d85f 4502{
82b978d7
RD
4503 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4504
e4606ed9 4505 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
47d67540 4506
d3b9f782 4507 g_captureWindow = NULL;
c43430bb 4508
d3b9f782 4509 GdkWindow *window = NULL;
b231914f 4510 if (m_wxwindow)
f089940f 4511 window = GTKGetDrawingWindow();
ed673c6a 4512 else
989d151c 4513 window = gtk_widget_get_window(GetConnectWidget());
148cd9b6 4514
b02da6b1
VZ
4515 if (!window)
4516 return;
c50f1fb9 4517
6bfcc4ec
PC
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
b02da6b1 4524 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
6bfcc4ec 4525#endif
362c6693 4526}
c801d85f 4527
7738af59
VZ
4528void wxWindowGTK::GTKReleaseMouseAndNotify()
4529{
4530 DoReleaseMouse();
4531 wxMouseCaptureLostEvent evt(GetId());
4532 evt.SetEventObject( this );
937013e0 4533 HandleWindowEvent( evt );
7738af59
VZ
4534}
4535
1e6feb95
VZ
4536/* static */
4537wxWindow *wxWindowBase::GetCapture()
4538{
4539 return (wxWindow *)g_captureWindow;
4540}
4541
4542bool wxWindowGTK::IsRetained() const
c801d85f 4543{
0a164d4c 4544 return false;
362c6693 4545}
c801d85f 4546
22c9b211
VZ
4547void wxWindowGTK::SetScrollbar(int orient,
4548 int pos,
4549 int thumbVisible,
4550 int range,
4551 bool WXUNUSED(update))
c801d85f 4552{
63c95f27
PC
4553 const int dir = ScrollDirFromOrient(orient);
4554 GtkRange* const sb = m_scrollBar[dir];
9a83f860 4555 wxCHECK_RET( sb, wxT("this window is not scrollable") );
c801d85f 4556
8466fc74 4557 if (range <= 0)
de7bb802
PC
4558 {
4559 // GtkRange requires upper > lower
4560 range =
4561 thumbVisible = 1;
4562 }
47d67540 4563
63c95f27
PC
4564 g_signal_handlers_block_by_func(
4565 sb, (void*)gtk_scrollbar_value_changed, this);
4566
2bca0d20 4567 gtk_range_set_increments(sb, 1, thumbVisible);
989d151c 4568 gtk_adjustment_set_page_size(gtk_range_get_adjustment(sb), thumbVisible);
63c95f27 4569 gtk_range_set_range(sb, 0, range);
2bca0d20
PC
4570 gtk_range_set_value(sb, pos);
4571 m_scrollPos[dir] = gtk_range_get_value(sb);
63c95f27
PC
4572
4573 g_signal_handlers_unblock_by_func(
4574 sb, (void*)gtk_scrollbar_value_changed, this);
87a3ebe9
VZ
4575}
4576
22c9b211 4577void wxWindowGTK::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh))
c801d85f 4578{
b9e7f011
VZ
4579 const int dir = ScrollDirFromOrient(orient);
4580 GtkRange * const sb = m_scrollBar[dir];
9a83f860 4581 wxCHECK_RET( sb, wxT("this window is not scrollable") );
1ecc4d80 4582
add7cadd
PC
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)
47d67540 4586 {
63c95f27
PC
4587 g_signal_handlers_block_by_func(
4588 sb, (void*)gtk_scrollbar_value_changed, this);
40e5ebbf 4589
63c95f27 4590 gtk_range_set_value(sb, pos);
2bca0d20 4591 m_scrollPos[dir] = gtk_range_get_value(sb);
98264520 4592
63c95f27
PC
4593 g_signal_handlers_unblock_by_func(
4594 sb, (void*)gtk_scrollbar_value_changed, this);
cb43b372 4595 }
362c6693 4596}
c801d85f 4597
22c9b211 4598int wxWindowGTK::GetScrollThumb(int orient) const
c801d85f 4599{
b9e7f011 4600 GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
9a83f860 4601 wxCHECK_MSG( sb, 0, wxT("this window is not scrollable") );
47d67540 4602
989d151c 4603 return wxRound(gtk_adjustment_get_page_size(gtk_range_get_adjustment(sb)));
362c6693 4604}
c801d85f 4605
1e6feb95 4606int wxWindowGTK::GetScrollPos( int orient ) const
c801d85f 4607{
b9e7f011 4608 GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
9a83f860 4609 wxCHECK_MSG( sb, 0, wxT("this window is not scrollable") );
c801d85f 4610
2bca0d20 4611 return wxRound(gtk_range_get_value(sb));
362c6693 4612}
c801d85f 4613
1e6feb95 4614int wxWindowGTK::GetScrollRange( int orient ) const
c801d85f 4615{
b9e7f011 4616 GtkRange * const sb = m_scrollBar[ScrollDirFromOrient(orient)];
9a83f860 4617 wxCHECK_MSG( sb, 0, wxT("this window is not scrollable") );
c801d85f 4618
989d151c 4619 return wxRound(gtk_adjustment_get_upper(gtk_range_get_adjustment(sb)));
add7cadd
PC
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
4624static inline bool IsScrollIncrement(double increment, double x)
4625{
45d66f59
VS
4626 wxASSERT(increment >= 0);
4627 if ( increment == 0. )
4628 return false;
add7cadd
PC
4629 const double tolerance = 1.0 / 1024;
4630 return fabs(increment - fabs(x)) < tolerance;
4631}
4632
71ead4bf 4633wxEventType wxWindowGTK::GTKGetScrollEventType(GtkRange* range)
add7cadd 4634{
add7cadd
PC
4635 wxASSERT(range == m_scrollBar[0] || range == m_scrollBar[1]);
4636
4637 const int barIndex = range == m_scrollBar[1];
fcb29b23 4638
2bca0d20 4639 const double value = gtk_range_get_value(range);
fcb29b23 4640
add7cadd
PC
4641 // save previous position
4642 const double oldPos = m_scrollPos[barIndex];
4643 // update current position
2bca0d20 4644 m_scrollPos[barIndex] = value;
add7cadd 4645 // If event should be ignored, or integral position has not changed
8ab75332 4646 if (g_blockEventsOnDrag || wxRound(value) == wxRound(oldPos))
add7cadd
PC
4647 {
4648 return wxEVT_NULL;
4649 }
4650
4651 wxEventType eventType = wxEVT_SCROLL_THUMBTRACK;
4652 if (!m_isScrolling)
4653 {
4654 // Difference from last change event
2bca0d20 4655 const double diff = value - oldPos;
add7cadd
PC
4656 const bool isDown = diff > 0;
4657
2bca0d20 4658 GtkAdjustment* adj = gtk_range_get_adjustment(range);
989d151c 4659 if (IsScrollIncrement(gtk_adjustment_get_step_increment(adj), diff))
add7cadd
PC
4660 {
4661 eventType = isDown ? wxEVT_SCROLL_LINEDOWN : wxEVT_SCROLL_LINEUP;
4662 }
989d151c 4663 else if (IsScrollIncrement(gtk_adjustment_get_page_increment(adj), diff))
add7cadd
PC
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;
362c6693 4674}
c801d85f 4675
1e6feb95 4676void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
c801d85f 4677{
82b978d7
RD
4678 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4679
223d09f6 4680 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
2b5f62a0 4681
f47ae6e7 4682 // No scrolling requested.
8e217128 4683 if ((dx == 0) && (dy == 0)) return;
35510e48 4684
0a164d4c 4685 m_clipPaintRegion = true;
0fc5dbf5 4686
08f53168 4687 WX_PIZZA(m_wxwindow)->scroll(dx, dy);
0fc5dbf5 4688
0a164d4c 4689 m_clipPaintRegion = false;
113faca1 4690
231018bd 4691#if wxUSE_CARET
113faca1
JS
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
fcb29b23 4699 {
113faca1 4700 caretRect.x += dx; caretRect.width -= dx;
fcb29b23 4701 }
113faca1
JS
4702 if (dy > 0)
4703 caretRect.height += dy;
4704 else
fcb29b23 4705 {
113faca1 4706 caretRect.y += dy; caretRect.height -= dy;
fcb29b23
VZ
4707 }
4708
113faca1
JS
4709 RefreshRect(caretRect);
4710 }
fcb29b23 4711#endif // wxUSE_CARET
c801d85f 4712}
3723b7b1 4713
496e7ec6 4714void wxWindowGTK::GTKScrolledWindowSetBorder(GtkWidget* w, int wxstyle)
6493aaca
VZ
4715{
4716 //RN: Note that static controls usually have no border on gtk, so maybe
88a7a4e1 4717 //it makes sense to treat that as simply no border at the wx level
6493aaca
VZ
4718 //as well...
4719 if (!(wxstyle & wxNO_BORDER) && !(wxstyle & wxBORDER_STATIC))
4720 {
4721 GtkShadowType gtkstyle;
88a7a4e1 4722
6493aaca
VZ
4723 if(wxstyle & wxBORDER_RAISED)
4724 gtkstyle = GTK_SHADOW_OUT;
ec2d6790 4725 else if ((wxstyle & wxBORDER_SUNKEN) || (wxstyle & wxBORDER_THEME))
6493aaca 4726 gtkstyle = GTK_SHADOW_IN;
78cd9c69
JS
4727#if 0
4728 // Now obsolete
6493aaca
VZ
4729 else if (wxstyle & wxBORDER_DOUBLE)
4730 gtkstyle = GTK_SHADOW_ETCHED_IN;
78cd9c69 4731#endif
6493aaca
VZ
4732 else //default
4733 gtkstyle = GTK_SHADOW_IN;
4734
88a7a4e1 4735 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w),
6493aaca
VZ
4736 gtkstyle );
4737 }
4738}
4739
3723b7b1
JS
4740// Find the wxWindow at the current mouse position, also returning the mouse
4741// position.
4742wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4743{
59a12e90
JS
4744 pt = wxGetMousePosition();
4745 wxWindow* found = wxFindWindowAtPoint(pt);
4746 return found;
3723b7b1
JS
4747}
4748
4749// Get the current mouse position.
d180f4e0 4750void wxGetMousePosition(int* x, int* y)
3723b7b1 4751{
6bfcc4ec
PC
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);
d180f4e0 4756 gdk_device_get_position(device, NULL, x, y);
6bfcc4ec 4757#else
d180f4e0 4758 gdk_display_get_pointer(display, NULL, x, y, NULL);
6bfcc4ec 4759#endif
d180f4e0
PC
4760}
4761
4762wxPoint wxGetMousePosition()
4763{
4764 wxPoint pt;
4765 wxGetMousePosition(&pt.x, &pt.y);
4766 return pt;
3723b7b1
JS
4767}
4768
08f53168
PC
4769GdkWindow* wxWindowGTK::GTKGetDrawingWindow() const
4770{
4771 GdkWindow* window = NULL;
4772 if (m_wxwindow)
989d151c 4773 window = gtk_widget_get_window(m_wxwindow);
08f53168
PC
4774 return window;
4775}
5f346ddc
VS
4776
4777// ----------------------------------------------------------------------------
4778// freeze/thaw
4779// ----------------------------------------------------------------------------
4780
603e7f6d 4781void wxWindowGTK::GTKFreezeWidget(GtkWidget* widget)
89267fe5 4782{
603e7f6d 4783 if (widget && gtk_widget_get_has_window(widget))
89267fe5 4784 {
603e7f6d
PC
4785 GdkWindow* window = gtk_widget_get_window(widget);
4786 if (window)
4787 gdk_window_freeze_updates(window);
89267fe5 4788 }
5f346ddc
VS
4789}
4790
603e7f6d 4791void wxWindowGTK::GTKThawWidget(GtkWidget* widget)
5f346ddc 4792{
603e7f6d 4793 if (widget && gtk_widget_get_has_window(widget))
89267fe5 4794 {
603e7f6d
PC
4795 GdkWindow* window = gtk_widget_get_window(widget);
4796 if (window)
4797 gdk_window_thaw_updates(window);
89267fe5 4798 }
5f346ddc
VS
4799}
4800
4801void wxWindowGTK::DoFreeze()
4802{
603e7f6d
PC
4803 GtkWidget* widget = m_wxwindow;
4804 if (widget == NULL)
4805 widget = m_widget;
4806 GTKFreezeWidget(widget);
5f346ddc
VS
4807}
4808
4809void wxWindowGTK::DoThaw()
4810{
603e7f6d
PC
4811 GtkWidget* widget = m_wxwindow;
4812 if (widget == NULL)
4813 widget = m_widget;
4814 GTKThawWidget(widget);
5f346ddc 4815}