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