]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/window.cpp
I don't think anylonger that DrawHeaderButton() should
[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.
1e89eecd 1462 if ((gdk_event->type == GDK_BUTTON_PRESS) && (win->m_wxwindow))
2b5f62a0
VZ
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__
af05af4d
PC
2321 if (HasFlag(wxSIMPLE_BORDER))
2322 gtk_container_set_border_width((GtkContainer*)m_wxwindow, 1);
2323 else if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
2324 gtk_container_set_border_width((GtkContainer*)m_wxwindow, 2);
1e6feb95 2325#endif // __WXUNIVERSAL__
47d67540 2326
4e5a4c69
RR
2327 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
2328
3da17724 2329 GTK_WIDGET_SET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
0a164d4c 2330 m_acceptsFocus = true;
ca298c88 2331
22c9b211
VZ
2332 // connect various scroll-related events
2333 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2334 {
2335 // these handlers block mouse events to any window during scrolling
2336 // such as motion events and prevent GTK and wxWidgets from fighting
2337 // over where the slider should be
2338 g_signal_connect(m_scrollBar[dir], "button_press_event",
2339 G_CALLBACK(gtk_scrollbar_button_press_event), this);
2340 g_signal_connect(m_scrollBar[dir], "button_release_event",
2341 G_CALLBACK(gtk_scrollbar_button_release_event), this);
2342
2343 gulong handler_id = g_signal_connect(m_scrollBar[dir], "event_after",
2344 G_CALLBACK(gtk_scrollbar_event_after), this);
2345 g_signal_handler_block(m_scrollBar[dir], handler_id);
2346
2347 // these handlers get notified when scrollbar slider moves
2348 g_signal_connect(m_scrollBar[dir], "value_changed",
2349 G_CALLBACK(gtk_scrollbar_value_changed), this);
2350 }
76ed8f8d 2351
f03fc89f 2352 gtk_widget_show( m_wxwindow );
47d67540 2353
f03fc89f
VZ
2354 if (m_parent)
2355 m_parent->DoAddChild( this );
94633ad9 2356
76fcf0f2 2357 m_focusWidget = m_wxwindow;
8bbe427f 2358
e380f72b 2359 PostCreation();
8bbe427f 2360
0a164d4c 2361 return true;
362c6693 2362}
c801d85f 2363
1e6feb95 2364wxWindowGTK::~wxWindowGTK()
c801d85f 2365{
7de59551
RD
2366 SendDestroyEvent();
2367
44cd54c2
JS
2368 if (g_focusWindow == this)
2369 g_focusWindow = NULL;
2370
3e679f01
VZ
2371 if ( g_delayedFocus == this )
2372 g_delayedFocus = NULL;
2373
0a164d4c
WS
2374 m_isBeingDeleted = true;
2375 m_hasVMT = false;
47d67540 2376
02c3e53b
JS
2377 // destroy children before destroying this window itself
2378 DestroyChildren();
2379
2380 // unhook focus handlers to prevent stray events being
2381 // propagated to this (soon to be) dead object
2382 if (m_focusWidget != NULL)
2383 {
9fa72bd2
MR
2384 g_signal_handlers_disconnect_by_func (m_focusWidget,
2385 (gpointer) gtk_window_focus_in_callback,
2386 this);
2387 g_signal_handlers_disconnect_by_func (m_focusWidget,
2388 (gpointer) gtk_window_focus_out_callback,
2389 this);
02c3e53b
JS
2390 }
2391
f03fc89f 2392 if (m_widget)
0a164d4c 2393 Show( false );
8bbe427f 2394
f6551618
MW
2395 // delete before the widgets to avoid a crash on solaris
2396 delete m_imData;
f6551618 2397
f03fc89f 2398 if (m_wxwindow)
a2053b27 2399 {
f03fc89f 2400 gtk_widget_destroy( m_wxwindow );
c50f1fb9 2401 m_wxwindow = (GtkWidget*) NULL;
a2053b27 2402 }
8bbe427f 2403
f03fc89f 2404 if (m_widget)
a2053b27 2405 {
f03fc89f 2406 gtk_widget_destroy( m_widget );
c50f1fb9 2407 m_widget = (GtkWidget*) NULL;
a2053b27 2408 }
362c6693 2409}
c801d85f 2410
1e6feb95 2411bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
c801d85f 2412{
0a164d4c 2413 wxCHECK_MSG( !m_needParent || parent, false, wxT("Need complete parent.") );
8bbe427f 2414
a7c26d10
RD
2415 // Use either the given size, or the default if -1 is given.
2416 // See wxWindowBase for these functions.
3013a903 2417 m_width = WidthDefault(size.x) ;
f03fc89f 2418 m_height = HeightDefault(size.y);
8bbe427f 2419
43a18898
RR
2420 m_x = (int)pos.x;
2421 m_y = (int)pos.y;
8bbe427f 2422
0a164d4c 2423 return true;
c801d85f
KB
2424}
2425
1e6feb95 2426void wxWindowGTK::PostCreation()
c801d85f 2427{
82b978d7
RD
2428 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2429
43a18898
RR
2430 if (m_wxwindow)
2431 {
147bc491 2432 if (!m_noExpose)
b02da6b1 2433 {
77ffb593 2434 // these get reported to wxWidgets -> wxPaintEvent
1e6feb95 2435
9fa72bd2
MR
2436 g_signal_connect (m_wxwindow, "expose_event",
2437 G_CALLBACK (gtk_window_expose_callback), this);
147bc491 2438
847dfdb4
RR
2439 if (GetLayoutDirection() == wxLayout_LeftToRight)
2440 gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), HasFlag( wxFULL_REPAINT_ON_RESIZE ) );
93d23d8f 2441 }
2b5f62a0 2442
ed56a258
JS
2443 // Create input method handler
2444 m_imData = new wxGtkIMData;
2445
2b5f62a0 2446 // Cannot handle drawing preedited text yet
a3c15d89 2447 gtk_im_context_set_use_preedit( m_imData->context, FALSE );
2b5f62a0 2448
9fa72bd2 2449 g_signal_connect (m_imData->context, "commit",
da210120 2450 G_CALLBACK (gtk_wxwindow_commit_cb), this);
148cd9b6 2451
67d78217 2452 // these are called when the "sunken" or "raised" borders are drawn
9fa72bd2
MR
2453 g_signal_connect (m_widget, "expose_event",
2454 G_CALLBACK (gtk_window_own_expose_callback), this);
43a18898 2455 }
47d67540 2456
76fcf0f2 2457 // focus handling
63081513 2458
06fda9e8
RR
2459 if (!GTK_IS_WINDOW(m_widget))
2460 {
2461 if (m_focusWidget == NULL)
2462 m_focusWidget = m_widget;
0a164d4c 2463
4c20ee63
RR
2464 if (m_wxwindow)
2465 {
2466 g_signal_connect (m_focusWidget, "focus_in_event",
9fa72bd2 2467 G_CALLBACK (gtk_window_focus_in_callback), this);
4c20ee63 2468 g_signal_connect (m_focusWidget, "focus_out_event",
f1e57cb9 2469 G_CALLBACK (gtk_window_focus_out_callback), this);
4c20ee63
RR
2470 }
2471 else
2472 {
2473 g_signal_connect_after (m_focusWidget, "focus_in_event",
2474 G_CALLBACK (gtk_window_focus_in_callback), this);
2475 g_signal_connect_after (m_focusWidget, "focus_out_event",
2476 G_CALLBACK (gtk_window_focus_out_callback), this);
2477 }
06fda9e8 2478 }
76fcf0f2
RR
2479
2480 // connect to the various key and mouse handlers
63081513 2481
a2053b27 2482 GtkWidget *connect_widget = GetConnectWidget();
f03fc89f 2483
a2053b27 2484 ConnectWidget( connect_widget );
47d67540 2485
63081513 2486 /* We cannot set colours, fonts and cursors before the widget has
a2053b27 2487 been realized, so we do this directly after realization */
9fa72bd2
MR
2488 g_signal_connect (connect_widget, "realize",
2489 G_CALLBACK (gtk_window_realized_callback), this);
2daa0ce9 2490
63081513
RR
2491 if (m_wxwindow)
2492 {
47c93b63 2493 // Catch native resize events
9fa72bd2
MR
2494 g_signal_connect (m_wxwindow, "size_allocate",
2495 G_CALLBACK (gtk_window_size_callback), this);
63081513 2496 }
2daa0ce9 2497
024e9a4c
RR
2498 if (GTK_IS_COMBO(m_widget))
2499 {
2500 GtkCombo *gcombo = GTK_COMBO(m_widget);
0a164d4c 2501
9fa72bd2
MR
2502 g_signal_connect (gcombo->entry, "size_request",
2503 G_CALLBACK (wxgtk_combo_size_request_callback),
2504 this);
024e9a4c 2505 }
5b34e141 2506#ifdef GTK_IS_FILE_CHOOSER_BUTTON
e892f2fd 2507 else if (!gtk_check_version(2,6,0) && GTK_IS_FILE_CHOOSER_BUTTON(m_widget))
ec376c8f 2508 {
556151f5
MW
2509 // If we connect to the "size_request" signal of a GtkFileChooserButton
2510 // then that control won't be sized properly when placed inside sizers
2511 // (this can be tested removing this elseif and running XRC or WIDGETS samples)
ec376c8f
VZ
2512 // FIXME: what should be done here ?
2513 }
5b34e141 2514#endif
024e9a4c 2515 else
47c93b63
RR
2516 {
2517 // This is needed if we want to add our windows into native
024e9a4c 2518 // GTK controls, such as the toolbar. With this callback, the
47c93b63 2519 // toolbar gets to know the correct size (the one set by the
024e9a4c 2520 // programmer). Sadly, it misbehaves for wxComboBox.
9fa72bd2
MR
2521 g_signal_connect (m_widget, "size_request",
2522 G_CALLBACK (wxgtk_window_size_request_callback),
2523 this);
47c93b63 2524 }
1e6feb95 2525
40bab631
VS
2526 InheritAttributes();
2527
0a164d4c 2528 m_hasVMT = true;
e892f2fd 2529
978af864 2530 SetLayoutDirection(wxLayout_Default);
a433fbd5
VZ
2531
2532 // unless the window was created initially hidden (i.e. Hide() had been
2533 // called before Create()), we should show it at GTK+ level as well
2534 if ( IsShown() )
2535 gtk_widget_show( m_widget );
b4071e91
RR
2536}
2537
1e6feb95 2538void wxWindowGTK::ConnectWidget( GtkWidget *widget )
b4071e91 2539{
9fa72bd2
MR
2540 g_signal_connect (widget, "key_press_event",
2541 G_CALLBACK (gtk_window_key_press_callback), this);
2542 g_signal_connect (widget, "key_release_event",
2543 G_CALLBACK (gtk_window_key_release_callback), this);
2544 g_signal_connect (widget, "button_press_event",
2545 G_CALLBACK (gtk_window_button_press_callback), this);
2546 g_signal_connect (widget, "button_release_event",
2547 G_CALLBACK (gtk_window_button_release_callback), this);
2548 g_signal_connect (widget, "motion_notify_event",
2549 G_CALLBACK (gtk_window_motion_notify_callback), this);
2550 g_signal_connect (widget, "scroll_event",
76e4be8e 2551 G_CALLBACK (window_scroll_event), this);
9fa72bd2
MR
2552 g_signal_connect (widget, "popup_menu",
2553 G_CALLBACK (wxgtk_window_popup_menu_callback), this);
2554 g_signal_connect (widget, "enter_notify_event",
2555 G_CALLBACK (gtk_window_enter_callback), this);
2556 g_signal_connect (widget, "leave_notify_event",
2557 G_CALLBACK (gtk_window_leave_callback), this);
362c6693 2558}
c801d85f 2559
1e6feb95 2560bool wxWindowGTK::Destroy()
c801d85f 2561{
82b978d7 2562 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
47d67540 2563
0a164d4c 2564 m_hasVMT = false;
c801d85f 2565
f03fc89f 2566 return wxWindowBase::Destroy();
362c6693 2567}
c801d85f 2568
1e6feb95 2569void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
23efdd02 2570{
69597639 2571 // inform the parent to perform the move
23efdd02 2572 gtk_pizza_set_size( GTK_PIZZA(m_parent->m_wxwindow), m_widget, x, y, width, height );
69597639 2573
23efdd02 2574}
2daa0ce9 2575
1e6feb95 2576void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
c801d85f 2577{
82b978d7 2578 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
1e6feb95 2579 wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") );
8bbe427f 2580
e27ce4e9 2581 if (m_resizing) return; /* I don't like recursions */
0a164d4c 2582 m_resizing = true;
1e6feb95 2583
b9f29261
VS
2584 int currentX, currentY;
2585 GetPosition(&currentX, &currentY);
443c834d 2586 if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
b9f29261 2587 x = currentX;
443c834d 2588 if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
b9f29261 2589 y = currentY;
a200c35e
VS
2590 AdjustForParentClientOrigin(x, y, sizeFlags);
2591
fe39b16a
RR
2592 // calculate the best size if we should auto size the window
2593 if ( ((sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1) ||
2594 ((sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1) )
2595 {
2596 const wxSize sizeBest = GetBestSize();
2597 if ( (sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1 )
2598 width = sizeBest.x;
2599 if ( (sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1 )
2600 height = sizeBest.y;
2601 }
2602
2603 if (width != -1)
2604 m_width = width;
2605 if (height != -1)
2606 m_height = height;
2607
2608 int minWidth = GetMinWidth(),
2609 minHeight = GetMinHeight(),
2610 maxWidth = GetMaxWidth(),
2611 maxHeight = GetMaxHeight();
2612
2613 if ((minWidth != -1) && (m_width < minWidth )) m_width = minWidth;
2614 if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight;
2615 if ((maxWidth != -1) && (m_width > maxWidth )) m_width = maxWidth;
2616 if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
2617
2618#if wxUSE_TOOLBAR_NATIVE
2619 if (wxDynamicCast(GetParent(), wxToolBar))
fb1585ae 2620 {
fe39b16a
RR
2621 // don't take the x,y values, they're wrong because toolbar sets them
2622 GtkWidget *widget = GTK_WIDGET(m_widget);
2623 gtk_widget_set_size_request (widget, m_width, m_height);
fe39b16a 2624 }
2e1f5012 2625 else
fe39b16a
RR
2626#endif
2627 if (m_parent->m_wxwindow == NULL) // i.e. wxNotebook
2628 {
2629 // don't set the size for children of wxNotebook, just take the values.
fb1585ae
RR
2630 m_x = x;
2631 m_y = y;
2632 m_width = width;
ba4e3652 2633 m_height = height;
fb1585ae 2634 }
ba4e3652 2635 else
fb1585ae 2636 {
da048e3d 2637 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
85ad5eb5 2638 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
ba4e3652 2639 {
a31bb944
RR
2640 if (x != -1) m_x = x + gtk_pizza_get_xoffset( pizza );
2641 if (y != -1) m_y = y + gtk_pizza_get_yoffset( pizza );
ba4e3652
RR
2642 }
2643 else
2644 {
a31bb944
RR
2645 m_x = x + gtk_pizza_get_xoffset( pizza );
2646 m_y = y + gtk_pizza_get_yoffset( pizza );
ba4e3652 2647 }
47d67540 2648
863e0817
RR
2649 int left_border = 0;
2650 int right_border = 0;
2651 int top_border = 0;
c50f1fb9 2652 int bottom_border = 0;
f03fc89f 2653
863e0817 2654 /* the default button has a border around it */
29f538ce 2655 if (GTK_WIDGET_CAN_DEFAULT(m_widget))
c50f1fb9 2656 {
f893066b
RR
2657 GtkBorder *default_border = NULL;
2658 gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
2659 if (default_border)
863e0817 2660 {
f893066b
RR
2661 left_border += default_border->left;
2662 right_border += default_border->right;
2663 top_border += default_border->top;
2664 bottom_border += default_border->bottom;
2665 g_free( default_border );
863e0817 2666 }
863e0817 2667 }
c50f1fb9 2668
863e0817
RR
2669 DoMoveWindow( m_x-top_border,
2670 m_y-left_border,
2671 m_width+left_border+right_border,
2672 m_height+top_border+bottom_border );
54517652 2673 }
148cd9b6 2674
5b8a521e
RR
2675 if (m_hasScrolling)
2676 {
1e6feb95 2677 /* Sometimes the client area changes size without the
b6fa52db
RR
2678 whole windows's size changing, but if the whole
2679 windows's size doesn't change, no wxSizeEvent will
2680 normally be sent. Here we add an extra test if
2681 the client test has been changed and this will
2682 be used then. */
5b8a521e
RR
2683 GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
2684 }
2685
54517652 2686/*
6d693bb4
RR
2687 wxPrintf( "OnSize sent from " );
2688 if (GetClassInfo() && GetClassInfo()->GetClassName())
2689 wxPrintf( GetClassInfo()->GetClassName() );
2690 wxPrintf( " %d %d %d %d\n", (int)m_x, (int)m_y, (int)m_width, (int)m_height );
2691*/
2692
30760ce7
RR
2693 if (!m_nativeSizeEvent)
2694 {
2695 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
2696 event.SetEventObject( this );
2697 GetEventHandler()->ProcessEvent( event );
2698 }
6d693bb4 2699
0a164d4c 2700 m_resizing = false;
362c6693 2701}
c801d85f 2702
7317857d 2703bool wxWindowGTK::GtkShowFromOnIdle()
9390a202 2704{
f46ad98f
RR
2705 if (IsShown() && m_showOnIdle && !GTK_WIDGET_VISIBLE (m_widget))
2706 {
2707 GtkAllocation alloc;
2708 alloc.x = m_x;
2709 alloc.y = m_y;
2710 alloc.width = m_width;
2711 alloc.height = m_height;
2712 gtk_widget_size_allocate( m_widget, &alloc );
2713 gtk_widget_show( m_widget );
2714 wxShowEvent eventShow(GetId(), true);
2715 eventShow.SetEventObject(this);
2716 GetEventHandler()->ProcessEvent(eventShow);
2717 m_showOnIdle = false;
7317857d 2718 return true;
f46ad98f 2719 }
02761f6c 2720
7317857d
RR
2721 return false;
2722}
2723
2724void wxWindowGTK::OnInternalIdle()
2725{
2726 // Check if we have to show window now
2727 if (GtkShowFromOnIdle()) return;
2728
2729 if ( m_dirtyTabOrder )
2730 {
2731 m_dirtyTabOrder = false;
2732 RealizeTabOrder();
2733 }
2734
2735 // Update style if the window was not yet realized
2736 // and SetBackgroundStyle(wxBG_STYLE_CUSTOM) was called
2737 if (m_needsStyleChange)
2738 {
2739 SetBackgroundStyle(GetBackgroundStyle());
2740 m_needsStyleChange = false;
2741 }
02761f6c 2742
9146082c
RR
2743 wxCursor cursor = m_cursor;
2744 if (g_globalCursor.Ok()) cursor = g_globalCursor;
c50f1fb9 2745
f7a11f8c 2746 if (cursor.Ok())
9146082c 2747 {
3017f78d 2748 /* I now set the cursor anew in every OnInternalIdle call
b02da6b1
VZ
2749 as setting the cursor in a parent window also effects the
2750 windows above so that checking for the current cursor is
2751 not possible. */
148cd9b6 2752
9146082c 2753 if (m_wxwindow)
6a008b33 2754 {
da048e3d 2755 GdkWindow *window = GTK_PIZZA(m_wxwindow)->bin_window;
6a008b33 2756 if (window)
c50f1fb9 2757 gdk_window_set_cursor( window, cursor.GetCursor() );
6a008b33
VZ
2758
2759 if (!g_globalCursor.Ok())
2760 cursor = *wxSTANDARD_CURSOR;
2761
2762 window = m_widget->window;
5e014a0c 2763 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
9146082c 2764 gdk_window_set_cursor( window, cursor.GetCursor() );
5e014a0c 2765
6a008b33 2766 }
b3d006af 2767 else if ( m_widget )
6a008b33 2768 {
9146082c 2769 GdkWindow *window = m_widget->window;
b3d006af 2770 if ( window && !GTK_WIDGET_NO_WINDOW(m_widget) )
9146082c 2771 gdk_window_set_cursor( window, cursor.GetCursor() );
6a008b33 2772 }
9146082c 2773 }
6a008b33 2774
e39af974
JS
2775 if (wxUpdateUIEvent::CanUpdate(this))
2776 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
9390a202
RR
2777}
2778
1e6feb95 2779void wxWindowGTK::DoGetSize( int *width, int *height ) const
c801d85f 2780{
82b978d7 2781 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 2782
fb1585ae
RR
2783 if (width) (*width) = m_width;
2784 if (height) (*height) = m_height;
362c6693 2785}
c801d85f 2786
1e6feb95 2787void wxWindowGTK::DoSetClientSize( int width, int height )
c801d85f 2788{
82b978d7
RD
2789 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2790
09bf8378 2791 if (m_wxwindow)
c801d85f 2792 {
1ecc4d80
RR
2793 int dw = 0;
2794 int dh = 0;
2795
09bf8378
PC
2796 if (m_hasScrolling)
2797 {
2798 GetScrollbarWidth(m_widget, dw, dh);
2799 }
2800
af05af4d
PC
2801 const int border = GTK_CONTAINER(m_wxwindow)->border_width;
2802 dw += 2 * border;
2803 dh += 2 * border;
034be888 2804
09bf8378
PC
2805 width += dw;
2806 height += dh;
1ecc4d80 2807 }
09bf8378
PC
2808
2809 SetSize(width, height);
362c6693 2810}
c801d85f 2811
1e6feb95 2812void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
c801d85f 2813{
82b978d7 2814 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 2815
09bf8378
PC
2816 int w = m_width;
2817 int h = m_height;
2818
2819 if (m_wxwindow)
c801d85f 2820 {
1ecc4d80
RR
2821 int dw = 0;
2822 int dh = 0;
2823
09bf8378 2824 if (m_hasScrolling)
09bf8378 2825 GetScrollbarWidth(m_widget, dw, dh);
09bf8378 2826
af05af4d
PC
2827 const int border = GTK_CONTAINER(m_wxwindow)->border_width;
2828 dw += 2 * border;
2829 dh += 2 * border;
9000c624 2830
09bf8378
PC
2831 w -= dw;
2832 h -= dh;
69346023
PC
2833 if (w < 0)
2834 w = 0;
2835 if (h < 0)
2836 h = 0;
1ecc4d80 2837 }
1e6feb95 2838
09bf8378
PC
2839 if (width) *width = w;
2840 if (height) *height = h;
362c6693 2841}
c801d85f 2842
1e6feb95 2843void wxWindowGTK::DoGetPosition( int *x, int *y ) const
c801d85f 2844{
82b978d7 2845 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 2846
bf0c00c6
RR
2847 int dx = 0;
2848 int dy = 0;
2849 if (m_parent && m_parent->m_wxwindow)
2850 {
da048e3d 2851 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
a31bb944
RR
2852 dx = gtk_pizza_get_xoffset( pizza );
2853 dy = gtk_pizza_get_yoffset( pizza );
bf0c00c6 2854 }
94633ad9 2855
96c8547e
JS
2856 if (m_x == -1 && m_y == -1)
2857 {
2858 GdkWindow *source = (GdkWindow *) NULL;
2859 if (m_wxwindow)
2860 source = GTK_PIZZA(m_wxwindow)->bin_window;
2861 else
2862 source = m_widget->window;
2863
2864 if (source)
2865 {
2866 int org_x = 0;
2867 int org_y = 0;
2868 gdk_window_get_origin( source, &org_x, &org_y );
2869
2870 if (GetParent())
2871 GetParent()->ScreenToClient(&org_x, &org_y);
2872
bfeeb7f3
PC
2873 wx_const_cast(wxWindowGTK*, this)->m_x = org_x;
2874 wx_const_cast(wxWindowGTK*, this)->m_y = org_y;
2875 }
96c8547e
JS
2876 }
2877
496beb3f
VS
2878 if (x) (*x) = m_x - dx;
2879 if (y) (*y) = m_y - dy;
362c6693 2880}
c801d85f 2881
1e6feb95 2882void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
c801d85f 2883{
82b978d7 2884 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 2885
a2053b27
RR
2886 if (!m_widget->window) return;
2887
43a18898
RR
2888 GdkWindow *source = (GdkWindow *) NULL;
2889 if (m_wxwindow)
da048e3d 2890 source = GTK_PIZZA(m_wxwindow)->bin_window;
43a18898
RR
2891 else
2892 source = m_widget->window;
47d67540 2893
43a18898
RR
2894 int org_x = 0;
2895 int org_y = 0;
2896 gdk_window_get_origin( source, &org_x, &org_y );
c801d85f 2897
43a18898 2898 if (!m_wxwindow)
c801d85f 2899 {
43a18898
RR
2900 if (GTK_WIDGET_NO_WINDOW (m_widget))
2901 {
2902 org_x += m_widget->allocation.x;
2903 org_y += m_widget->allocation.y;
2904 }
362c6693 2905 }
47d67540 2906
49e74855
RR
2907
2908 if (x)
2909 {
2910 if (GetLayoutDirection() == wxLayout_RightToLeft)
2911 *x = (GetClientSize().x - *x) + org_x;
2912 else
2913 *x += org_x;
2914 }
2915
43a18898 2916 if (y) *y += org_y;
362c6693 2917}
c801d85f 2918
1e6feb95 2919void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
c801d85f 2920{
82b978d7 2921 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
47d67540 2922
a2053b27
RR
2923 if (!m_widget->window) return;
2924
1ecc4d80
RR
2925 GdkWindow *source = (GdkWindow *) NULL;
2926 if (m_wxwindow)
da048e3d 2927 source = GTK_PIZZA(m_wxwindow)->bin_window;
1ecc4d80
RR
2928 else
2929 source = m_widget->window;
47d67540 2930
1ecc4d80
RR
2931 int org_x = 0;
2932 int org_y = 0;
2933 gdk_window_get_origin( source, &org_x, &org_y );
c801d85f 2934
1ecc4d80 2935 if (!m_wxwindow)
c801d85f 2936 {
1ecc4d80
RR
2937 if (GTK_WIDGET_NO_WINDOW (m_widget))
2938 {
2939 org_x += m_widget->allocation.x;
2940 org_y += m_widget->allocation.y;
2941 }
362c6693 2942 }
47d67540 2943
49e74855
RR
2944 if (x)
2945 {
2946 if (GetLayoutDirection() == wxLayout_RightToLeft)
2947 *x = (GetClientSize().x - *x) - org_x;
2948 else
2949 *x -= org_x;
2950 }
1ecc4d80 2951 if (y) *y -= org_y;
362c6693 2952}
c801d85f 2953
1e6feb95 2954bool wxWindowGTK::Show( bool show )
c801d85f 2955{
0a164d4c 2956 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
47d67540 2957
739730ca
RR
2958 if (!wxWindowBase::Show(show))
2959 {
2960 // nothing to do
0a164d4c 2961 return false;
739730ca 2962 }
8bbe427f 2963
f03fc89f 2964 if (show)
f46ad98f
RR
2965 {
2966 if (!m_showOnIdle)
2967 {
2968 gtk_widget_show( m_widget );
2969 wxShowEvent eventShow(GetId(), show);
2970 eventShow.SetEventObject(this);
2971 GetEventHandler()->ProcessEvent(eventShow);
2972 }
2973 }
1ecc4d80 2974 else
f46ad98f 2975 {
f03fc89f 2976 gtk_widget_hide( m_widget );
f46ad98f
RR
2977 wxShowEvent eventShow(GetId(), show);
2978 eventShow.SetEventObject(this);
2979 GetEventHandler()->ProcessEvent(eventShow);
2980 }
2b5f62a0 2981
0a164d4c 2982 return true;
362c6693 2983}
c801d85f 2984
3379ed37 2985static void wxWindowNotifyEnable(wxWindowGTK* win, bool enable)
fdca68a6
JS
2986{
2987 win->OnParentEnable(enable);
2988
2989 // Recurse, so that children have the opportunity to Do The Right Thing
2990 // and reset colours that have been messed up by a parent's (really ancestor's)
2991 // Enable call
222ed1d6 2992 for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
fdca68a6
JS
2993 node;
2994 node = node->GetNext() )
2995 {
2996 wxWindow *child = node->GetData();
2997 if (!child->IsKindOf(CLASSINFO(wxDialog)) && !child->IsKindOf(CLASSINFO(wxFrame)))
2998 wxWindowNotifyEnable(child, enable);
2999 }
3000}
3001
3379ed37 3002bool wxWindowGTK::Enable( bool enable )
c801d85f 3003{
0a164d4c 3004 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
82b978d7 3005
739730ca
RR
3006 if (!wxWindowBase::Enable(enable))
3007 {
3008 // nothing to do
0a164d4c 3009 return false;
739730ca 3010 }
1ecc4d80 3011
f03fc89f
VZ
3012 gtk_widget_set_sensitive( m_widget, enable );
3013 if ( m_wxwindow )
3014 gtk_widget_set_sensitive( m_wxwindow, enable );
ff8bfdbb 3015
fdca68a6 3016 wxWindowNotifyEnable(this, enable);
513903c4 3017
0a164d4c 3018 return true;
362c6693 3019}
c801d85f 3020
1e6feb95 3021int wxWindowGTK::GetCharHeight() const
2f2aa628 3022{
82b978d7 3023 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
47d67540 3024
cc402e64
VZ
3025 wxFont font = GetFont();
3026 wxCHECK_MSG( font.Ok(), 12, wxT("invalid font") );
2f2aa628 3027
bbd006c0
RR
3028 PangoContext *context = NULL;
3029 if (m_widget)
3030 context = gtk_widget_get_pango_context( m_widget );
3031
3032 if (!context)
3033 return 0;
3034
cc402e64 3035 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
bbd006c0
RR
3036 PangoLayout *layout = pango_layout_new(context);
3037 pango_layout_set_font_description(layout, desc);
3038 pango_layout_set_text(layout, "H", 1);
3039 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3040
3041 PangoRectangle rect;
3042 pango_layout_line_get_extents(line, NULL, &rect);
3043
3fe39b0c 3044 g_object_unref (layout);
7de59551 3045
f69e2009 3046 return (int) PANGO_PIXELS(rect.height);
362c6693 3047}
c801d85f 3048
1e6feb95 3049int wxWindowGTK::GetCharWidth() const
c33c4050 3050{
82b978d7 3051 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
47d67540 3052
cc402e64
VZ
3053 wxFont font = GetFont();
3054 wxCHECK_MSG( font.Ok(), 8, wxT("invalid font") );
47d67540 3055
bbd006c0
RR
3056 PangoContext *context = NULL;
3057 if (m_widget)
3058 context = gtk_widget_get_pango_context( m_widget );
3059
3060 if (!context)
3061 return 0;
3062
cc402e64 3063 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
bbd006c0
RR
3064 PangoLayout *layout = pango_layout_new(context);
3065 pango_layout_set_font_description(layout, desc);
95c430aa 3066 pango_layout_set_text(layout, "g", 1);
bbd006c0
RR
3067 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3068
3069 PangoRectangle rect;
3070 pango_layout_line_get_extents(line, NULL, &rect);
3071
3fe39b0c 3072 g_object_unref (layout);
7de59551 3073
f69e2009 3074 return (int) PANGO_PIXELS(rect.width);
c33c4050
RR
3075}
3076
1e6feb95 3077void wxWindowGTK::GetTextExtent( const wxString& string,
0a164d4c
WS
3078 int *x,
3079 int *y,
3080 int *descent,
3081 int *externalLeading,
3082 const wxFont *theFont ) const
c33c4050 3083{
cc402e64 3084 wxFont fontToUse = theFont ? *theFont : GetFont();
47d67540 3085
223d09f6 3086 wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") );
2b5f62a0 3087
0a164d4c 3088 if (string.empty())
48d011c8 3089 {
b15ed747
RR
3090 if (x) (*x) = 0;
3091 if (y) (*y) = 0;
48d011c8
RR
3092 return;
3093 }
47d67540 3094
48d011c8
RR
3095 PangoContext *context = NULL;
3096 if (m_widget)
2b5f62a0
VZ
3097 context = gtk_widget_get_pango_context( m_widget );
3098
48d011c8
RR
3099 if (!context)
3100 {
b15ed747
RR
3101 if (x) (*x) = 0;
3102 if (y) (*y) = 0;
48d011c8
RR
3103 return;
3104 }
2b5f62a0 3105
48d011c8
RR
3106 PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
3107 PangoLayout *layout = pango_layout_new(context);
3108 pango_layout_set_font_description(layout, desc);
3109 {
a3669332
VZ
3110 const wxCharBuffer data = wxGTK_CONV( string );
3111 if ( data )
3112 pango_layout_set_text(layout, data, strlen(data));
48d011c8 3113 }
2b5f62a0 3114
48d011c8 3115 PangoRectangle rect;
fd43b1b3 3116 pango_layout_get_extents(layout, NULL, &rect);
2b5f62a0 3117
f69e2009
VS
3118 if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width);
3119 if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height);
48d011c8
RR
3120 if (descent)
3121 {
f69e2009
VS
3122 PangoLayoutIter *iter = pango_layout_get_iter(layout);
3123 int baseline = pango_layout_iter_get_baseline(iter);
3124 pango_layout_iter_free(iter);
3125 *descent = *y - PANGO_PIXELS(baseline);
48d011c8
RR
3126 }
3127 if (externalLeading) (*externalLeading) = 0; // ??
2b5f62a0 3128
3fe39b0c 3129 g_object_unref (layout);
c33c4050
RR
3130}
3131
ef5c70f9
VZ
3132bool wxWindowGTK::GTKSetDelayedFocusIfNeeded()
3133{
3134 if ( g_delayedFocus == this )
3135 {
3136 if ( GTK_WIDGET_REALIZED(m_widget) )
3137 {
3138 gtk_widget_grab_focus(m_widget);
3139 g_delayedFocus = NULL;
3140
3141 return true;
3142 }
3143 }
3144
3145 return false;
3146}
3147
1e6feb95 3148void wxWindowGTK::SetFocus()
c801d85f 3149{
82b978d7 3150 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
6cad4f1b
VZ
3151 if ( m_hasFocus )
3152 {
3153 // don't do anything if we already have focus
3154 return;
3155 }
2daa0ce9 3156
354aa1e3
RR
3157 if (m_wxwindow)
3158 {
173348db 3159 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
b231914f 3160 {
173348db 3161 gtk_widget_grab_focus (m_wxwindow);
b231914f 3162 }
354aa1e3 3163 }
b231914f 3164 else if (m_widget)
c801d85f 3165 {
eccd5602
RR
3166 if (GTK_IS_CONTAINER(m_widget))
3167 {
ccb5db57
RR
3168 if (IsKindOf(CLASSINFO(wxRadioButton)))
3169 {
3170 gtk_widget_grab_focus (m_widget);
3171 return;
3172 }
3173
eccd5602
RR
3174 gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
3175 }
3176 else
173348db 3177 if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
463c1fa1 3178 {
0a164d4c 3179
d7fa7eaa 3180 if (!GTK_WIDGET_REALIZED(m_widget))
6aeb6f2a 3181 {
6cad4f1b
VZ
3182 // we can't set the focus to the widget now so we remember that
3183 // it should be focused and will do it later, during the idle
3184 // time, as soon as we can
3185 wxLogTrace(TRACE_FOCUS,
3186 _T("Delaying setting focus to %s(%s)"),
6aeb6f2a
VZ
3187 GetClassInfo()->GetClassName(), GetLabel().c_str());
3188
d7fa7eaa 3189 g_delayedFocus = this;
6aeb6f2a 3190 }
d7fa7eaa 3191 else
6aeb6f2a 3192 {
6cad4f1b
VZ
3193 wxLogTrace(TRACE_FOCUS,
3194 _T("Setting focus to %s(%s)"),
6aeb6f2a
VZ
3195 GetClassInfo()->GetClassName(), GetLabel().c_str());
3196
d7fa7eaa 3197 gtk_widget_grab_focus (m_widget);
6aeb6f2a 3198 }
463c1fa1 3199 }
0a164d4c 3200 else
ff8bfdbb 3201 {
6cad4f1b
VZ
3202 wxLogTrace(TRACE_FOCUS,
3203 _T("Can't set focus to %s(%s)"),
3204 GetClassInfo()->GetClassName(), GetLabel().c_str());
ff8bfdbb 3205 }
362c6693 3206 }
362c6693 3207}
c801d85f 3208
1e6feb95 3209bool wxWindowGTK::AcceptsFocus() const
b292e2f5 3210{
f03fc89f 3211 return m_acceptsFocus && wxWindowBase::AcceptsFocus();
b292e2f5
RR
3212}
3213
1e6feb95 3214bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
463c1fa1 3215{
0a164d4c 3216 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
c50f1fb9 3217
1e6feb95
VZ
3218 wxWindowGTK *oldParent = m_parent,
3219 *newParent = (wxWindowGTK *)newParentBase;
a2053b27 3220
5fd11f09
RR
3221 wxASSERT( GTK_IS_WIDGET(m_widget) );
3222
f03fc89f 3223 if ( !wxWindowBase::Reparent(newParent) )
0a164d4c 3224 return false;
8bbe427f 3225
5fd11f09
RR
3226 wxASSERT( GTK_IS_WIDGET(m_widget) );
3227
3228 /* prevent GTK from deleting the widget arbitrarily */
3229 gtk_widget_ref( m_widget );
3230
8ce63e9d
RR
3231 if (oldParent)
3232 {
3017f78d 3233 gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
8ce63e9d 3234 }
c50f1fb9 3235
5fd11f09
RR
3236 wxASSERT( GTK_IS_WIDGET(m_widget) );
3237
8ce63e9d
RR
3238 if (newParent)
3239 {
f46ad98f
RR
3240 if (GTK_WIDGET_VISIBLE (newParent->m_widget))
3241 {
3242 m_showOnIdle = true;
3243 gtk_widget_hide( m_widget );
3244 }
02761f6c 3245
8ce63e9d
RR
3246 /* insert GTK representation */
3247 (*(newParent->m_insertCallback))(newParent, this);
3248 }
c50f1fb9 3249
5fd11f09
RR
3250 /* reverse: prevent GTK from deleting the widget arbitrarily */
3251 gtk_widget_unref( m_widget );
e892f2fd 3252
978af864 3253 SetLayoutDirection(wxLayout_Default);
148cd9b6 3254
0a164d4c 3255 return true;
362c6693 3256}
c801d85f 3257
1e6feb95 3258void wxWindowGTK::DoAddChild(wxWindowGTK *child)
ddb6bc71 3259{
223d09f6 3260 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
ddb6bc71 3261
223d09f6 3262 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
ddb6bc71 3263
223d09f6 3264 wxASSERT_MSG( (m_insertCallback != NULL), wxT("invalid child insertion function") );
c50f1fb9 3265
ddb6bc71
RR
3266 /* add to list */
3267 AddChild( child );
c50f1fb9 3268
ddb6bc71
RR
3269 /* insert GTK representation */
3270 (*m_insertCallback)(this, child);
3271}
3272
a589495e
VS
3273void wxWindowGTK::AddChild(wxWindowBase *child)
3274{
3275 wxWindowBase::AddChild(child);
3276 m_dirtyTabOrder = true;
3277 if (g_isIdle)
3278 wxapp_install_idle_handler();
3279}
3280
3281void wxWindowGTK::RemoveChild(wxWindowBase *child)
3282{
3283 wxWindowBase::RemoveChild(child);
3284 m_dirtyTabOrder = true;
3285 if (g_isIdle)
3286 wxapp_install_idle_handler();
3287}
0a164d4c 3288
978af864
VZ
3289/* static */
3290wxLayoutDirection wxWindowGTK::GTKGetLayout(GtkWidget *widget)
3291{
3292 return gtk_widget_get_direction(GTK_WIDGET(widget)) == GTK_TEXT_DIR_RTL
3293 ? wxLayout_RightToLeft
3294 : wxLayout_LeftToRight;
3295}
3296
3297/* static */
3298void wxWindowGTK::GTKSetLayout(GtkWidget *widget, wxLayoutDirection dir)
3299{
3300 wxASSERT_MSG( dir != wxLayout_Default, _T("invalid layout direction") );
3301
3302 gtk_widget_set_direction(GTK_WIDGET(widget),
3303 dir == wxLayout_RightToLeft ? GTK_TEXT_DIR_RTL
3304 : GTK_TEXT_DIR_LTR);
3305}
3306
3307wxLayoutDirection wxWindowGTK::GetLayoutDirection() const
3308{
3309 return GTKGetLayout(m_widget);
3310}
3311
3312void wxWindowGTK::SetLayoutDirection(wxLayoutDirection dir)
3313{
3314 if ( dir == wxLayout_Default )
3315 {
3316 const wxWindow *const parent = GetParent();
3317 if ( parent )
3318 {
3319 // inherit layout from parent.
3320 dir = parent->GetLayoutDirection();
3321 }
3322 else // no parent, use global default layout
3323 {
3324 dir = wxTheApp->GetLayoutDirection();
3325 }
3326 }
3327
3328 if ( dir == wxLayout_Default )
3329 return;
3330
3331 GTKSetLayout(m_widget, dir);
2df5e0bf
RR
3332
3333 if (m_wxwindow)
69597639
RR
3334 GTKSetLayout(m_wxwindow, dir);
3335}
3336
3337wxCoord
3338wxWindowGTK::AdjustForLayoutDirection(wxCoord x,
3339 wxCoord WXUNUSED(width),
3340 wxCoord WXUNUSED(widthTotal)) const
3341{
3342 // We now mirrors the coordinates of RTL windows in GtkPizza
3343 return x;
978af864
VZ
3344}
3345
a589495e
VS
3346void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, MoveKind move)
3347{
3348 wxWindowBase::DoMoveInTabOrder(win, move);
3349 m_dirtyTabOrder = true;
3350 if (g_isIdle)
3351 wxapp_install_idle_handler();
3352}
3353
2e1f5012
VZ
3354bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3355{
3356 // none needed by default
3357 return false;
3358}
3359
3360void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget* WXUNUSED(w))
3361{
3362 // nothing to do by default since none is needed
3363}
3364
a589495e
VS
3365void wxWindowGTK::RealizeTabOrder()
3366{
3367 if (m_wxwindow)
3368 {
12848fda 3369 if ( !m_children.empty() )
a589495e 3370 {
259858fc 3371 // we don't only construct the correct focus chain but also use
2e1f5012
VZ
3372 // this opportunity to update the mnemonic widgets for the widgets
3373 // that need them
259858fc 3374
a589495e 3375 GList *chain = NULL;
2e1f5012 3376 wxWindowGTK* mnemonicWindow = NULL;
0a164d4c 3377
12848fda
VZ
3378 for ( wxWindowList::const_iterator i = m_children.begin();
3379 i != m_children.end();
3380 ++i )
a589495e 3381 {
259858fc 3382 wxWindowGTK *win = *i;
2e1f5012
VZ
3383
3384 if ( mnemonicWindow )
259858fc
VZ
3385 {
3386 if ( win->AcceptsFocusFromKeyboard() )
3387 {
2e1f5012
VZ
3388 // wxComboBox et al. needs to focus on on a different
3389 // widget than m_widget, so if the main widget isn't
3390 // focusable try the connect widget
3391 GtkWidget* w = win->m_widget;
3392 if ( !GTK_WIDGET_CAN_FOCUS(w) )
3393 {
3394 w = win->GetConnectWidget();
3395 if ( !GTK_WIDGET_CAN_FOCUS(w) )
3396 w = NULL;
3397 }
3398
3399 if ( w )
3400 {
3401 mnemonicWindow->GTKWidgetDoSetMnemonic(w);
3402 mnemonicWindow = NULL;
3403 }
259858fc
VZ
3404 }
3405 }
2e1f5012 3406 else if ( win->GTKWidgetNeedsMnemonic() )
259858fc 3407 {
2e1f5012 3408 mnemonicWindow = win;
259858fc 3409 }
259858fc
VZ
3410
3411 chain = g_list_prepend(chain, win->m_widget);
a589495e 3412 }
0a164d4c 3413
a589495e 3414 chain = g_list_reverse(chain);
0a164d4c 3415
a589495e
VS
3416 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain);
3417 g_list_free(chain);
3418 }
12848fda 3419 else // no children
a589495e
VS
3420 {
3421 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow));
3422 }
3423 }
a589495e
VS
3424}
3425
1e6feb95 3426void wxWindowGTK::Raise()
362c6693 3427{
82b978d7
RD
3428 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3429
fdfb8475
RR
3430 if (m_wxwindow && m_wxwindow->window)
3431 {
3432 gdk_window_raise( m_wxwindow->window );
3433 }
0a164d4c 3434 else if (m_widget->window)
fdfb8475
RR
3435 {
3436 gdk_window_raise( m_widget->window );
3437 }
362c6693
RR
3438}
3439
1e6feb95 3440void wxWindowGTK::Lower()
362c6693 3441{
82b978d7
RD
3442 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3443
fdfb8475
RR
3444 if (m_wxwindow && m_wxwindow->window)
3445 {
3446 gdk_window_lower( m_wxwindow->window );
3447 }
0a164d4c 3448 else if (m_widget->window)
fdfb8475
RR
3449 {
3450 gdk_window_lower( m_widget->window );
3451 }
362c6693 3452}
c801d85f 3453
1e6feb95 3454bool wxWindowGTK::SetCursor( const wxCursor &cursor )
86b29a61 3455{
fe282c08 3456 if ( !wxWindowBase::SetCursor(cursor.Ok() ? cursor : *wxSTANDARD_CURSOR) )
2f262021 3457 return false;
f6bcfd97 3458
2f262021 3459 GTKUpdateCursor();
1e6feb95 3460
2f262021 3461 return true;
ef5c70f9
VZ
3462}
3463
3464void wxWindowGTK::GTKUpdateCursor()
3465{
3466 wxCursor cursor(g_globalCursor.Ok() ? g_globalCursor : GetCursor());
3467 if ( cursor.Ok() )
3468 {
3469 wxArrayGdkWindows windowsThis;
3470 GdkWindow * const winThis = GTKGetWindow(windowsThis);
3471 if ( winThis )
3472 {
3473 gdk_window_set_cursor(winThis, cursor.GetCursor());
3474 }
3475 else
3476 {
3477 const size_t count = windowsThis.size();
3478 for ( size_t n = 0; n < count; n++ )
3479 {
5dd7eef7
VZ
3480 GdkWindow *win = windowsThis[n];
3481 if ( !win )
3482 {
3483 wxFAIL_MSG(_T("NULL window returned by GTKGetWindow()?"));
3484 continue;
3485 }
3486
3487 gdk_window_set_cursor(win, cursor.GetCursor());
ef5c70f9
VZ
3488 }
3489 }
3490 }
362c6693 3491}
c801d85f 3492
1e6feb95 3493void wxWindowGTK::WarpPointer( int x, int y )
4f22cf8d 3494{
82b978d7
RD
3495 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3496
3bcc8d15
RR
3497 // We provide this function ourselves as it is
3498 // missing in GDK (top of this file).
148cd9b6 3499
ed673c6a
RR
3500 GdkWindow *window = (GdkWindow*) NULL;
3501 if (m_wxwindow)
da048e3d 3502 window = GTK_PIZZA(m_wxwindow)->bin_window;
ed673c6a
RR
3503 else
3504 window = GetConnectWidget()->window;
148cd9b6 3505
ed673c6a
RR
3506 if (window)
3507 gdk_window_warp_pointer( window, x, y );
4f22cf8d
RR
3508}
3509
22c9b211 3510wxWindowGTK::ScrollDir wxWindowGTK::ScrollDirFromRange(GtkRange *range) const
0c131a5a 3511{
22c9b211
VZ
3512 // find the scrollbar which generated the event
3513 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
0c131a5a 3514 {
22c9b211
VZ
3515 if ( range == m_scrollBar[dir] )
3516 return (ScrollDir)dir;
0c131a5a 3517 }
22c9b211
VZ
3518
3519 wxFAIL_MSG( _T("event from unknown scrollbar received") );
3520
3521 return ScrollDir_Max;
0c131a5a
VZ
3522}
3523
22c9b211 3524bool wxWindowGTK::DoScrollByUnits(ScrollDir dir, ScrollUnit unit, int units)
0c131a5a 3525{
add7cadd 3526 bool changed = false;
22c9b211
VZ
3527 GtkRange* range = m_scrollBar[dir];
3528 if ( range && units )
add7cadd
PC
3529 {
3530 GtkAdjustment* adj = range->adjustment;
22c9b211
VZ
3531 gdouble inc = unit == ScrollUnit_Line ? adj->step_increment
3532 : adj->page_increment;
3533
3534 const int posOld = int(adj->value + 0.5);
3535 gtk_range_set_value(range, posOld + units*inc);
3536
3537 changed = int(adj->value + 0.5) != posOld;
add7cadd 3538 }
22c9b211 3539
add7cadd 3540 return changed;
0c131a5a 3541}
3013a903 3542
22c9b211
VZ
3543bool wxWindowGTK::ScrollLines(int lines)
3544{
3545 return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Line, lines);
3546}
3547
3548bool wxWindowGTK::ScrollPages(int pages)
3549{
3550 return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Page, pages);
3551}
3552
1e6feb95 3553void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
c801d85f 3554{
a67f1484
VZ
3555 if (!m_widget)
3556 return;
3557 if (!m_widget->window)
3558 return;
2b5f62a0 3559
4e5a4c69
RR
3560 if (m_wxwindow)
3561 {
f46ad98f 3562 if (!GTK_PIZZA(m_wxwindow)->bin_window) return;
02761f6c 3563
a67f1484
VZ
3564 GdkRectangle gdk_rect,
3565 *p;
4e5a4c69
RR
3566 if (rect)
3567 {
4e5a4c69
RR
3568 gdk_rect.x = rect->x;
3569 gdk_rect.y = rect->y;
3570 gdk_rect.width = rect->width;
3571 gdk_rect.height = rect->height;
49e74855
RR
3572 if (GetLayoutDirection() == wxLayout_RightToLeft)
3573 gdk_rect.x = GetClientSize().x - gdk_rect.x - gdk_rect.width;
807a572e 3574
a67f1484 3575 p = &gdk_rect;
4e5a4c69 3576 }
a67f1484 3577 else // invalidate everything
4e5a4c69 3578 {
a67f1484 3579 p = NULL;
4e5a4c69 3580 }
847dfdb4 3581
a67f1484 3582 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, p, TRUE );
4e5a4c69 3583 }
362c6693 3584}
c801d85f 3585
beab25bd 3586void wxWindowGTK::Update()
010afced
RR
3587{
3588 GtkUpdate();
1b965a9c
VZ
3589
3590 // when we call Update() we really want to update the window immediately on
90e572f1 3591 // screen, even if it means flushing the entire queue and hence slowing down
1b965a9c
VZ
3592 // everything -- but it should still be done, it's just that Update() should
3593 // be called very rarely
3594 gdk_flush();
010afced
RR
3595}
3596
3597void wxWindowGTK::GtkUpdate()
beab25bd 3598{
4e5a4c69
RR
3599 if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window)
3600 gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE );
6bdeda47
AB
3601 if (m_widget && m_widget->window)
3602 gdk_window_process_updates( m_widget->window, FALSE );
a67f1484
VZ
3603
3604 // for consistency with other platforms (and also because it's convenient
3605 // to be able to update an entire TLW by calling Update() only once), we
3606 // should also update all our children here
3607 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
3608 node;
3609 node = node->GetNext() )
3610 {
3611 node->GetData()->GtkUpdate();
3612 }
beab25bd
RR
3613}
3614
657b4fd4 3615bool wxWindowGTK::DoIsExposed( int x, int y ) const
847dfdb4
RR
3616{
3617 return m_updateRegion.Contains(x, y) != wxOutRegion;
3618}
3619
3620
657b4fd4 3621bool wxWindowGTK::DoIsExposed( int x, int y, int w, int h ) const
847dfdb4
RR
3622{
3623 if (GetLayoutDirection() == wxLayout_RightToLeft)
3624 return m_updateRegion.Contains(x-w, y, w, h) != wxOutRegion;
3625 else
3626 return m_updateRegion.Contains(x, y, w, h) != wxOutRegion;
3627}
3628
beab25bd
RR
3629void wxWindowGTK::GtkSendPaintEvents()
3630{
3bcc8d15
RR
3631 if (!m_wxwindow)
3632 {
3bcc8d15
RR
3633 m_updateRegion.Clear();
3634 return;
3635 }
beab25bd 3636
f90566f5 3637 // Clip to paint region in wxClientDC
0a164d4c 3638 m_clipPaintRegion = true;
fab591c5 3639
bcb614b3
RR
3640 m_nativeUpdateRegion = m_updateRegion;
3641
847dfdb4
RR
3642 if (GetLayoutDirection() == wxLayout_RightToLeft)
3643 {
bcb614b3
RR
3644 // Transform m_updateRegion under RTL
3645 m_updateRegion.Clear();
847dfdb4
RR
3646
3647 gint width;
3648 gdk_window_get_geometry( GTK_PIZZA(m_wxwindow)->bin_window,
3649 NULL, NULL, &width, NULL, NULL );
3650
bcb614b3 3651 wxRegionIterator upd( m_nativeUpdateRegion );
847dfdb4
RR
3652 while (upd)
3653 {
3654 wxRect rect;
3655 rect.x = upd.GetX();
3656 rect.y = upd.GetY();
3657 rect.width = upd.GetWidth();
3658 rect.height = upd.GetHeight();
3659
3660 rect.x = width - rect.x - rect.width;
bcb614b3 3661 m_updateRegion.Union( rect );
847dfdb4
RR
3662
3663 ++upd;
3664 }
3665 }
847dfdb4 3666
b15ed747
RR
3667 // widget to draw on
3668 GtkPizza *pizza = GTK_PIZZA (m_wxwindow);
2b5f62a0 3669
aac97549 3670 if (GetThemeEnabled() && (GetBackgroundStyle() == wxBG_STYLE_SYSTEM))
f90566f5
RR
3671 {
3672 // find ancestor from which to steal background
cd5e74ba 3673 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
f90566f5 3674 if (!parent)
cc06fe74 3675 parent = (wxWindow*)this;
2b5f62a0 3676
822cf31c 3677 if (GTK_WIDGET_MAPPED(parent->m_widget))
f90566f5 3678 {
bcb614b3 3679 wxRegionIterator upd( m_nativeUpdateRegion );
822cf31c
KH
3680 while (upd)
3681 {
3682 GdkRectangle rect;
3683 rect.x = upd.GetX();
3684 rect.y = upd.GetY();
3685 rect.width = upd.GetWidth();
3686 rect.height = upd.GetHeight();
3687
3688 gtk_paint_flat_box( parent->m_widget->style,
3689 pizza->bin_window,
3690 (GtkStateType)GTK_WIDGET_STATE(m_wxwindow),
3691 GTK_SHADOW_NONE,
3692 &rect,
3693 parent->m_widget,
3694 (char *)"base",
3695 0, 0, -1, -1 );
3696
60d8e886 3697 ++upd;
822cf31c 3698 }
f90566f5
RR
3699 }
3700 }
3701 else
b15ed747
RR
3702 {
3703 wxWindowDC dc( (wxWindow*)this );
3704 dc.SetClippingRegion( m_updateRegion );
3705
3706 wxEraseEvent erase_event( GetId(), &dc );
3707 erase_event.SetEventObject( this );
3708
3709 GetEventHandler()->ProcessEvent(erase_event);
3710 }
beab25bd
RR
3711
3712 wxNcPaintEvent nc_paint_event( GetId() );
3713 nc_paint_event.SetEventObject( this );
3714 GetEventHandler()->ProcessEvent( nc_paint_event );
3715
3716 wxPaintEvent paint_event( GetId() );
3717 paint_event.SetEventObject( this );
3718 GetEventHandler()->ProcessEvent( paint_event );
3719
0a164d4c 3720 m_clipPaintRegion = false;
c89f5c02 3721
c89f5c02 3722 m_updateRegion.Clear();
bcb614b3 3723 m_nativeUpdateRegion.Clear();
beab25bd
RR
3724}
3725
8e1a5bf9
VZ
3726void wxWindowGTK::SetDoubleBuffered( bool on )
3727{
3728 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3729
3730 if ( m_wxwindow )
3731 gtk_widget_set_double_buffered( m_wxwindow, on );
3732}
3733
2e992e06
VZ
3734bool wxWindowGTK::IsDoubleBuffered() const
3735{
3736 return GTK_WIDGET_DOUBLE_BUFFERED( m_wxwindow );
3737}
3738
596f1d11 3739void wxWindowGTK::ClearBackground()
c801d85f 3740{
82b978d7 3741 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
362c6693 3742}
c801d85f 3743
ff8bfdbb 3744#if wxUSE_TOOLTIPS
1e6feb95 3745void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
b1170810 3746{
f03fc89f 3747 wxWindowBase::DoSetToolTip(tip);
ff8bfdbb 3748
f03fc89f 3749 if (m_tooltip)
3379ed37 3750 m_tooltip->Apply( (wxWindow *)this );
b1170810
RR
3751}
3752
1e6feb95 3753void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
b1170810 3754{
aa154cb1
RR
3755 wxString tmp( tip );
3756 gtk_tooltips_set_tip( tips, GetConnectWidget(), wxGTK_CONV(tmp), (gchar*) NULL );
301cd871 3757}
ff8bfdbb 3758#endif // wxUSE_TOOLTIPS
b1170810 3759
1e6feb95 3760bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
c801d85f 3761{
0a164d4c 3762 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
8bbe427f 3763
739730ca 3764 if (!wxWindowBase::SetBackgroundColour(colour))
44dfb5ce 3765 return false;
c50f1fb9 3766
5edef14e 3767 if (colour.Ok())
994bc575 3768 {
5edef14e
VS
3769 // We need the pixel value e.g. for background clearing.
3770 m_backgroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
994bc575 3771 }
ca298c88 3772
5edef14e 3773 // apply style change (forceStyle=true so that new style is applied
c7382f91
JS
3774 // even if the bg colour changed from valid to wxNullColour)
3775 if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM)
3776 ApplyWidgetStyle(true);
ea323db3 3777
5edef14e 3778 return true;
6de97a3b
RR
3779}
3780
1e6feb95 3781bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
6de97a3b 3782{
0a164d4c 3783 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
8bbe427f 3784
739730ca
RR
3785 if (!wxWindowBase::SetForegroundColour(colour))
3786 {
5edef14e 3787 return false;
739730ca 3788 }
0a164d4c 3789
5edef14e 3790 if (colour.Ok())
ea323db3 3791 {
5edef14e
VS
3792 // We need the pixel value e.g. for background clearing.
3793 m_foregroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
ea323db3 3794 }
f03fc89f 3795
5edef14e
VS
3796 // apply style change (forceStyle=true so that new style is applied
3797 // even if the bg colour changed from valid to wxNullColour):
3798 ApplyWidgetStyle(true);
3799
44dfb5ce 3800 return true;
58614078
RR
3801}
3802
2b5f62a0
VZ
3803PangoContext *wxWindowGTK::GtkGetPangoDefaultContext()
3804{
3805 return gtk_widget_get_pango_context( m_widget );
3806}
0a164d4c 3807
5edef14e 3808GtkRcStyle *wxWindowGTK::CreateWidgetStyle(bool forceStyle)
58614078 3809{
f40fdaa3 3810 // do we need to apply any changes at all?
5edef14e 3811 if ( !forceStyle &&
984e8d0b
VS
3812 !m_font.Ok() &&
3813 !m_foregroundColour.Ok() && !m_backgroundColour.Ok() )
fb65642c 3814 {
f40fdaa3 3815 return NULL;
fb65642c
RR
3816 }
3817
f40fdaa3 3818 GtkRcStyle *style = gtk_rc_style_new();
1ecc4d80 3819
984e8d0b 3820 if ( m_font.Ok() )
db434467 3821 {
0a164d4c 3822 style->font_desc =
f40fdaa3 3823 pango_font_description_copy( m_font.GetNativeFontInfo()->description );
288059b2 3824 }
1ecc4d80 3825
fe161a26 3826 if ( m_foregroundColour.Ok() )
1ecc4d80 3827 {
c6685317 3828 const GdkColor *fg = m_foregroundColour.GetColor();
0a164d4c 3829
5edef14e 3830 style->fg[GTK_STATE_NORMAL] = *fg;
f40fdaa3 3831 style->color_flags[GTK_STATE_NORMAL] = GTK_RC_FG;
0a164d4c 3832
5edef14e 3833 style->fg[GTK_STATE_PRELIGHT] = *fg;
f40fdaa3 3834 style->color_flags[GTK_STATE_PRELIGHT] = GTK_RC_FG;
0a164d4c 3835
5edef14e 3836 style->fg[GTK_STATE_ACTIVE] = *fg;
f40fdaa3 3837 style->color_flags[GTK_STATE_ACTIVE] = GTK_RC_FG;
1ecc4d80
RR
3838 }
3839
fe161a26 3840 if ( m_backgroundColour.Ok() )
1ecc4d80 3841 {
c6685317 3842 const GdkColor *bg = m_backgroundColour.GetColor();
5edef14e
VS
3843
3844 style->bg[GTK_STATE_NORMAL] = *bg;
3845 style->base[GTK_STATE_NORMAL] = *bg;
f40fdaa3
VS
3846 style->color_flags[GTK_STATE_NORMAL] = (GtkRcFlags)
3847 (style->color_flags[GTK_STATE_NORMAL] | GTK_RC_BG | GTK_RC_BASE);
0a164d4c 3848
5edef14e
VS
3849 style->bg[GTK_STATE_PRELIGHT] = *bg;
3850 style->base[GTK_STATE_PRELIGHT] = *bg;
f40fdaa3
VS
3851 style->color_flags[GTK_STATE_PRELIGHT] = (GtkRcFlags)
3852 (style->color_flags[GTK_STATE_PRELIGHT] | GTK_RC_BG | GTK_RC_BASE);
0a164d4c 3853
5edef14e
VS
3854 style->bg[GTK_STATE_ACTIVE] = *bg;
3855 style->base[GTK_STATE_ACTIVE] = *bg;
f40fdaa3
VS
3856 style->color_flags[GTK_STATE_ACTIVE] = (GtkRcFlags)
3857 (style->color_flags[GTK_STATE_ACTIVE] | GTK_RC_BG | GTK_RC_BASE);
0a164d4c 3858
5edef14e
VS
3859 style->bg[GTK_STATE_INSENSITIVE] = *bg;
3860 style->base[GTK_STATE_INSENSITIVE] = *bg;
f40fdaa3
VS
3861 style->color_flags[GTK_STATE_INSENSITIVE] = (GtkRcFlags)
3862 (style->color_flags[GTK_STATE_INSENSITIVE] | GTK_RC_BG | GTK_RC_BASE);
1ecc4d80 3863 }
0a164d4c 3864
f40fdaa3 3865 return style;
a81258be
RR
3866}
3867
f8e045e2 3868void wxWindowGTK::ApplyWidgetStyle(bool forceStyle)
a81258be 3869{
f8e045e2
RD
3870 GtkRcStyle *style = CreateWidgetStyle(forceStyle);
3871 if ( style )
3872 {
7074ce35 3873 DoApplyWidgetStyle(style);
f8e045e2
RD
3874 gtk_rc_style_unref(style);
3875 }
6dd18972
VS
3876
3877 // Style change may affect GTK+'s size calculation:
3878 InvalidateBestSize();
6de97a3b
RR
3879}
3880
7074ce35
VS
3881void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
3882{
3883 if (m_wxwindow)
7074ce35 3884 gtk_widget_modify_style(m_wxwindow, style);
7cb93e45
RR
3885 else
3886 gtk_widget_modify_style(m_widget, style);
7074ce35
VS
3887}
3888
c7382f91
JS
3889bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
3890{
3891 wxWindowBase::SetBackgroundStyle(style);
0a164d4c 3892
c7382f91
JS
3893 if (style == wxBG_STYLE_CUSTOM)
3894 {
3895 GdkWindow *window = (GdkWindow*) NULL;
3896 if (m_wxwindow)
3897 window = GTK_PIZZA(m_wxwindow)->bin_window;
3898 else
3899 window = GetConnectWidget()->window;
3900
3901 if (window)
3902 {
3903 // Make sure GDK/X11 doesn't refresh the window
3904 // automatically.
3905 gdk_window_set_back_pixmap( window, None, False );
3906#ifdef __X__
3907 Display* display = GDK_WINDOW_DISPLAY(window);
3908 XFlush(display);
3909#endif
3910 m_needsStyleChange = false;
3911 }
3912 else
3913 // Do in OnIdle, because the window is not yet available
3914 m_needsStyleChange = true;
0a164d4c 3915
c7382f91
JS
3916 // Don't apply widget style, or we get a grey background
3917 }
3918 else
3919 {
3920 // apply style change (forceStyle=true so that new style is applied
3921 // even if the bg colour changed from valid to wxNullColour):
3922 ApplyWidgetStyle(true);
3923 }
3924 return true;
3925}
7074ce35 3926
06cfab17 3927#if wxUSE_DRAG_AND_DROP
ac57418f 3928
1e6feb95 3929void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
c801d85f 3930{
82b978d7
RD
3931 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3932
1ecc4d80 3933 GtkWidget *dnd_widget = GetConnectWidget();
47d67540 3934
1ecc4d80 3935 if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
47d67540 3936
1ecc4d80
RR
3937 if (m_dropTarget) delete m_dropTarget;
3938 m_dropTarget = dropTarget;
47d67540 3939
1ecc4d80 3940 if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
362c6693 3941}
c801d85f 3942
f03fc89f 3943#endif // wxUSE_DRAG_AND_DROP
ac57418f 3944
1e6feb95 3945GtkWidget* wxWindowGTK::GetConnectWidget()
e3e65dac 3946{
1ecc4d80
RR
3947 GtkWidget *connect_widget = m_widget;
3948 if (m_wxwindow) connect_widget = m_wxwindow;
47d67540 3949
1ecc4d80 3950 return connect_widget;
e3e65dac 3951}
47d67540 3952
ef5c70f9 3953bool wxWindowGTK::GTKIsOwnWindow(GdkWindow *window) const
903f689b 3954{
ef5c70f9
VZ
3955 wxArrayGdkWindows windowsThis;
3956 GdkWindow * const winThis = GTKGetWindow(windowsThis);
148cd9b6 3957
ef5c70f9
VZ
3958 return winThis ? window == winThis
3959 : windowsThis.Index(window) != wxNOT_FOUND;
3960}
3961
3962GdkWindow *wxWindowGTK::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
3963{
3964 return m_wxwindow ? GTK_PIZZA(m_wxwindow)->bin_window : m_widget->window;
903f689b
RR
3965}
3966
1e6feb95 3967bool wxWindowGTK::SetFont( const wxFont &font )
c801d85f 3968{
0a164d4c 3969 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
9c288e4d 3970
5edef14e
VS
3971 if (!wxWindowBase::SetFont(font))
3972 return false;
c801d85f 3973
5edef14e
VS
3974 // apply style change (forceStyle=true so that new style is applied
3975 // even if the font changed from valid to wxNullFont):
0a164d4c 3976 ApplyWidgetStyle(true);
5edef14e
VS
3977
3978 return true;
362c6693 3979}
c801d85f 3980
94633ad9 3981void wxWindowGTK::DoCaptureMouse()
c801d85f 3982{
82b978d7
RD
3983 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3984
ed673c6a 3985 GdkWindow *window = (GdkWindow*) NULL;
b231914f
VZ
3986 if (m_wxwindow)
3987 window = GTK_PIZZA(m_wxwindow)->bin_window;
ed673c6a 3988 else
b231914f 3989 window = GetConnectWidget()->window;
148cd9b6 3990
e4606ed9 3991 wxCHECK_RET( window, _T("CaptureMouse() failed") );
c50f1fb9 3992
f516d986 3993 const wxCursor* cursor = &m_cursor;
cca602ac
JS
3994 if (!cursor->Ok())
3995 cursor = wxSTANDARD_CURSOR;
3996
ed673c6a 3997 gdk_pointer_grab( window, FALSE,
1ecc4d80
RR
3998 (GdkEventMask)
3999 (GDK_BUTTON_PRESS_MASK |
4000 GDK_BUTTON_RELEASE_MASK |
148cd9b6 4001 GDK_POINTER_MOTION_HINT_MASK |
1ecc4d80 4002 GDK_POINTER_MOTION_MASK),
ff8bfdbb 4003 (GdkWindow *) NULL,
cca602ac 4004 cursor->GetCursor(),
b02da6b1 4005 (guint32)GDK_CURRENT_TIME );
b231914f 4006 g_captureWindow = this;
0a164d4c 4007 g_captureWindowHasMouse = true;
362c6693 4008}
c801d85f 4009
94633ad9 4010void wxWindowGTK::DoReleaseMouse()
c801d85f 4011{
82b978d7
RD
4012 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4013
e4606ed9 4014 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
47d67540 4015
c43430bb
VS
4016 g_captureWindow = (wxWindowGTK*) NULL;
4017
ed673c6a 4018 GdkWindow *window = (GdkWindow*) NULL;
b231914f
VZ
4019 if (m_wxwindow)
4020 window = GTK_PIZZA(m_wxwindow)->bin_window;
ed673c6a 4021 else
b231914f 4022 window = GetConnectWidget()->window;
148cd9b6 4023
b02da6b1
VZ
4024 if (!window)
4025 return;
c50f1fb9 4026
b02da6b1 4027 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
362c6693 4028}
c801d85f 4029
1e6feb95
VZ
4030/* static */
4031wxWindow *wxWindowBase::GetCapture()
4032{
4033 return (wxWindow *)g_captureWindow;
4034}
4035
4036bool wxWindowGTK::IsRetained() const
c801d85f 4037{
0a164d4c 4038 return false;
362c6693 4039}
c801d85f 4040
add7cadd
PC
4041void wxWindowGTK::BlockScrollEvent()
4042{
4043 wxASSERT(!m_blockScrollEvent);
4044 m_blockScrollEvent = true;
4045}
4046
4047void wxWindowGTK::UnblockScrollEvent()
4048{
4049 wxASSERT(m_blockScrollEvent);
4050 m_blockScrollEvent = false;
4051}
4052
22c9b211
VZ
4053void wxWindowGTK::SetScrollbar(int orient,
4054 int pos,
4055 int thumbVisible,
4056 int range,
4057 bool WXUNUSED(update))
c801d85f 4058{
82b978d7 4059 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
223d09f6 4060 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
c801d85f 4061
de7bb802
PC
4062 if (range > 0)
4063 {
4064 m_hasScrolling = true;
4065 }
4066 else
4067 {
4068 // GtkRange requires upper > lower
4069 range =
4070 thumbVisible = 1;
4071 }
47d67540 4072
8ea30e36
PC
4073 if (pos > range - thumbVisible)
4074 pos = range - thumbVisible;
4075 if (pos < 0)
4076 pos = 0;
22c9b211 4077 GtkAdjustment* adj = m_scrollBar[ScrollDirFromOrient(orient)]->adjustment;
add7cadd
PC
4078 adj->step_increment = 1;
4079 adj->page_increment =
4080 adj->page_size = thumbVisible;
8ea30e36
PC
4081 adj->upper = range;
4082 SetScrollPos(orient, pos);
4083 gtk_adjustment_changed(adj);
87a3ebe9
VZ
4084}
4085
22c9b211 4086void wxWindowGTK::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh))
c801d85f 4087{
82b978d7 4088 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
223d09f6 4089 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
1ecc4d80 4090
add7cadd
PC
4091 // This check is more than an optimization. Without it, the slider
4092 // will not move smoothly while tracking when using wxScrollHelper.
4093 if (GetScrollPos(orient) != pos)
47d67540 4094 {
22c9b211
VZ
4095 const int dir = ScrollDirFromOrient(orient);
4096 GtkAdjustment* adj = m_scrollBar[dir]->adjustment;
8ea30e36
PC
4097 const int max = int(adj->upper - adj->page_size);
4098 if (pos > max)
4099 pos = max;
4100 if (pos < 0)
4101 pos = 0;
807a572e
RR
4102 m_scrollPos[dir] = adj->value = pos;
4103
8ea30e36 4104 // If a "value_changed" signal emission is not already in progress
22c9b211 4105 if (!m_blockValueChanged[dir])
8ea30e36
PC
4106 {
4107 gtk_adjustment_value_changed(adj);
4108 }
cb43b372 4109 }
362c6693 4110}
c801d85f 4111
22c9b211 4112int wxWindowGTK::GetScrollThumb(int orient) const
c801d85f 4113{
82b978d7 4114 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
223d09f6 4115 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
47d67540 4116
22c9b211 4117 return int(m_scrollBar[ScrollDirFromOrient(orient)]->adjustment->page_size);
362c6693 4118}
c801d85f 4119
1e6feb95 4120int wxWindowGTK::GetScrollPos( int orient ) const
c801d85f 4121{
82b978d7 4122 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
223d09f6 4123 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
c801d85f 4124
22c9b211 4125 return int(m_scrollBar[ScrollDirFromOrient(orient)]->adjustment->value + 0.5);
362c6693 4126}
c801d85f 4127
1e6feb95 4128int wxWindowGTK::GetScrollRange( int orient ) const
c801d85f 4129{
82b978d7 4130 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
223d09f6 4131 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
c801d85f 4132
22c9b211 4133 return int(m_scrollBar[ScrollDirFromOrient(orient)]->adjustment->upper);
add7cadd
PC
4134}
4135
4136// Determine if increment is the same as +/-x, allowing for some small
4137// difference due to possible inexactness in floating point arithmetic
4138static inline bool IsScrollIncrement(double increment, double x)
4139{
4140 wxASSERT(increment > 0);
4141 const double tolerance = 1.0 / 1024;
4142 return fabs(increment - fabs(x)) < tolerance;
4143}
4144
38009079 4145wxEventType wxWindowGTK::GetScrollEventType(GtkRange* range)
add7cadd
PC
4146{
4147 DEBUG_MAIN_THREAD
4148
4149 if (g_isIdle)
4150 wxapp_install_idle_handler();
4151
4152 wxASSERT(range == m_scrollBar[0] || range == m_scrollBar[1]);
4153
4154 const int barIndex = range == m_scrollBar[1];
4155 GtkAdjustment* adj = range->adjustment;
807a572e 4156
add7cadd 4157 const int value = int(adj->value + 0.5);
807a572e 4158
add7cadd
PC
4159 // save previous position
4160 const double oldPos = m_scrollPos[barIndex];
4161 // update current position
4162 m_scrollPos[barIndex] = adj->value;
4163 // If event should be ignored, or integral position has not changed
8ea30e36 4164 if (!m_hasVMT || g_blockEventsOnDrag || value == int(oldPos + 0.5))
add7cadd
PC
4165 {
4166 return wxEVT_NULL;
4167 }
4168
4169 wxEventType eventType = wxEVT_SCROLL_THUMBTRACK;
4170 if (!m_isScrolling)
4171 {
4172 // Difference from last change event
4173 const double diff = adj->value - oldPos;
4174 const bool isDown = diff > 0;
4175
4176 if (IsScrollIncrement(adj->step_increment, diff))
4177 {
4178 eventType = isDown ? wxEVT_SCROLL_LINEDOWN : wxEVT_SCROLL_LINEUP;
4179 }
4180 else if (IsScrollIncrement(adj->page_increment, diff))
4181 {
4182 eventType = isDown ? wxEVT_SCROLL_PAGEDOWN : wxEVT_SCROLL_PAGEUP;
4183 }
4184 else if (m_mouseButtonDown)
4185 {
4186 // Assume track event
4187 m_isScrolling = true;
4188 }
4189 }
4190 return eventType;
362c6693 4191}
c801d85f 4192
1e6feb95 4193void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
c801d85f 4194{
82b978d7
RD
4195 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4196
223d09f6 4197 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
2b5f62a0 4198
f47ae6e7 4199 // No scrolling requested.
8e217128 4200 if ((dx == 0) && (dy == 0)) return;
0fc5dbf5 4201
0a164d4c 4202 m_clipPaintRegion = true;
0fc5dbf5 4203
807a572e
RR
4204 if (GetLayoutDirection() == wxLayout_RightToLeft)
4205 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), dx, -dy );
4206 else
4207 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy );
0fc5dbf5 4208
0a164d4c 4209 m_clipPaintRegion = false;
c801d85f 4210}
3723b7b1 4211
6493aaca
VZ
4212void wxWindowGTK::GtkScrolledWindowSetBorder(GtkWidget* w, int wxstyle)
4213{
4214 //RN: Note that static controls usually have no border on gtk, so maybe
88a7a4e1 4215 //it makes sense to treat that as simply no border at the wx level
6493aaca
VZ
4216 //as well...
4217 if (!(wxstyle & wxNO_BORDER) && !(wxstyle & wxBORDER_STATIC))
4218 {
4219 GtkShadowType gtkstyle;
88a7a4e1 4220
6493aaca
VZ
4221 if(wxstyle & wxBORDER_RAISED)
4222 gtkstyle = GTK_SHADOW_OUT;
4223 else if (wxstyle & wxBORDER_SUNKEN)
4224 gtkstyle = GTK_SHADOW_IN;
4225 else if (wxstyle & wxBORDER_DOUBLE)
4226 gtkstyle = GTK_SHADOW_ETCHED_IN;
4227 else //default
4228 gtkstyle = GTK_SHADOW_IN;
4229
88a7a4e1 4230 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w),
6493aaca
VZ
4231 gtkstyle );
4232 }
4233}
4234
015dca24
MR
4235void wxWindowGTK::SetWindowStyleFlag( long style )
4236{
4237 // Updates the internal variable. NB: Now m_windowStyle bits carry the _new_ style values already
4238 wxWindowBase::SetWindowStyleFlag(style);
4239}
4e5a4c69 4240
3723b7b1
JS
4241// Find the wxWindow at the current mouse position, also returning the mouse
4242// position.
4243wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4244{
59a12e90
JS
4245 pt = wxGetMousePosition();
4246 wxWindow* found = wxFindWindowAtPoint(pt);
4247 return found;
3723b7b1
JS
4248}
4249
4250// Get the current mouse position.
4251wxPoint wxGetMousePosition()
4252{
59a12e90
JS
4253 /* This crashes when used within wxHelpContext,
4254 so we have to use the X-specific implementation below.
4255 gint x, y;
4256 GdkModifierType *mask;
4257 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4258
4259 return wxPoint(x, y);
4260 */
4261
3723b7b1
JS
4262 int x, y;
4263 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
59a12e90 4264
37d81cc2 4265 Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY();
59a12e90
JS
4266 Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
4267 Window rootReturn, childReturn;
4268 int rootX, rootY, winX, winY;
4269 unsigned int maskReturn;
4270
4271 XQueryPointer (display,
5cd09f0b
RR
4272 rootWindow,
4273 &rootReturn,
59a12e90
JS
4274 &childReturn,
4275 &rootX, &rootY, &winX, &winY, &maskReturn);
4276 return wxPoint(rootX, rootY);
4277
3723b7b1
JS
4278}
4279
224016a8
JS
4280// Needed for implementing e.g. combobox on wxGTK within a modal dialog.
4281void wxAddGrab(wxWindow* window)
4282{
4283 gtk_grab_add( (GtkWidget*) window->GetHandle() );
4284}
4285
4286void wxRemoveGrab(wxWindow* window)
4287{
4288 gtk_grab_remove( (GtkWidget*) window->GetHandle() );
4289}