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