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