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