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