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