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