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