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