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