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