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