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