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