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