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