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