Better place for coordinate mirroring and removal
[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)
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 GtkPizza *pizza = GTK_PIZZA(m_wxwindow);
2322
2323 if (HasFlag(wxRAISED_BORDER))
2324 {
2325 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_OUT );
2326 }
2327 else if (HasFlag(wxSUNKEN_BORDER))
2328 {
2329 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_IN );
2330 }
2331 else if (HasFlag(wxSIMPLE_BORDER))
2332 {
2333 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_THIN );
2334 }
2335 else
2336 {
2337 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_NONE );
2338 }
2339 #endif // __WXUNIVERSAL__
2340
2341 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
2342
2343 GTK_WIDGET_SET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
2344 m_acceptsFocus = true;
2345
2346 // connect various scroll-related events
2347 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2348 {
2349 // these handlers block mouse events to any window during scrolling
2350 // such as motion events and prevent GTK and wxWidgets from fighting
2351 // over where the slider should be
2352 g_signal_connect(m_scrollBar[dir], "button_press_event",
2353 G_CALLBACK(gtk_scrollbar_button_press_event), this);
2354 g_signal_connect(m_scrollBar[dir], "button_release_event",
2355 G_CALLBACK(gtk_scrollbar_button_release_event), this);
2356
2357 gulong handler_id = g_signal_connect(m_scrollBar[dir], "event_after",
2358 G_CALLBACK(gtk_scrollbar_event_after), this);
2359 g_signal_handler_block(m_scrollBar[dir], handler_id);
2360
2361 // these handlers get notified when scrollbar slider moves
2362 g_signal_connect(m_scrollBar[dir], "value_changed",
2363 G_CALLBACK(gtk_scrollbar_value_changed), this);
2364 }
2365
2366 gtk_widget_show( m_wxwindow );
2367
2368 if (m_parent)
2369 m_parent->DoAddChild( this );
2370
2371 m_focusWidget = m_wxwindow;
2372
2373 PostCreation();
2374
2375 return true;
2376 }
2377
2378 wxWindowGTK::~wxWindowGTK()
2379 {
2380 SendDestroyEvent();
2381
2382 if (g_focusWindow == this)
2383 g_focusWindow = NULL;
2384
2385 if ( g_delayedFocus == this )
2386 g_delayedFocus = NULL;
2387
2388 m_isBeingDeleted = true;
2389 m_hasVMT = false;
2390
2391 // destroy children before destroying this window itself
2392 DestroyChildren();
2393
2394 // unhook focus handlers to prevent stray events being
2395 // propagated to this (soon to be) dead object
2396 if (m_focusWidget != NULL)
2397 {
2398 g_signal_handlers_disconnect_by_func (m_focusWidget,
2399 (gpointer) gtk_window_focus_in_callback,
2400 this);
2401 g_signal_handlers_disconnect_by_func (m_focusWidget,
2402 (gpointer) gtk_window_focus_out_callback,
2403 this);
2404 }
2405
2406 if (m_widget)
2407 Show( false );
2408
2409 // delete before the widgets to avoid a crash on solaris
2410 delete m_imData;
2411
2412 if (m_wxwindow)
2413 {
2414 gtk_widget_destroy( m_wxwindow );
2415 m_wxwindow = (GtkWidget*) NULL;
2416 }
2417
2418 if (m_widget)
2419 {
2420 gtk_widget_destroy( m_widget );
2421 m_widget = (GtkWidget*) NULL;
2422 }
2423 }
2424
2425 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
2426 {
2427 wxCHECK_MSG( !m_needParent || parent, false, wxT("Need complete parent.") );
2428
2429 // Use either the given size, or the default if -1 is given.
2430 // See wxWindowBase for these functions.
2431 m_width = WidthDefault(size.x) ;
2432 m_height = HeightDefault(size.y);
2433
2434 m_x = (int)pos.x;
2435 m_y = (int)pos.y;
2436
2437 return true;
2438 }
2439
2440 void wxWindowGTK::PostCreation()
2441 {
2442 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2443
2444 if (m_wxwindow)
2445 {
2446 if (!m_noExpose)
2447 {
2448 // these get reported to wxWidgets -> wxPaintEvent
2449
2450 g_signal_connect (m_wxwindow, "expose_event",
2451 G_CALLBACK (gtk_window_expose_callback), this);
2452
2453 if (GetLayoutDirection() == wxLayout_LeftToRight)
2454 gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), HasFlag( wxFULL_REPAINT_ON_RESIZE ) );
2455 }
2456
2457 // Create input method handler
2458 m_imData = new wxGtkIMData;
2459
2460 // Cannot handle drawing preedited text yet
2461 gtk_im_context_set_use_preedit( m_imData->context, FALSE );
2462
2463 g_signal_connect (m_imData->context, "commit",
2464 G_CALLBACK (gtk_wxwindow_commit_cb), this);
2465
2466 // these are called when the "sunken" or "raised" borders are drawn
2467 g_signal_connect (m_widget, "expose_event",
2468 G_CALLBACK (gtk_window_own_expose_callback), this);
2469 }
2470
2471 // focus handling
2472
2473 if (!GTK_IS_WINDOW(m_widget))
2474 {
2475 if (m_focusWidget == NULL)
2476 m_focusWidget = m_widget;
2477
2478 if (m_wxwindow)
2479 {
2480 g_signal_connect (m_focusWidget, "focus_in_event",
2481 G_CALLBACK (gtk_window_focus_in_callback), this);
2482 g_signal_connect (m_focusWidget, "focus_out_event",
2483 G_CALLBACK (gtk_window_focus_out_callback), this);
2484 }
2485 else
2486 {
2487 g_signal_connect_after (m_focusWidget, "focus_in_event",
2488 G_CALLBACK (gtk_window_focus_in_callback), this);
2489 g_signal_connect_after (m_focusWidget, "focus_out_event",
2490 G_CALLBACK (gtk_window_focus_out_callback), this);
2491 }
2492 }
2493
2494 // connect to the various key and mouse handlers
2495
2496 GtkWidget *connect_widget = GetConnectWidget();
2497
2498 ConnectWidget( connect_widget );
2499
2500 /* We cannot set colours, fonts and cursors before the widget has
2501 been realized, so we do this directly after realization */
2502 g_signal_connect (connect_widget, "realize",
2503 G_CALLBACK (gtk_window_realized_callback), this);
2504
2505 if (m_wxwindow)
2506 {
2507 // Catch native resize events
2508 g_signal_connect (m_wxwindow, "size_allocate",
2509 G_CALLBACK (gtk_window_size_callback), this);
2510 }
2511
2512 if (GTK_IS_COMBO(m_widget))
2513 {
2514 GtkCombo *gcombo = GTK_COMBO(m_widget);
2515
2516 g_signal_connect (gcombo->entry, "size_request",
2517 G_CALLBACK (wxgtk_combo_size_request_callback),
2518 this);
2519 }
2520 #ifdef GTK_IS_FILE_CHOOSER_BUTTON
2521 else if (!gtk_check_version(2,6,0) && GTK_IS_FILE_CHOOSER_BUTTON(m_widget))
2522 {
2523 // If we connect to the "size_request" signal of a GtkFileChooserButton
2524 // then that control won't be sized properly when placed inside sizers
2525 // (this can be tested removing this elseif and running XRC or WIDGETS samples)
2526 // FIXME: what should be done here ?
2527 }
2528 #endif
2529 else
2530 {
2531 // This is needed if we want to add our windows into native
2532 // GTK controls, such as the toolbar. With this callback, the
2533 // toolbar gets to know the correct size (the one set by the
2534 // programmer). Sadly, it misbehaves for wxComboBox.
2535 g_signal_connect (m_widget, "size_request",
2536 G_CALLBACK (wxgtk_window_size_request_callback),
2537 this);
2538 }
2539
2540 InheritAttributes();
2541
2542 m_hasVMT = true;
2543
2544 SetLayoutDirection(wxLayout_Default);
2545
2546 // unless the window was created initially hidden (i.e. Hide() had been
2547 // called before Create()), we should show it at GTK+ level as well
2548 if ( IsShown() )
2549 gtk_widget_show( m_widget );
2550 }
2551
2552 void wxWindowGTK::ConnectWidget( GtkWidget *widget )
2553 {
2554 g_signal_connect (widget, "key_press_event",
2555 G_CALLBACK (gtk_window_key_press_callback), this);
2556 g_signal_connect (widget, "key_release_event",
2557 G_CALLBACK (gtk_window_key_release_callback), this);
2558 g_signal_connect (widget, "button_press_event",
2559 G_CALLBACK (gtk_window_button_press_callback), this);
2560 g_signal_connect (widget, "button_release_event",
2561 G_CALLBACK (gtk_window_button_release_callback), this);
2562 g_signal_connect (widget, "motion_notify_event",
2563 G_CALLBACK (gtk_window_motion_notify_callback), this);
2564 g_signal_connect (widget, "scroll_event",
2565 G_CALLBACK (window_scroll_event), this);
2566 g_signal_connect (widget, "popup_menu",
2567 G_CALLBACK (wxgtk_window_popup_menu_callback), this);
2568 g_signal_connect (widget, "enter_notify_event",
2569 G_CALLBACK (gtk_window_enter_callback), this);
2570 g_signal_connect (widget, "leave_notify_event",
2571 G_CALLBACK (gtk_window_leave_callback), this);
2572 }
2573
2574 bool wxWindowGTK::Destroy()
2575 {
2576 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2577
2578 m_hasVMT = false;
2579
2580 return wxWindowBase::Destroy();
2581 }
2582
2583 void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
2584 {
2585 // inform the parent to perform the move
2586 gtk_pizza_set_size( GTK_PIZZA(m_parent->m_wxwindow), m_widget, x, y, width, height );
2587
2588 }
2589
2590 void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
2591 {
2592 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2593 wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") );
2594
2595 if (m_resizing) return; /* I don't like recursions */
2596 m_resizing = true;
2597
2598 int currentX, currentY;
2599 GetPosition(&currentX, &currentY);
2600 if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2601 x = currentX;
2602 if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2603 y = currentY;
2604 AdjustForParentClientOrigin(x, y, sizeFlags);
2605
2606 // calculate the best size if we should auto size the window
2607 if ( ((sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1) ||
2608 ((sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1) )
2609 {
2610 const wxSize sizeBest = GetBestSize();
2611 if ( (sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1 )
2612 width = sizeBest.x;
2613 if ( (sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1 )
2614 height = sizeBest.y;
2615 }
2616
2617 if (width != -1)
2618 m_width = width;
2619 if (height != -1)
2620 m_height = height;
2621
2622 int minWidth = GetMinWidth(),
2623 minHeight = GetMinHeight(),
2624 maxWidth = GetMaxWidth(),
2625 maxHeight = GetMaxHeight();
2626
2627 if ((minWidth != -1) && (m_width < minWidth )) m_width = minWidth;
2628 if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight;
2629 if ((maxWidth != -1) && (m_width > maxWidth )) m_width = maxWidth;
2630 if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
2631
2632 #if wxUSE_TOOLBAR_NATIVE
2633 if (wxDynamicCast(GetParent(), wxToolBar))
2634 {
2635 // don't take the x,y values, they're wrong because toolbar sets them
2636 GtkWidget *widget = GTK_WIDGET(m_widget);
2637 gtk_widget_set_size_request (widget, m_width, m_height);
2638 }
2639 else
2640 #endif
2641 if (m_parent->m_wxwindow == NULL) // i.e. wxNotebook
2642 {
2643 // don't set the size for children of wxNotebook, just take the values.
2644 m_x = x;
2645 m_y = y;
2646 m_width = width;
2647 m_height = height;
2648 }
2649 else
2650 {
2651 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
2652 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
2653 {
2654 if (x != -1) m_x = x + gtk_pizza_get_xoffset( pizza );
2655 if (y != -1) m_y = y + gtk_pizza_get_yoffset( pizza );
2656 }
2657 else
2658 {
2659 m_x = x + gtk_pizza_get_xoffset( pizza );
2660 m_y = y + gtk_pizza_get_yoffset( pizza );
2661 }
2662
2663 int left_border = 0;
2664 int right_border = 0;
2665 int top_border = 0;
2666 int bottom_border = 0;
2667
2668 /* the default button has a border around it */
2669 if (GTK_WIDGET_CAN_DEFAULT(m_widget))
2670 {
2671 GtkBorder *default_border = NULL;
2672 gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
2673 if (default_border)
2674 {
2675 left_border += default_border->left;
2676 right_border += default_border->right;
2677 top_border += default_border->top;
2678 bottom_border += default_border->bottom;
2679 g_free( default_border );
2680 }
2681 }
2682
2683 DoMoveWindow( m_x-top_border,
2684 m_y-left_border,
2685 m_width+left_border+right_border,
2686 m_height+top_border+bottom_border );
2687 }
2688
2689 if (m_hasScrolling)
2690 {
2691 /* Sometimes the client area changes size without the
2692 whole windows's size changing, but if the whole
2693 windows's size doesn't change, no wxSizeEvent will
2694 normally be sent. Here we add an extra test if
2695 the client test has been changed and this will
2696 be used then. */
2697 GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
2698 }
2699
2700 /*
2701 wxPrintf( "OnSize sent from " );
2702 if (GetClassInfo() && GetClassInfo()->GetClassName())
2703 wxPrintf( GetClassInfo()->GetClassName() );
2704 wxPrintf( " %d %d %d %d\n", (int)m_x, (int)m_y, (int)m_width, (int)m_height );
2705 */
2706
2707 if (!m_nativeSizeEvent)
2708 {
2709 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
2710 event.SetEventObject( this );
2711 GetEventHandler()->ProcessEvent( event );
2712 }
2713
2714 m_resizing = false;
2715 }
2716
2717 bool wxWindowGTK::GtkShowFromOnIdle()
2718 {
2719 if (IsShown() && m_showOnIdle && !GTK_WIDGET_VISIBLE (m_widget))
2720 {
2721 GtkAllocation alloc;
2722 alloc.x = m_x;
2723 alloc.y = m_y;
2724 alloc.width = m_width;
2725 alloc.height = m_height;
2726 gtk_widget_size_allocate( m_widget, &alloc );
2727 gtk_widget_show( m_widget );
2728 wxShowEvent eventShow(GetId(), true);
2729 eventShow.SetEventObject(this);
2730 GetEventHandler()->ProcessEvent(eventShow);
2731 m_showOnIdle = false;
2732 return true;
2733 }
2734
2735 return false;
2736 }
2737
2738 void wxWindowGTK::OnInternalIdle()
2739 {
2740 // Check if we have to show window now
2741 if (GtkShowFromOnIdle()) return;
2742
2743 if ( m_dirtyTabOrder )
2744 {
2745 m_dirtyTabOrder = false;
2746 RealizeTabOrder();
2747 }
2748
2749 // Update style if the window was not yet realized
2750 // and SetBackgroundStyle(wxBG_STYLE_CUSTOM) was called
2751 if (m_needsStyleChange)
2752 {
2753 SetBackgroundStyle(GetBackgroundStyle());
2754 m_needsStyleChange = false;
2755 }
2756
2757 wxCursor cursor = m_cursor;
2758 if (g_globalCursor.Ok()) cursor = g_globalCursor;
2759
2760 if (cursor.Ok())
2761 {
2762 /* I now set the cursor anew in every OnInternalIdle call
2763 as setting the cursor in a parent window also effects the
2764 windows above so that checking for the current cursor is
2765 not possible. */
2766
2767 if (m_wxwindow)
2768 {
2769 GdkWindow *window = GTK_PIZZA(m_wxwindow)->bin_window;
2770 if (window)
2771 gdk_window_set_cursor( window, cursor.GetCursor() );
2772
2773 if (!g_globalCursor.Ok())
2774 cursor = *wxSTANDARD_CURSOR;
2775
2776 window = m_widget->window;
2777 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
2778 gdk_window_set_cursor( window, cursor.GetCursor() );
2779
2780 }
2781 else if ( m_widget )
2782 {
2783 GdkWindow *window = m_widget->window;
2784 if ( window && !GTK_WIDGET_NO_WINDOW(m_widget) )
2785 gdk_window_set_cursor( window, cursor.GetCursor() );
2786 }
2787 }
2788
2789 if (wxUpdateUIEvent::CanUpdate(this))
2790 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
2791 }
2792
2793 void wxWindowGTK::DoGetSize( int *width, int *height ) const
2794 {
2795 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2796
2797 if (width) (*width) = m_width;
2798 if (height) (*height) = m_height;
2799 }
2800
2801 void wxWindowGTK::DoSetClientSize( int width, int height )
2802 {
2803 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2804
2805 if (m_wxwindow)
2806 {
2807 int dw = 0;
2808 int dh = 0;
2809
2810 if (m_hasScrolling)
2811 {
2812 GetScrollbarWidth(m_widget, dw, dh);
2813 }
2814
2815 #ifndef __WXUNIVERSAL__
2816 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
2817 {
2818 // shadow border size is 2
2819 dw += 2 * 2;
2820 dh += 2 * 2;
2821 }
2822 if (HasFlag(wxSIMPLE_BORDER))
2823 {
2824 // simple border size is 1
2825 dw += 1 * 2;
2826 dh += 1 * 2;
2827 }
2828 #endif // __WXUNIVERSAL__
2829
2830 width += dw;
2831 height += dh;
2832 }
2833
2834 SetSize(width, height);
2835 }
2836
2837 void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
2838 {
2839 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2840
2841 int w = m_width;
2842 int h = m_height;
2843
2844 if (m_wxwindow)
2845 {
2846 int dw = 0;
2847 int dh = 0;
2848
2849 if (m_hasScrolling)
2850 GetScrollbarWidth(m_widget, dw, dh);
2851
2852 #ifndef __WXUNIVERSAL__
2853 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
2854 {
2855 // shadow border size is 2
2856 dw += 2 * 2;
2857 dh += 2 * 2;
2858 }
2859 if (HasFlag(wxSIMPLE_BORDER))
2860 {
2861 // simple border size is 1
2862 dw += 1 * 2;
2863 dh += 1 * 2;
2864 }
2865 #endif // __WXUNIVERSAL__
2866
2867 w -= dw;
2868 h -= dh;
2869 if (w < 0)
2870 w = 0;
2871 if (h < 0)
2872 h = 0;
2873 }
2874
2875 if (width) *width = w;
2876 if (height) *height = h;
2877 }
2878
2879 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
2880 {
2881 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2882
2883 int dx = 0;
2884 int dy = 0;
2885 if (m_parent && m_parent->m_wxwindow)
2886 {
2887 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
2888 dx = gtk_pizza_get_xoffset( pizza );
2889 dy = gtk_pizza_get_yoffset( pizza );
2890 }
2891
2892 if (m_x == -1 && m_y == -1)
2893 {
2894 GdkWindow *source = (GdkWindow *) NULL;
2895 if (m_wxwindow)
2896 source = GTK_PIZZA(m_wxwindow)->bin_window;
2897 else
2898 source = m_widget->window;
2899
2900 if (source)
2901 {
2902 int org_x = 0;
2903 int org_y = 0;
2904 gdk_window_get_origin( source, &org_x, &org_y );
2905
2906 if (GetParent())
2907 GetParent()->ScreenToClient(&org_x, &org_y);
2908
2909 wx_const_cast(wxWindowGTK*, this)->m_x = org_x;
2910 wx_const_cast(wxWindowGTK*, this)->m_y = org_y;
2911 }
2912 }
2913
2914 if (x) (*x) = m_x - dx;
2915 if (y) (*y) = m_y - dy;
2916 }
2917
2918 void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
2919 {
2920 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2921
2922 if (!m_widget->window) return;
2923
2924 GdkWindow *source = (GdkWindow *) NULL;
2925 if (m_wxwindow)
2926 source = GTK_PIZZA(m_wxwindow)->bin_window;
2927 else
2928 source = m_widget->window;
2929
2930 int org_x = 0;
2931 int org_y = 0;
2932 gdk_window_get_origin( source, &org_x, &org_y );
2933
2934 if (!m_wxwindow)
2935 {
2936 if (GTK_WIDGET_NO_WINDOW (m_widget))
2937 {
2938 org_x += m_widget->allocation.x;
2939 org_y += m_widget->allocation.y;
2940 }
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
2952 if (y) *y += org_y;
2953 }
2954
2955 void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
2956 {
2957 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2958
2959 if (!m_widget->window) return;
2960
2961 GdkWindow *source = (GdkWindow *) NULL;
2962 if (m_wxwindow)
2963 source = GTK_PIZZA(m_wxwindow)->bin_window;
2964 else
2965 source = m_widget->window;
2966
2967 int org_x = 0;
2968 int org_y = 0;
2969 gdk_window_get_origin( source, &org_x, &org_y );
2970
2971 if (!m_wxwindow)
2972 {
2973 if (GTK_WIDGET_NO_WINDOW (m_widget))
2974 {
2975 org_x += m_widget->allocation.x;
2976 org_y += m_widget->allocation.y;
2977 }
2978 }
2979
2980 if (x)
2981 {
2982 if (GetLayoutDirection() == wxLayout_RightToLeft)
2983 *x = (GetClientSize().x - *x) - org_x;
2984 else
2985 *x -= org_x;
2986 }
2987 if (y) *y -= org_y;
2988 }
2989
2990 bool wxWindowGTK::Show( bool show )
2991 {
2992 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
2993
2994 if (!wxWindowBase::Show(show))
2995 {
2996 // nothing to do
2997 return false;
2998 }
2999
3000 if (show)
3001 {
3002 if (!m_showOnIdle)
3003 {
3004 gtk_widget_show( m_widget );
3005 wxShowEvent eventShow(GetId(), show);
3006 eventShow.SetEventObject(this);
3007 GetEventHandler()->ProcessEvent(eventShow);
3008 }
3009 }
3010 else
3011 {
3012 gtk_widget_hide( m_widget );
3013 wxShowEvent eventShow(GetId(), show);
3014 eventShow.SetEventObject(this);
3015 GetEventHandler()->ProcessEvent(eventShow);
3016 }
3017
3018 return true;
3019 }
3020
3021 static void wxWindowNotifyEnable(wxWindowGTK* win, bool enable)
3022 {
3023 win->OnParentEnable(enable);
3024
3025 // Recurse, so that children have the opportunity to Do The Right Thing
3026 // and reset colours that have been messed up by a parent's (really ancestor's)
3027 // Enable call
3028 for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
3029 node;
3030 node = node->GetNext() )
3031 {
3032 wxWindow *child = node->GetData();
3033 if (!child->IsKindOf(CLASSINFO(wxDialog)) && !child->IsKindOf(CLASSINFO(wxFrame)))
3034 wxWindowNotifyEnable(child, enable);
3035 }
3036 }
3037
3038 bool wxWindowGTK::Enable( bool enable )
3039 {
3040 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3041
3042 if (!wxWindowBase::Enable(enable))
3043 {
3044 // nothing to do
3045 return false;
3046 }
3047
3048 gtk_widget_set_sensitive( m_widget, enable );
3049 if ( m_wxwindow )
3050 gtk_widget_set_sensitive( m_wxwindow, enable );
3051
3052 wxWindowNotifyEnable(this, enable);
3053
3054 return true;
3055 }
3056
3057 int wxWindowGTK::GetCharHeight() const
3058 {
3059 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
3060
3061 wxFont font = GetFont();
3062 wxCHECK_MSG( font.Ok(), 12, wxT("invalid font") );
3063
3064 PangoContext *context = NULL;
3065 if (m_widget)
3066 context = gtk_widget_get_pango_context( m_widget );
3067
3068 if (!context)
3069 return 0;
3070
3071 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3072 PangoLayout *layout = pango_layout_new(context);
3073 pango_layout_set_font_description(layout, desc);
3074 pango_layout_set_text(layout, "H", 1);
3075 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3076
3077 PangoRectangle rect;
3078 pango_layout_line_get_extents(line, NULL, &rect);
3079
3080 g_object_unref (layout);
3081
3082 return (int) PANGO_PIXELS(rect.height);
3083 }
3084
3085 int wxWindowGTK::GetCharWidth() const
3086 {
3087 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
3088
3089 wxFont font = GetFont();
3090 wxCHECK_MSG( font.Ok(), 8, wxT("invalid font") );
3091
3092 PangoContext *context = NULL;
3093 if (m_widget)
3094 context = gtk_widget_get_pango_context( m_widget );
3095
3096 if (!context)
3097 return 0;
3098
3099 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3100 PangoLayout *layout = pango_layout_new(context);
3101 pango_layout_set_font_description(layout, desc);
3102 pango_layout_set_text(layout, "g", 1);
3103 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3104
3105 PangoRectangle rect;
3106 pango_layout_line_get_extents(line, NULL, &rect);
3107
3108 g_object_unref (layout);
3109
3110 return (int) PANGO_PIXELS(rect.width);
3111 }
3112
3113 void wxWindowGTK::GetTextExtent( const wxString& string,
3114 int *x,
3115 int *y,
3116 int *descent,
3117 int *externalLeading,
3118 const wxFont *theFont ) const
3119 {
3120 wxFont fontToUse = theFont ? *theFont : GetFont();
3121
3122 wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") );
3123
3124 if (string.empty())
3125 {
3126 if (x) (*x) = 0;
3127 if (y) (*y) = 0;
3128 return;
3129 }
3130
3131 PangoContext *context = NULL;
3132 if (m_widget)
3133 context = gtk_widget_get_pango_context( m_widget );
3134
3135 if (!context)
3136 {
3137 if (x) (*x) = 0;
3138 if (y) (*y) = 0;
3139 return;
3140 }
3141
3142 PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
3143 PangoLayout *layout = pango_layout_new(context);
3144 pango_layout_set_font_description(layout, desc);
3145 {
3146 const wxCharBuffer data = wxGTK_CONV( string );
3147 if ( data )
3148 pango_layout_set_text(layout, data, strlen(data));
3149 }
3150
3151 PangoRectangle rect;
3152 pango_layout_get_extents(layout, NULL, &rect);
3153
3154 if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width);
3155 if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height);
3156 if (descent)
3157 {
3158 PangoLayoutIter *iter = pango_layout_get_iter(layout);
3159 int baseline = pango_layout_iter_get_baseline(iter);
3160 pango_layout_iter_free(iter);
3161 *descent = *y - PANGO_PIXELS(baseline);
3162 }
3163 if (externalLeading) (*externalLeading) = 0; // ??
3164
3165 g_object_unref (layout);
3166 }
3167
3168 bool wxWindowGTK::GTKSetDelayedFocusIfNeeded()
3169 {
3170 if ( g_delayedFocus == this )
3171 {
3172 if ( GTK_WIDGET_REALIZED(m_widget) )
3173 {
3174 gtk_widget_grab_focus(m_widget);
3175 g_delayedFocus = NULL;
3176
3177 return true;
3178 }
3179 }
3180
3181 return false;
3182 }
3183
3184 void wxWindowGTK::SetFocus()
3185 {
3186 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3187 if ( m_hasFocus )
3188 {
3189 // don't do anything if we already have focus
3190 return;
3191 }
3192
3193 if (m_wxwindow)
3194 {
3195 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
3196 {
3197 gtk_widget_grab_focus (m_wxwindow);
3198 }
3199 }
3200 else if (m_widget)
3201 {
3202 if (GTK_IS_CONTAINER(m_widget))
3203 {
3204 if (IsKindOf(CLASSINFO(wxRadioButton)))
3205 {
3206 gtk_widget_grab_focus (m_widget);
3207 return;
3208 }
3209
3210 gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
3211 }
3212 else
3213 if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
3214 {
3215
3216 if (!GTK_WIDGET_REALIZED(m_widget))
3217 {
3218 // we can't set the focus to the widget now so we remember that
3219 // it should be focused and will do it later, during the idle
3220 // time, as soon as we can
3221 wxLogTrace(TRACE_FOCUS,
3222 _T("Delaying setting focus to %s(%s)"),
3223 GetClassInfo()->GetClassName(), GetLabel().c_str());
3224
3225 g_delayedFocus = this;
3226 }
3227 else
3228 {
3229 wxLogTrace(TRACE_FOCUS,
3230 _T("Setting focus to %s(%s)"),
3231 GetClassInfo()->GetClassName(), GetLabel().c_str());
3232
3233 gtk_widget_grab_focus (m_widget);
3234 }
3235 }
3236 else
3237 {
3238 wxLogTrace(TRACE_FOCUS,
3239 _T("Can't set focus to %s(%s)"),
3240 GetClassInfo()->GetClassName(), GetLabel().c_str());
3241 }
3242 }
3243 }
3244
3245 bool wxWindowGTK::AcceptsFocus() const
3246 {
3247 return m_acceptsFocus && wxWindowBase::AcceptsFocus();
3248 }
3249
3250 bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
3251 {
3252 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3253
3254 wxWindowGTK *oldParent = m_parent,
3255 *newParent = (wxWindowGTK *)newParentBase;
3256
3257 wxASSERT( GTK_IS_WIDGET(m_widget) );
3258
3259 if ( !wxWindowBase::Reparent(newParent) )
3260 return false;
3261
3262 wxASSERT( GTK_IS_WIDGET(m_widget) );
3263
3264 /* prevent GTK from deleting the widget arbitrarily */
3265 gtk_widget_ref( m_widget );
3266
3267 if (oldParent)
3268 {
3269 gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
3270 }
3271
3272 wxASSERT( GTK_IS_WIDGET(m_widget) );
3273
3274 if (newParent)
3275 {
3276 if (GTK_WIDGET_VISIBLE (newParent->m_widget))
3277 {
3278 m_showOnIdle = true;
3279 gtk_widget_hide( m_widget );
3280 }
3281
3282 /* insert GTK representation */
3283 (*(newParent->m_insertCallback))(newParent, this);
3284 }
3285
3286 /* reverse: prevent GTK from deleting the widget arbitrarily */
3287 gtk_widget_unref( m_widget );
3288
3289 SetLayoutDirection(wxLayout_Default);
3290
3291 return true;
3292 }
3293
3294 void wxWindowGTK::DoAddChild(wxWindowGTK *child)
3295 {
3296 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
3297
3298 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
3299
3300 wxASSERT_MSG( (m_insertCallback != NULL), wxT("invalid child insertion function") );
3301
3302 /* add to list */
3303 AddChild( child );
3304
3305 /* insert GTK representation */
3306 (*m_insertCallback)(this, child);
3307 }
3308
3309 void wxWindowGTK::AddChild(wxWindowBase *child)
3310 {
3311 wxWindowBase::AddChild(child);
3312 m_dirtyTabOrder = true;
3313 if (g_isIdle)
3314 wxapp_install_idle_handler();
3315 }
3316
3317 void wxWindowGTK::RemoveChild(wxWindowBase *child)
3318 {
3319 wxWindowBase::RemoveChild(child);
3320 m_dirtyTabOrder = true;
3321 if (g_isIdle)
3322 wxapp_install_idle_handler();
3323 }
3324
3325 /* static */
3326 wxLayoutDirection wxWindowGTK::GTKGetLayout(GtkWidget *widget)
3327 {
3328 return gtk_widget_get_direction(GTK_WIDGET(widget)) == GTK_TEXT_DIR_RTL
3329 ? wxLayout_RightToLeft
3330 : wxLayout_LeftToRight;
3331 }
3332
3333 /* static */
3334 void wxWindowGTK::GTKSetLayout(GtkWidget *widget, wxLayoutDirection dir)
3335 {
3336 wxASSERT_MSG( dir != wxLayout_Default, _T("invalid layout direction") );
3337
3338 gtk_widget_set_direction(GTK_WIDGET(widget),
3339 dir == wxLayout_RightToLeft ? GTK_TEXT_DIR_RTL
3340 : GTK_TEXT_DIR_LTR);
3341 }
3342
3343 wxLayoutDirection wxWindowGTK::GetLayoutDirection() const
3344 {
3345 return GTKGetLayout(m_widget);
3346 }
3347
3348 void wxWindowGTK::SetLayoutDirection(wxLayoutDirection dir)
3349 {
3350 if ( dir == wxLayout_Default )
3351 {
3352 const wxWindow *const parent = GetParent();
3353 if ( parent )
3354 {
3355 // inherit layout from parent.
3356 dir = parent->GetLayoutDirection();
3357 }
3358 else // no parent, use global default layout
3359 {
3360 dir = wxTheApp->GetLayoutDirection();
3361 }
3362 }
3363
3364 if ( dir == wxLayout_Default )
3365 return;
3366
3367 GTKSetLayout(m_widget, dir);
3368
3369 if (m_wxwindow)
3370 GTKSetLayout(m_wxwindow, dir);
3371 }
3372
3373 wxCoord
3374 wxWindowGTK::AdjustForLayoutDirection(wxCoord x,
3375 wxCoord WXUNUSED(width),
3376 wxCoord WXUNUSED(widthTotal)) const
3377 {
3378 // We now mirrors the coordinates of RTL windows in GtkPizza
3379 return x;
3380 }
3381
3382 void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, MoveKind move)
3383 {
3384 wxWindowBase::DoMoveInTabOrder(win, move);
3385 m_dirtyTabOrder = true;
3386 if (g_isIdle)
3387 wxapp_install_idle_handler();
3388 }
3389
3390 bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3391 {
3392 // none needed by default
3393 return false;
3394 }
3395
3396 void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget* WXUNUSED(w))
3397 {
3398 // nothing to do by default since none is needed
3399 }
3400
3401 void wxWindowGTK::RealizeTabOrder()
3402 {
3403 if (m_wxwindow)
3404 {
3405 if ( !m_children.empty() )
3406 {
3407 // we don't only construct the correct focus chain but also use
3408 // this opportunity to update the mnemonic widgets for the widgets
3409 // that need them
3410
3411 GList *chain = NULL;
3412 wxWindowGTK* mnemonicWindow = NULL;
3413
3414 for ( wxWindowList::const_iterator i = m_children.begin();
3415 i != m_children.end();
3416 ++i )
3417 {
3418 wxWindowGTK *win = *i;
3419
3420 if ( mnemonicWindow )
3421 {
3422 if ( win->AcceptsFocusFromKeyboard() )
3423 {
3424 // wxComboBox et al. needs to focus on on a different
3425 // widget than m_widget, so if the main widget isn't
3426 // focusable try the connect widget
3427 GtkWidget* w = win->m_widget;
3428 if ( !GTK_WIDGET_CAN_FOCUS(w) )
3429 {
3430 w = win->GetConnectWidget();
3431 if ( !GTK_WIDGET_CAN_FOCUS(w) )
3432 w = NULL;
3433 }
3434
3435 if ( w )
3436 {
3437 mnemonicWindow->GTKWidgetDoSetMnemonic(w);
3438 mnemonicWindow = NULL;
3439 }
3440 }
3441 }
3442 else if ( win->GTKWidgetNeedsMnemonic() )
3443 {
3444 mnemonicWindow = win;
3445 }
3446
3447 chain = g_list_prepend(chain, win->m_widget);
3448 }
3449
3450 chain = g_list_reverse(chain);
3451
3452 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain);
3453 g_list_free(chain);
3454 }
3455 else // no children
3456 {
3457 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow));
3458 }
3459 }
3460 }
3461
3462 void wxWindowGTK::Raise()
3463 {
3464 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3465
3466 if (m_wxwindow && m_wxwindow->window)
3467 {
3468 gdk_window_raise( m_wxwindow->window );
3469 }
3470 else if (m_widget->window)
3471 {
3472 gdk_window_raise( m_widget->window );
3473 }
3474 }
3475
3476 void wxWindowGTK::Lower()
3477 {
3478 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3479
3480 if (m_wxwindow && m_wxwindow->window)
3481 {
3482 gdk_window_lower( m_wxwindow->window );
3483 }
3484 else if (m_widget->window)
3485 {
3486 gdk_window_lower( m_widget->window );
3487 }
3488 }
3489
3490 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
3491 {
3492 if ( !wxWindowBase::SetCursor(cursor.Ok() ? cursor : *wxSTANDARD_CURSOR) )
3493 return false;
3494
3495 GTKUpdateCursor();
3496
3497 return true;
3498 }
3499
3500 void wxWindowGTK::GTKUpdateCursor()
3501 {
3502 wxCursor cursor(g_globalCursor.Ok() ? g_globalCursor : GetCursor());
3503 if ( cursor.Ok() )
3504 {
3505 wxArrayGdkWindows windowsThis;
3506 GdkWindow * const winThis = GTKGetWindow(windowsThis);
3507 if ( winThis )
3508 {
3509 gdk_window_set_cursor(winThis, cursor.GetCursor());
3510 }
3511 else
3512 {
3513 const size_t count = windowsThis.size();
3514 for ( size_t n = 0; n < count; n++ )
3515 {
3516 GdkWindow *win = windowsThis[n];
3517 if ( !win )
3518 {
3519 wxFAIL_MSG(_T("NULL window returned by GTKGetWindow()?"));
3520 continue;
3521 }
3522
3523 gdk_window_set_cursor(win, cursor.GetCursor());
3524 }
3525 }
3526 }
3527 }
3528
3529 void wxWindowGTK::WarpPointer( int x, int y )
3530 {
3531 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3532
3533 // We provide this function ourselves as it is
3534 // missing in GDK (top of this file).
3535
3536 GdkWindow *window = (GdkWindow*) NULL;
3537 if (m_wxwindow)
3538 window = GTK_PIZZA(m_wxwindow)->bin_window;
3539 else
3540 window = GetConnectWidget()->window;
3541
3542 if (window)
3543 gdk_window_warp_pointer( window, x, y );
3544 }
3545
3546 wxWindowGTK::ScrollDir wxWindowGTK::ScrollDirFromRange(GtkRange *range) const
3547 {
3548 // find the scrollbar which generated the event
3549 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
3550 {
3551 if ( range == m_scrollBar[dir] )
3552 return (ScrollDir)dir;
3553 }
3554
3555 wxFAIL_MSG( _T("event from unknown scrollbar received") );
3556
3557 return ScrollDir_Max;
3558 }
3559
3560 bool wxWindowGTK::DoScrollByUnits(ScrollDir dir, ScrollUnit unit, int units)
3561 {
3562 bool changed = false;
3563 GtkRange* range = m_scrollBar[dir];
3564 if ( range && units )
3565 {
3566 GtkAdjustment* adj = range->adjustment;
3567 gdouble inc = unit == ScrollUnit_Line ? adj->step_increment
3568 : adj->page_increment;
3569
3570 const int posOld = int(adj->value + 0.5);
3571 gtk_range_set_value(range, posOld + units*inc);
3572
3573 changed = int(adj->value + 0.5) != posOld;
3574 }
3575
3576 return changed;
3577 }
3578
3579 bool wxWindowGTK::ScrollLines(int lines)
3580 {
3581 return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Line, lines);
3582 }
3583
3584 bool wxWindowGTK::ScrollPages(int pages)
3585 {
3586 return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Page, pages);
3587 }
3588
3589 void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
3590 {
3591 if (!m_widget)
3592 return;
3593 if (!m_widget->window)
3594 return;
3595
3596 if (m_wxwindow)
3597 {
3598 if (!GTK_PIZZA(m_wxwindow)->bin_window) return;
3599
3600 GdkRectangle gdk_rect,
3601 *p;
3602 if (rect)
3603 {
3604 gdk_rect.x = rect->x;
3605 gdk_rect.y = rect->y;
3606 gdk_rect.width = rect->width;
3607 gdk_rect.height = rect->height;
3608 if (GetLayoutDirection() == wxLayout_RightToLeft)
3609 gdk_rect.x = GetClientSize().x - gdk_rect.x - gdk_rect.width;
3610
3611 p = &gdk_rect;
3612 }
3613 else // invalidate everything
3614 {
3615 p = NULL;
3616 }
3617
3618 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, p, TRUE );
3619 }
3620 }
3621
3622 void wxWindowGTK::Update()
3623 {
3624 GtkUpdate();
3625
3626 // when we call Update() we really want to update the window immediately on
3627 // screen, even if it means flushing the entire queue and hence slowing down
3628 // everything -- but it should still be done, it's just that Update() should
3629 // be called very rarely
3630 gdk_flush();
3631 }
3632
3633 void wxWindowGTK::GtkUpdate()
3634 {
3635 if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window)
3636 gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE );
3637 if (m_widget && m_widget->window)
3638 gdk_window_process_updates( m_widget->window, FALSE );
3639
3640 // for consistency with other platforms (and also because it's convenient
3641 // to be able to update an entire TLW by calling Update() only once), we
3642 // should also update all our children here
3643 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
3644 node;
3645 node = node->GetNext() )
3646 {
3647 node->GetData()->GtkUpdate();
3648 }
3649 }
3650
3651 bool wxWindowGTK::DoIsExposed( int x, int y ) const
3652 {
3653 return m_updateRegion.Contains(x, y) != wxOutRegion;
3654 }
3655
3656
3657 bool wxWindowGTK::DoIsExposed( int x, int y, int w, int h ) const
3658 {
3659 if (GetLayoutDirection() == wxLayout_RightToLeft)
3660 return m_updateRegion.Contains(x-w, y, w, h) != wxOutRegion;
3661 else
3662 return m_updateRegion.Contains(x, y, w, h) != wxOutRegion;
3663 }
3664
3665 void wxWindowGTK::GtkSendPaintEvents()
3666 {
3667 if (!m_wxwindow)
3668 {
3669 m_updateRegion.Clear();
3670 return;
3671 }
3672
3673 // Clip to paint region in wxClientDC
3674 m_clipPaintRegion = true;
3675
3676 m_nativeUpdateRegion = m_updateRegion;
3677
3678 if (GetLayoutDirection() == wxLayout_RightToLeft)
3679 {
3680 // Transform m_updateRegion under RTL
3681 m_updateRegion.Clear();
3682
3683 gint width;
3684 gdk_window_get_geometry( GTK_PIZZA(m_wxwindow)->bin_window,
3685 NULL, NULL, &width, NULL, NULL );
3686
3687 wxRegionIterator upd( m_nativeUpdateRegion );
3688 while (upd)
3689 {
3690 wxRect rect;
3691 rect.x = upd.GetX();
3692 rect.y = upd.GetY();
3693 rect.width = upd.GetWidth();
3694 rect.height = upd.GetHeight();
3695
3696 rect.x = width - rect.x - rect.width;
3697 m_updateRegion.Union( rect );
3698
3699 ++upd;
3700 }
3701 }
3702
3703 // widget to draw on
3704 GtkPizza *pizza = GTK_PIZZA (m_wxwindow);
3705
3706 if (GetThemeEnabled() && (GetBackgroundStyle() == wxBG_STYLE_SYSTEM))
3707 {
3708 // find ancestor from which to steal background
3709 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
3710 if (!parent)
3711 parent = (wxWindow*)this;
3712
3713 if (GTK_WIDGET_MAPPED(parent->m_widget))
3714 {
3715 wxRegionIterator upd( m_nativeUpdateRegion );
3716 while (upd)
3717 {
3718 GdkRectangle rect;
3719 rect.x = upd.GetX();
3720 rect.y = upd.GetY();
3721 rect.width = upd.GetWidth();
3722 rect.height = upd.GetHeight();
3723
3724 gtk_paint_flat_box( parent->m_widget->style,
3725 pizza->bin_window,
3726 (GtkStateType)GTK_WIDGET_STATE(m_wxwindow),
3727 GTK_SHADOW_NONE,
3728 &rect,
3729 parent->m_widget,
3730 (char *)"base",
3731 0, 0, -1, -1 );
3732
3733 ++upd;
3734 }
3735 }
3736 }
3737 else
3738 {
3739 wxWindowDC dc( (wxWindow*)this );
3740 dc.SetClippingRegion( m_updateRegion );
3741
3742 wxEraseEvent erase_event( GetId(), &dc );
3743 erase_event.SetEventObject( this );
3744
3745 GetEventHandler()->ProcessEvent(erase_event);
3746 }
3747
3748 wxNcPaintEvent nc_paint_event( GetId() );
3749 nc_paint_event.SetEventObject( this );
3750 GetEventHandler()->ProcessEvent( nc_paint_event );
3751
3752 wxPaintEvent paint_event( GetId() );
3753 paint_event.SetEventObject( this );
3754 GetEventHandler()->ProcessEvent( paint_event );
3755
3756 m_clipPaintRegion = false;
3757
3758 m_updateRegion.Clear();
3759 m_nativeUpdateRegion.Clear();
3760 }
3761
3762 void wxWindowGTK::SetDoubleBuffered( bool on )
3763 {
3764 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3765
3766 if ( m_wxwindow )
3767 gtk_widget_set_double_buffered( m_wxwindow, on );
3768 }
3769
3770 bool wxWindowGTK::IsDoubleBuffered() const
3771 {
3772 return GTK_WIDGET_DOUBLE_BUFFERED( m_wxwindow );
3773 }
3774
3775 void wxWindowGTK::ClearBackground()
3776 {
3777 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3778 }
3779
3780 #if wxUSE_TOOLTIPS
3781 void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
3782 {
3783 wxWindowBase::DoSetToolTip(tip);
3784
3785 if (m_tooltip)
3786 m_tooltip->Apply( (wxWindow *)this );
3787 }
3788
3789 void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
3790 {
3791 wxString tmp( tip );
3792 gtk_tooltips_set_tip( tips, GetConnectWidget(), wxGTK_CONV(tmp), (gchar*) NULL );
3793 }
3794 #endif // wxUSE_TOOLTIPS
3795
3796 bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
3797 {
3798 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3799
3800 if (!wxWindowBase::SetBackgroundColour(colour))
3801 return false;
3802
3803 if (colour.Ok())
3804 {
3805 // We need the pixel value e.g. for background clearing.
3806 m_backgroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
3807 }
3808
3809 // apply style change (forceStyle=true so that new style is applied
3810 // even if the bg colour changed from valid to wxNullColour)
3811 if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM)
3812 ApplyWidgetStyle(true);
3813
3814 return true;
3815 }
3816
3817 bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
3818 {
3819 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3820
3821 if (!wxWindowBase::SetForegroundColour(colour))
3822 {
3823 return false;
3824 }
3825
3826 if (colour.Ok())
3827 {
3828 // We need the pixel value e.g. for background clearing.
3829 m_foregroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
3830 }
3831
3832 // apply style change (forceStyle=true so that new style is applied
3833 // even if the bg colour changed from valid to wxNullColour):
3834 ApplyWidgetStyle(true);
3835
3836 return true;
3837 }
3838
3839 PangoContext *wxWindowGTK::GtkGetPangoDefaultContext()
3840 {
3841 return gtk_widget_get_pango_context( m_widget );
3842 }
3843
3844 GtkRcStyle *wxWindowGTK::CreateWidgetStyle(bool forceStyle)
3845 {
3846 // do we need to apply any changes at all?
3847 if ( !forceStyle &&
3848 !m_font.Ok() &&
3849 !m_foregroundColour.Ok() && !m_backgroundColour.Ok() )
3850 {
3851 return NULL;
3852 }
3853
3854 GtkRcStyle *style = gtk_rc_style_new();
3855
3856 if ( m_font.Ok() )
3857 {
3858 style->font_desc =
3859 pango_font_description_copy( m_font.GetNativeFontInfo()->description );
3860 }
3861
3862 if ( m_foregroundColour.Ok() )
3863 {
3864 const GdkColor *fg = m_foregroundColour.GetColor();
3865
3866 style->fg[GTK_STATE_NORMAL] = *fg;
3867 style->color_flags[GTK_STATE_NORMAL] = GTK_RC_FG;
3868
3869 style->fg[GTK_STATE_PRELIGHT] = *fg;
3870 style->color_flags[GTK_STATE_PRELIGHT] = GTK_RC_FG;
3871
3872 style->fg[GTK_STATE_ACTIVE] = *fg;
3873 style->color_flags[GTK_STATE_ACTIVE] = GTK_RC_FG;
3874 }
3875
3876 if ( m_backgroundColour.Ok() )
3877 {
3878 const GdkColor *bg = m_backgroundColour.GetColor();
3879
3880 style->bg[GTK_STATE_NORMAL] = *bg;
3881 style->base[GTK_STATE_NORMAL] = *bg;
3882 style->color_flags[GTK_STATE_NORMAL] = (GtkRcFlags)
3883 (style->color_flags[GTK_STATE_NORMAL] | GTK_RC_BG | GTK_RC_BASE);
3884
3885 style->bg[GTK_STATE_PRELIGHT] = *bg;
3886 style->base[GTK_STATE_PRELIGHT] = *bg;
3887 style->color_flags[GTK_STATE_PRELIGHT] = (GtkRcFlags)
3888 (style->color_flags[GTK_STATE_PRELIGHT] | GTK_RC_BG | GTK_RC_BASE);
3889
3890 style->bg[GTK_STATE_ACTIVE] = *bg;
3891 style->base[GTK_STATE_ACTIVE] = *bg;
3892 style->color_flags[GTK_STATE_ACTIVE] = (GtkRcFlags)
3893 (style->color_flags[GTK_STATE_ACTIVE] | GTK_RC_BG | GTK_RC_BASE);
3894
3895 style->bg[GTK_STATE_INSENSITIVE] = *bg;
3896 style->base[GTK_STATE_INSENSITIVE] = *bg;
3897 style->color_flags[GTK_STATE_INSENSITIVE] = (GtkRcFlags)
3898 (style->color_flags[GTK_STATE_INSENSITIVE] | GTK_RC_BG | GTK_RC_BASE);
3899 }
3900
3901 return style;
3902 }
3903
3904 void wxWindowGTK::ApplyWidgetStyle(bool forceStyle)
3905 {
3906 GtkRcStyle *style = CreateWidgetStyle(forceStyle);
3907 if ( style )
3908 {
3909 DoApplyWidgetStyle(style);
3910 gtk_rc_style_unref(style);
3911 }
3912
3913 // Style change may affect GTK+'s size calculation:
3914 InvalidateBestSize();
3915 }
3916
3917 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
3918 {
3919 if (m_wxwindow)
3920 gtk_widget_modify_style(m_wxwindow, style);
3921 else
3922 gtk_widget_modify_style(m_widget, style);
3923 }
3924
3925 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
3926 {
3927 wxWindowBase::SetBackgroundStyle(style);
3928
3929 if (style == wxBG_STYLE_CUSTOM)
3930 {
3931 GdkWindow *window = (GdkWindow*) NULL;
3932 if (m_wxwindow)
3933 window = GTK_PIZZA(m_wxwindow)->bin_window;
3934 else
3935 window = GetConnectWidget()->window;
3936
3937 if (window)
3938 {
3939 // Make sure GDK/X11 doesn't refresh the window
3940 // automatically.
3941 gdk_window_set_back_pixmap( window, None, False );
3942 #ifdef __X__
3943 Display* display = GDK_WINDOW_DISPLAY(window);
3944 XFlush(display);
3945 #endif
3946 m_needsStyleChange = false;
3947 }
3948 else
3949 // Do in OnIdle, because the window is not yet available
3950 m_needsStyleChange = true;
3951
3952 // Don't apply widget style, or we get a grey background
3953 }
3954 else
3955 {
3956 // apply style change (forceStyle=true so that new style is applied
3957 // even if the bg colour changed from valid to wxNullColour):
3958 ApplyWidgetStyle(true);
3959 }
3960 return true;
3961 }
3962
3963 #if wxUSE_DRAG_AND_DROP
3964
3965 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
3966 {
3967 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3968
3969 GtkWidget *dnd_widget = GetConnectWidget();
3970
3971 if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
3972
3973 if (m_dropTarget) delete m_dropTarget;
3974 m_dropTarget = dropTarget;
3975
3976 if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
3977 }
3978
3979 #endif // wxUSE_DRAG_AND_DROP
3980
3981 GtkWidget* wxWindowGTK::GetConnectWidget()
3982 {
3983 GtkWidget *connect_widget = m_widget;
3984 if (m_wxwindow) connect_widget = m_wxwindow;
3985
3986 return connect_widget;
3987 }
3988
3989 bool wxWindowGTK::GTKIsOwnWindow(GdkWindow *window) const
3990 {
3991 wxArrayGdkWindows windowsThis;
3992 GdkWindow * const winThis = GTKGetWindow(windowsThis);
3993
3994 return winThis ? window == winThis
3995 : windowsThis.Index(window) != wxNOT_FOUND;
3996 }
3997
3998 GdkWindow *wxWindowGTK::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
3999 {
4000 return m_wxwindow ? GTK_PIZZA(m_wxwindow)->bin_window : m_widget->window;
4001 }
4002
4003 bool wxWindowGTK::SetFont( const wxFont &font )
4004 {
4005 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
4006
4007 if (!wxWindowBase::SetFont(font))
4008 return false;
4009
4010 // apply style change (forceStyle=true so that new style is applied
4011 // even if the font changed from valid to wxNullFont):
4012 ApplyWidgetStyle(true);
4013
4014 return true;
4015 }
4016
4017 void wxWindowGTK::DoCaptureMouse()
4018 {
4019 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4020
4021 GdkWindow *window = (GdkWindow*) NULL;
4022 if (m_wxwindow)
4023 window = GTK_PIZZA(m_wxwindow)->bin_window;
4024 else
4025 window = GetConnectWidget()->window;
4026
4027 wxCHECK_RET( window, _T("CaptureMouse() failed") );
4028
4029 const wxCursor* cursor = &m_cursor;
4030 if (!cursor->Ok())
4031 cursor = wxSTANDARD_CURSOR;
4032
4033 gdk_pointer_grab( window, FALSE,
4034 (GdkEventMask)
4035 (GDK_BUTTON_PRESS_MASK |
4036 GDK_BUTTON_RELEASE_MASK |
4037 GDK_POINTER_MOTION_HINT_MASK |
4038 GDK_POINTER_MOTION_MASK),
4039 (GdkWindow *) NULL,
4040 cursor->GetCursor(),
4041 (guint32)GDK_CURRENT_TIME );
4042 g_captureWindow = this;
4043 g_captureWindowHasMouse = true;
4044 }
4045
4046 void wxWindowGTK::DoReleaseMouse()
4047 {
4048 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4049
4050 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
4051
4052 g_captureWindow = (wxWindowGTK*) NULL;
4053
4054 GdkWindow *window = (GdkWindow*) NULL;
4055 if (m_wxwindow)
4056 window = GTK_PIZZA(m_wxwindow)->bin_window;
4057 else
4058 window = GetConnectWidget()->window;
4059
4060 if (!window)
4061 return;
4062
4063 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
4064 }
4065
4066 /* static */
4067 wxWindow *wxWindowBase::GetCapture()
4068 {
4069 return (wxWindow *)g_captureWindow;
4070 }
4071
4072 bool wxWindowGTK::IsRetained() const
4073 {
4074 return false;
4075 }
4076
4077 void wxWindowGTK::BlockScrollEvent()
4078 {
4079 wxASSERT(!m_blockScrollEvent);
4080 m_blockScrollEvent = true;
4081 }
4082
4083 void wxWindowGTK::UnblockScrollEvent()
4084 {
4085 wxASSERT(m_blockScrollEvent);
4086 m_blockScrollEvent = false;
4087 }
4088
4089 void wxWindowGTK::SetScrollbar(int orient,
4090 int pos,
4091 int thumbVisible,
4092 int range,
4093 bool WXUNUSED(update))
4094 {
4095 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4096 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4097
4098 if (range > 0)
4099 {
4100 m_hasScrolling = true;
4101 }
4102 else
4103 {
4104 // GtkRange requires upper > lower
4105 range =
4106 thumbVisible = 1;
4107 }
4108
4109 if (pos > range - thumbVisible)
4110 pos = range - thumbVisible;
4111 if (pos < 0)
4112 pos = 0;
4113 GtkAdjustment* adj = m_scrollBar[ScrollDirFromOrient(orient)]->adjustment;
4114 adj->step_increment = 1;
4115 adj->page_increment =
4116 adj->page_size = thumbVisible;
4117 adj->upper = range;
4118 SetScrollPos(orient, pos);
4119 gtk_adjustment_changed(adj);
4120 }
4121
4122 void wxWindowGTK::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh))
4123 {
4124 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4125 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4126
4127 // This check is more than an optimization. Without it, the slider
4128 // will not move smoothly while tracking when using wxScrollHelper.
4129 if (GetScrollPos(orient) != pos)
4130 {
4131 const int dir = ScrollDirFromOrient(orient);
4132 GtkAdjustment* adj = m_scrollBar[dir]->adjustment;
4133 const int max = int(adj->upper - adj->page_size);
4134 if (pos > max)
4135 pos = max;
4136 if (pos < 0)
4137 pos = 0;
4138 m_scrollPos[dir] = adj->value = pos;
4139
4140 // If a "value_changed" signal emission is not already in progress
4141 if (!m_blockValueChanged[dir])
4142 {
4143 gtk_adjustment_value_changed(adj);
4144 }
4145 }
4146 }
4147
4148 int wxWindowGTK::GetScrollThumb(int orient) const
4149 {
4150 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4151 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4152
4153 return int(m_scrollBar[ScrollDirFromOrient(orient)]->adjustment->page_size);
4154 }
4155
4156 int wxWindowGTK::GetScrollPos( int orient ) const
4157 {
4158 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4159 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4160
4161 return int(m_scrollBar[ScrollDirFromOrient(orient)]->adjustment->value + 0.5);
4162 }
4163
4164 int wxWindowGTK::GetScrollRange( int orient ) const
4165 {
4166 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4167 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4168
4169 return int(m_scrollBar[ScrollDirFromOrient(orient)]->adjustment->upper);
4170 }
4171
4172 // Determine if increment is the same as +/-x, allowing for some small
4173 // difference due to possible inexactness in floating point arithmetic
4174 static inline bool IsScrollIncrement(double increment, double x)
4175 {
4176 wxASSERT(increment > 0);
4177 const double tolerance = 1.0 / 1024;
4178 return fabs(increment - fabs(x)) < tolerance;
4179 }
4180
4181 wxEventType wxWindowGTK::GetScrollEventType(GtkRange* range)
4182 {
4183 DEBUG_MAIN_THREAD
4184
4185 if (g_isIdle)
4186 wxapp_install_idle_handler();
4187
4188 wxASSERT(range == m_scrollBar[0] || range == m_scrollBar[1]);
4189
4190 const int barIndex = range == m_scrollBar[1];
4191 GtkAdjustment* adj = range->adjustment;
4192
4193 const int value = int(adj->value + 0.5);
4194
4195 // save previous position
4196 const double oldPos = m_scrollPos[barIndex];
4197 // update current position
4198 m_scrollPos[barIndex] = adj->value;
4199 // If event should be ignored, or integral position has not changed
4200 if (!m_hasVMT || g_blockEventsOnDrag || value == int(oldPos + 0.5))
4201 {
4202 return wxEVT_NULL;
4203 }
4204
4205 wxEventType eventType = wxEVT_SCROLL_THUMBTRACK;
4206 if (!m_isScrolling)
4207 {
4208 // Difference from last change event
4209 const double diff = adj->value - oldPos;
4210 const bool isDown = diff > 0;
4211
4212 if (IsScrollIncrement(adj->step_increment, diff))
4213 {
4214 eventType = isDown ? wxEVT_SCROLL_LINEDOWN : wxEVT_SCROLL_LINEUP;
4215 }
4216 else if (IsScrollIncrement(adj->page_increment, diff))
4217 {
4218 eventType = isDown ? wxEVT_SCROLL_PAGEDOWN : wxEVT_SCROLL_PAGEUP;
4219 }
4220 else if (m_mouseButtonDown)
4221 {
4222 // Assume track event
4223 m_isScrolling = true;
4224 }
4225 }
4226 return eventType;
4227 }
4228
4229 void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
4230 {
4231 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4232
4233 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4234
4235 // No scrolling requested.
4236 if ((dx == 0) && (dy == 0)) return;
4237
4238 m_clipPaintRegion = true;
4239
4240 if (GetLayoutDirection() == wxLayout_RightToLeft)
4241 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), dx, -dy );
4242 else
4243 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy );
4244
4245 m_clipPaintRegion = false;
4246 }
4247
4248 void wxWindowGTK::GtkScrolledWindowSetBorder(GtkWidget* w, int wxstyle)
4249 {
4250 //RN: Note that static controls usually have no border on gtk, so maybe
4251 //it makes sense to treat that as simply no border at the wx level
4252 //as well...
4253 if (!(wxstyle & wxNO_BORDER) && !(wxstyle & wxBORDER_STATIC))
4254 {
4255 GtkShadowType gtkstyle;
4256
4257 if(wxstyle & wxBORDER_RAISED)
4258 gtkstyle = GTK_SHADOW_OUT;
4259 else if (wxstyle & wxBORDER_SUNKEN)
4260 gtkstyle = GTK_SHADOW_IN;
4261 else if (wxstyle & wxBORDER_DOUBLE)
4262 gtkstyle = GTK_SHADOW_ETCHED_IN;
4263 else //default
4264 gtkstyle = GTK_SHADOW_IN;
4265
4266 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w),
4267 gtkstyle );
4268 }
4269 }
4270
4271 void wxWindowGTK::SetWindowStyleFlag( long style )
4272 {
4273 // Updates the internal variable. NB: Now m_windowStyle bits carry the _new_ style values already
4274 wxWindowBase::SetWindowStyleFlag(style);
4275 }
4276
4277 // Find the wxWindow at the current mouse position, also returning the mouse
4278 // position.
4279 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4280 {
4281 pt = wxGetMousePosition();
4282 wxWindow* found = wxFindWindowAtPoint(pt);
4283 return found;
4284 }
4285
4286 // Get the current mouse position.
4287 wxPoint wxGetMousePosition()
4288 {
4289 /* This crashes when used within wxHelpContext,
4290 so we have to use the X-specific implementation below.
4291 gint x, y;
4292 GdkModifierType *mask;
4293 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4294
4295 return wxPoint(x, y);
4296 */
4297
4298 int x, y;
4299 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
4300
4301 Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY();
4302 Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
4303 Window rootReturn, childReturn;
4304 int rootX, rootY, winX, winY;
4305 unsigned int maskReturn;
4306
4307 XQueryPointer (display,
4308 rootWindow,
4309 &rootReturn,
4310 &childReturn,
4311 &rootX, &rootY, &winX, &winY, &maskReturn);
4312 return wxPoint(rootX, rootY);
4313
4314 }
4315
4316 // Needed for implementing e.g. combobox on wxGTK within a modal dialog.
4317 void wxAddGrab(wxWindow* window)
4318 {
4319 gtk_grab_add( (GtkWidget*) window->GetHandle() );
4320 }
4321
4322 void wxRemoveGrab(wxWindow* window)
4323 {
4324 gtk_grab_remove( (GtkWidget*) window->GetHandle() );
4325 }