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