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