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