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