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