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