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