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