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