]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/window.cpp
More preview code for the DC reorganisation.
[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
1135 const wxWxCharBuffer data(wxGTK_CONV_BACK_SYS(str));
1136 if( !data )
1137 return;
1138
1139 bool ret = false;
1140
1141 // Implement OnCharHook by checking ancestor top level windows
1142 wxWindow *parent = window;
1143 while (parent && !parent->IsTopLevel())
1144 parent = parent->GetParent();
1145
1146 for( const wxChar* pstr = data; *pstr; pstr++ )
1147 {
1148 #if wxUSE_UNICODE
1149 event.m_uniChar = *pstr;
1150 // Backward compatible for ISO-8859-1
1151 event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0;
1152 wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), event.m_uniChar);
1153 #else
1154 event.m_keyCode = *pstr;
1155 #endif // wxUSE_UNICODE
1156
1157 // To conform to the docs we need to translate Ctrl-alpha
1158 // characters to values in the range 1-26.
1159 if ( event.ControlDown() &&
1160 ( wxIsLowerChar(*pstr) || wxIsUpperChar(*pstr) ))
1161 {
1162 if ( wxIsLowerChar(*pstr) )
1163 event.m_keyCode = *pstr - 'a' + 1;
1164 if ( wxIsUpperChar(*pstr) )
1165 event.m_keyCode = *pstr - 'A' + 1;
1166
1167 event.m_keyCode = *pstr - 'a' + 1;
1168 #if wxUSE_UNICODE
1169 event.m_uniChar = event.m_keyCode;
1170 #endif
1171 }
1172
1173 if (parent)
1174 {
1175 event.SetEventType( wxEVT_CHAR_HOOK );
1176 ret = parent->GetEventHandler()->ProcessEvent( event );
1177 }
1178
1179 if (!ret)
1180 {
1181 event.SetEventType(wxEVT_CHAR);
1182 ret = window->GetEventHandler()->ProcessEvent( event );
1183 }
1184 }
1185 }
1186 }
1187
1188
1189 //-----------------------------------------------------------------------------
1190 // "key_release_event" from any window
1191 //-----------------------------------------------------------------------------
1192
1193 extern "C" {
1194 static gboolean
1195 gtk_window_key_release_callback( GtkWidget *widget,
1196 GdkEventKey *gdk_event,
1197 wxWindowGTK *win )
1198 {
1199 DEBUG_MAIN_THREAD
1200
1201 if (!win->m_hasVMT)
1202 return FALSE;
1203
1204 if (g_blockEventsOnDrag)
1205 return FALSE;
1206
1207 wxKeyEvent event( wxEVT_KEY_UP );
1208 if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
1209 {
1210 // unknown key pressed, ignore (the event would be useless anyhow)
1211 return FALSE;
1212 }
1213
1214 return win->GTKProcessEvent(event);
1215 }
1216 }
1217
1218 // ============================================================================
1219 // the mouse events
1220 // ============================================================================
1221
1222 // ----------------------------------------------------------------------------
1223 // mouse event processing helpers
1224 // ----------------------------------------------------------------------------
1225
1226 // init wxMouseEvent with the info from GdkEventXXX struct
1227 template<typename T> void InitMouseEvent(wxWindowGTK *win,
1228 wxMouseEvent& event,
1229 T *gdk_event)
1230 {
1231 event.SetTimestamp( gdk_event->time );
1232 event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK);
1233 event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK);
1234 event.m_altDown = (gdk_event->state & GDK_MOD1_MASK);
1235 event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK);
1236 event.m_leftDown = (gdk_event->state & GDK_BUTTON1_MASK);
1237 event.m_middleDown = (gdk_event->state & GDK_BUTTON2_MASK);
1238 event.m_rightDown = (gdk_event->state & GDK_BUTTON3_MASK);
1239 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
1240 {
1241 event.m_linesPerAction = 3;
1242 event.m_wheelDelta = 120;
1243 if (((GdkEventButton*)gdk_event)->button == 4)
1244 event.m_wheelRotation = 120;
1245 else if (((GdkEventButton*)gdk_event)->button == 5)
1246 event.m_wheelRotation = -120;
1247 }
1248
1249 wxPoint pt = win->GetClientAreaOrigin();
1250 event.m_x = (wxCoord)gdk_event->x - pt.x;
1251 event.m_y = (wxCoord)gdk_event->y - pt.y;
1252
1253 if ((win->m_wxwindow) && (win->GetLayoutDirection() == wxLayout_RightToLeft))
1254 {
1255 // origin in the upper right corner
1256 int window_width = gtk_pizza_get_rtl_offset( GTK_PIZZA(win->m_wxwindow) );
1257 event.m_x = window_width - event.m_x;
1258 }
1259
1260 event.SetEventObject( win );
1261 event.SetId( win->GetId() );
1262 event.SetTimestamp( gdk_event->time );
1263 }
1264
1265 static void AdjustEventButtonState(wxMouseEvent& event)
1266 {
1267 // GDK reports the old state of the button for a button press event, but
1268 // for compatibility with MSW and common sense we want m_leftDown be TRUE
1269 // for a LEFT_DOWN event, not FALSE, so we will invert
1270 // left/right/middleDown for the corresponding click events
1271
1272 if ((event.GetEventType() == wxEVT_LEFT_DOWN) ||
1273 (event.GetEventType() == wxEVT_LEFT_DCLICK) ||
1274 (event.GetEventType() == wxEVT_LEFT_UP))
1275 {
1276 event.m_leftDown = !event.m_leftDown;
1277 return;
1278 }
1279
1280 if ((event.GetEventType() == wxEVT_MIDDLE_DOWN) ||
1281 (event.GetEventType() == wxEVT_MIDDLE_DCLICK) ||
1282 (event.GetEventType() == wxEVT_MIDDLE_UP))
1283 {
1284 event.m_middleDown = !event.m_middleDown;
1285 return;
1286 }
1287
1288 if ((event.GetEventType() == wxEVT_RIGHT_DOWN) ||
1289 (event.GetEventType() == wxEVT_RIGHT_DCLICK) ||
1290 (event.GetEventType() == wxEVT_RIGHT_UP))
1291 {
1292 event.m_rightDown = !event.m_rightDown;
1293 return;
1294 }
1295 }
1296
1297 // find the window to send the mouse event too
1298 static
1299 wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
1300 {
1301 wxCoord xx = x;
1302 wxCoord yy = y;
1303
1304 if (win->m_wxwindow)
1305 {
1306 GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
1307 xx += gtk_pizza_get_xoffset( pizza );
1308 yy += gtk_pizza_get_yoffset( pizza );
1309 }
1310
1311 wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
1312 while (node)
1313 {
1314 wxWindowGTK *child = node->GetData();
1315
1316 node = node->GetNext();
1317 if (!child->IsShown())
1318 continue;
1319
1320 if (child->IsTransparentForMouse())
1321 {
1322 // wxStaticBox is transparent in the box itself
1323 int xx1 = child->m_x;
1324 int yy1 = child->m_y;
1325 int xx2 = child->m_x + child->m_width;
1326 int yy2 = child->m_y + child->m_height;
1327
1328 // left
1329 if (((xx >= xx1) && (xx <= xx1+10) && (yy >= yy1) && (yy <= yy2)) ||
1330 // right
1331 ((xx >= xx2-10) && (xx <= xx2) && (yy >= yy1) && (yy <= yy2)) ||
1332 // top
1333 ((xx >= xx1) && (xx <= xx2) && (yy >= yy1) && (yy <= yy1+10)) ||
1334 // bottom
1335 ((xx >= xx1) && (xx <= xx2) && (yy >= yy2-1) && (yy <= yy2)))
1336 {
1337 win = child;
1338 x -= child->m_x;
1339 y -= child->m_y;
1340 break;
1341 }
1342
1343 }
1344 else
1345 {
1346 if ((child->m_wxwindow == (GtkWidget*) NULL) &&
1347 (child->m_x <= xx) &&
1348 (child->m_y <= yy) &&
1349 (child->m_x+child->m_width >= xx) &&
1350 (child->m_y+child->m_height >= yy))
1351 {
1352 win = child;
1353 x -= child->m_x;
1354 y -= child->m_y;
1355 break;
1356 }
1357 }
1358 }
1359
1360 return win;
1361 }
1362
1363 // ----------------------------------------------------------------------------
1364 // common event handlers helpers
1365 // ----------------------------------------------------------------------------
1366
1367 bool wxWindowGTK::GTKProcessEvent(wxEvent& event) const
1368 {
1369 // nothing special at this level
1370 return GetEventHandler()->ProcessEvent(event);
1371 }
1372
1373 int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny *event) const
1374 {
1375 DEBUG_MAIN_THREAD
1376
1377 if (!m_hasVMT)
1378 return FALSE;
1379 if (g_blockEventsOnDrag)
1380 return TRUE;
1381 if (g_blockEventsOnScroll)
1382 return TRUE;
1383
1384 if (!GTKIsOwnWindow(event->window))
1385 return FALSE;
1386
1387 return -1;
1388 }
1389
1390 // overloads for all GDK event types we use here: we need to have this as
1391 // GdkEventXXX can't be implicitly cast to GdkEventAny even if it, in fact,
1392 // derives from it in the sense that the structs have the same layout
1393 #define wxDEFINE_COMMON_PROLOGUE_OVERLOAD(T) \
1394 static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win) \
1395 { \
1396 return win->GTKCallbackCommonPrologue((GdkEventAny *)event); \
1397 }
1398
1399 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventButton)
1400 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion)
1401 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing)
1402
1403 #undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD
1404
1405 #define wxCOMMON_CALLBACK_PROLOGUE(event, win) \
1406 const int rc = wxGtkCallbackCommonPrologue(event, win); \
1407 if ( rc != -1 ) \
1408 return rc
1409
1410 // send the wxChildFocusEvent and wxFocusEvent, common code of
1411 // gtk_window_focus_in_callback() and SetFocus()
1412 static bool DoSendFocusEvents(wxWindow *win)
1413 {
1414 // Notify the parent keeping track of focus for the kbd navigation
1415 // purposes that we got it.
1416 wxChildFocusEvent eventChildFocus(win);
1417 (void)win->GetEventHandler()->ProcessEvent(eventChildFocus);
1418
1419 wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
1420 eventFocus.SetEventObject(win);
1421
1422 return win->GetEventHandler()->ProcessEvent(eventFocus);
1423 }
1424
1425 // all event handlers must have C linkage as they're called from GTK+ C code
1426 extern "C"
1427 {
1428
1429 //-----------------------------------------------------------------------------
1430 // "button_press_event"
1431 //-----------------------------------------------------------------------------
1432
1433 static gboolean
1434 gtk_window_button_press_callback( GtkWidget *widget,
1435 GdkEventButton *gdk_event,
1436 wxWindowGTK *win )
1437 {
1438 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1439
1440 g_lastButtonNumber = gdk_event->button;
1441
1442 if (win->m_wxwindow && (g_focusWindow != win) && win->CanAcceptFocus())
1443 {
1444 gtk_widget_grab_focus( win->m_wxwindow );
1445 }
1446
1447 // GDK sends surplus button down events
1448 // before a double click event. We
1449 // need to filter these out.
1450 if ((gdk_event->type == GDK_BUTTON_PRESS) && (win->m_wxwindow))
1451 {
1452 GdkEvent *peek_event = gdk_event_peek();
1453 if (peek_event)
1454 {
1455 if ((peek_event->type == GDK_2BUTTON_PRESS) ||
1456 (peek_event->type == GDK_3BUTTON_PRESS))
1457 {
1458 gdk_event_free( peek_event );
1459 return TRUE;
1460 }
1461 else
1462 {
1463 gdk_event_free( peek_event );
1464 }
1465 }
1466 }
1467
1468 wxEventType event_type = wxEVT_NULL;
1469
1470 // GdkDisplay is a GTK+ 2.2.0 thing
1471 #if defined(__WXGTK20__) && GTK_CHECK_VERSION(2, 2, 0)
1472 if ( gdk_event->type == GDK_2BUTTON_PRESS &&
1473 !gtk_check_version(2,2,0) &&
1474 gdk_event->button >= 1 && gdk_event->button <= 3 )
1475 {
1476 // Reset GDK internal timestamp variables in order to disable GDK
1477 // triple click events. GDK will then next time believe no button has
1478 // been clicked just before, and send a normal button click event.
1479 GdkDisplay* display = gtk_widget_get_display (widget);
1480 display->button_click_time[1] = 0;
1481 display->button_click_time[0] = 0;
1482 }
1483 #endif // GTK 2+
1484
1485 if (gdk_event->button == 1)
1486 {
1487 // note that GDK generates triple click events which are not supported
1488 // by wxWidgets but still have to be passed to the app as otherwise
1489 // clicks would simply go missing
1490 switch (gdk_event->type)
1491 {
1492 // we shouldn't get triple clicks at all for GTK2 because we
1493 // suppress them artificially using the code above but we still
1494 // should map them to something for GTK1 and not just ignore them
1495 // as this would lose clicks
1496 case GDK_3BUTTON_PRESS: // we could also map this to DCLICK...
1497 case GDK_BUTTON_PRESS:
1498 event_type = wxEVT_LEFT_DOWN;
1499 break;
1500
1501 case GDK_2BUTTON_PRESS:
1502 event_type = wxEVT_LEFT_DCLICK;
1503 break;
1504
1505 default:
1506 // just to silence gcc warnings
1507 ;
1508 }
1509 }
1510 else if (gdk_event->button == 2)
1511 {
1512 switch (gdk_event->type)
1513 {
1514 case GDK_3BUTTON_PRESS:
1515 case GDK_BUTTON_PRESS:
1516 event_type = wxEVT_MIDDLE_DOWN;
1517 break;
1518
1519 case GDK_2BUTTON_PRESS:
1520 event_type = wxEVT_MIDDLE_DCLICK;
1521 break;
1522
1523 default:
1524 ;
1525 }
1526 }
1527 else if (gdk_event->button == 3)
1528 {
1529 switch (gdk_event->type)
1530 {
1531 case GDK_3BUTTON_PRESS:
1532 case GDK_BUTTON_PRESS:
1533 event_type = wxEVT_RIGHT_DOWN;
1534 break;
1535
1536 case GDK_2BUTTON_PRESS:
1537 event_type = wxEVT_RIGHT_DCLICK;
1538 break;
1539
1540 default:
1541 ;
1542 }
1543 }
1544 else if (gdk_event->button == 4 || gdk_event->button == 5)
1545 {
1546 if (gdk_event->type == GDK_BUTTON_PRESS )
1547 {
1548 event_type = wxEVT_MOUSEWHEEL;
1549 }
1550 }
1551
1552 if ( event_type == wxEVT_NULL )
1553 {
1554 // unknown mouse button or click type
1555 return FALSE;
1556 }
1557
1558 g_lastMouseEvent = (GdkEvent*) gdk_event;
1559
1560 wxMouseEvent event( event_type );
1561 InitMouseEvent( win, event, gdk_event );
1562
1563 AdjustEventButtonState(event);
1564
1565 // wxListBox actually gets mouse events from the item, so we need to give it
1566 // a chance to correct this
1567 win->FixUpMouseEvent(widget, event.m_x, event.m_y);
1568
1569 // find the correct window to send the event to: it may be a different one
1570 // from the one which got it at GTK+ level because some controls don't have
1571 // their own X window and thus cannot get any events.
1572 if ( !g_captureWindow )
1573 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1574
1575 // reset the event object and id in case win changed.
1576 event.SetEventObject( win );
1577 event.SetId( win->GetId() );
1578
1579 bool ret = win->GTKProcessEvent( event );
1580 g_lastMouseEvent = NULL;
1581 if ( ret )
1582 return TRUE;
1583
1584 if ((event_type == wxEVT_LEFT_DOWN) && !win->IsOfStandardClass() &&
1585 (g_focusWindow != win) && win->CanAcceptFocus())
1586 {
1587 gtk_widget_grab_focus( win->m_wxwindow );
1588 }
1589
1590 if (event_type == wxEVT_RIGHT_DOWN)
1591 {
1592 // generate a "context menu" event: this is similar to right mouse
1593 // click under many GUIs except that it is generated differently
1594 // (right up under MSW, ctrl-click under Mac, right down here) and
1595 //
1596 // (a) it's a command event and so is propagated to the parent
1597 // (b) under some ports it can be generated from kbd too
1598 // (c) it uses screen coords (because of (a))
1599 wxContextMenuEvent evtCtx(
1600 wxEVT_CONTEXT_MENU,
1601 win->GetId(),
1602 win->ClientToScreen(event.GetPosition()));
1603 evtCtx.SetEventObject(win);
1604 return win->GTKProcessEvent(evtCtx);
1605 }
1606
1607 return FALSE;
1608 }
1609
1610 //-----------------------------------------------------------------------------
1611 // "button_release_event"
1612 //-----------------------------------------------------------------------------
1613
1614 static gboolean
1615 gtk_window_button_release_callback( GtkWidget *widget,
1616 GdkEventButton *gdk_event,
1617 wxWindowGTK *win )
1618 {
1619 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1620
1621 g_lastButtonNumber = 0;
1622
1623 wxEventType event_type = wxEVT_NULL;
1624
1625 switch (gdk_event->button)
1626 {
1627 case 1:
1628 event_type = wxEVT_LEFT_UP;
1629 break;
1630
1631 case 2:
1632 event_type = wxEVT_MIDDLE_UP;
1633 break;
1634
1635 case 3:
1636 event_type = wxEVT_RIGHT_UP;
1637 break;
1638
1639 default:
1640 // unknown button, don't process
1641 return FALSE;
1642 }
1643
1644 g_lastMouseEvent = (GdkEvent*) gdk_event;
1645
1646 wxMouseEvent event( event_type );
1647 InitMouseEvent( win, event, gdk_event );
1648
1649 AdjustEventButtonState(event);
1650
1651 // same wxListBox hack as above
1652 win->FixUpMouseEvent(widget, event.m_x, event.m_y);
1653
1654 if ( !g_captureWindow )
1655 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1656
1657 // reset the event object and id in case win changed.
1658 event.SetEventObject( win );
1659 event.SetId( win->GetId() );
1660
1661 bool ret = win->GTKProcessEvent(event);
1662
1663 g_lastMouseEvent = NULL;
1664
1665 return ret;
1666 }
1667
1668 //-----------------------------------------------------------------------------
1669 // "motion_notify_event"
1670 //-----------------------------------------------------------------------------
1671
1672 static gboolean
1673 gtk_window_motion_notify_callback( GtkWidget *widget,
1674 GdkEventMotion *gdk_event,
1675 wxWindowGTK *win )
1676 {
1677 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1678
1679 if (gdk_event->is_hint)
1680 {
1681 int x = 0;
1682 int y = 0;
1683 GdkModifierType state;
1684 gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
1685 gdk_event->x = x;
1686 gdk_event->y = y;
1687 }
1688
1689 g_lastMouseEvent = (GdkEvent*) gdk_event;
1690
1691 wxMouseEvent event( wxEVT_MOTION );
1692 InitMouseEvent(win, event, gdk_event);
1693
1694 if ( g_captureWindow )
1695 {
1696 // synthesise a mouse enter or leave event if needed
1697 GdkWindow *winUnderMouse = gdk_window_at_pointer(NULL, NULL);
1698 // This seems to be necessary and actually been added to
1699 // GDK itself in version 2.0.X
1700 gdk_flush();
1701
1702 bool hasMouse = winUnderMouse == gdk_event->window;
1703 if ( hasMouse != g_captureWindowHasMouse )
1704 {
1705 // the mouse changed window
1706 g_captureWindowHasMouse = hasMouse;
1707
1708 wxMouseEvent eventM(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW
1709 : wxEVT_LEAVE_WINDOW);
1710 InitMouseEvent(win, eventM, gdk_event);
1711 eventM.SetEventObject(win);
1712 win->GTKProcessEvent(eventM);
1713 }
1714 }
1715 else // no capture
1716 {
1717 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1718
1719 // reset the event object and id in case win changed.
1720 event.SetEventObject( win );
1721 event.SetId( win->GetId() );
1722 }
1723
1724 if ( !g_captureWindow )
1725 {
1726 wxSetCursorEvent cevent( event.m_x, event.m_y );
1727 if (win->GTKProcessEvent( cevent ))
1728 {
1729 win->SetCursor( cevent.GetCursor() );
1730 }
1731 }
1732
1733 bool ret = win->GTKProcessEvent(event);
1734
1735 g_lastMouseEvent = NULL;
1736
1737 return ret;
1738 }
1739
1740 //-----------------------------------------------------------------------------
1741 // "scroll_event", (mouse wheel event)
1742 //-----------------------------------------------------------------------------
1743
1744 static gboolean
1745 window_scroll_event(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
1746 {
1747 DEBUG_MAIN_THREAD
1748
1749 if (gdk_event->direction != GDK_SCROLL_UP &&
1750 gdk_event->direction != GDK_SCROLL_DOWN)
1751 {
1752 return false;
1753 }
1754
1755 wxMouseEvent event(wxEVT_MOUSEWHEEL);
1756 // Can't use InitMouse macro because scroll events don't have button
1757 event.SetTimestamp( gdk_event->time );
1758 event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK);
1759 event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK);
1760 event.m_altDown = (gdk_event->state & GDK_MOD1_MASK);
1761 event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK);
1762 event.m_leftDown = (gdk_event->state & GDK_BUTTON1_MASK);
1763 event.m_middleDown = (gdk_event->state & GDK_BUTTON2_MASK);
1764 event.m_rightDown = (gdk_event->state & GDK_BUTTON3_MASK);
1765 event.m_linesPerAction = 3;
1766 event.m_wheelDelta = 120;
1767 if (gdk_event->direction == GDK_SCROLL_UP)
1768 event.m_wheelRotation = 120;
1769 else
1770 event.m_wheelRotation = -120;
1771
1772 wxPoint pt = win->GetClientAreaOrigin();
1773 event.m_x = (wxCoord)gdk_event->x - pt.x;
1774 event.m_y = (wxCoord)gdk_event->y - pt.y;
1775
1776 event.SetEventObject( win );
1777 event.SetId( win->GetId() );
1778 event.SetTimestamp( gdk_event->time );
1779
1780 return win->GTKProcessEvent(event);
1781 }
1782
1783 //-----------------------------------------------------------------------------
1784 // "popup-menu"
1785 //-----------------------------------------------------------------------------
1786
1787 static gboolean wxgtk_window_popup_menu_callback(GtkWidget*, wxWindowGTK* win)
1788 {
1789 wxContextMenuEvent event(wxEVT_CONTEXT_MENU, win->GetId(), wxPoint(-1, -1));
1790 event.SetEventObject(win);
1791 return win->GTKProcessEvent(event);
1792 }
1793
1794 //-----------------------------------------------------------------------------
1795 // "focus_in_event"
1796 //-----------------------------------------------------------------------------
1797
1798 static gboolean
1799 gtk_window_focus_in_callback( GtkWidget *widget,
1800 GdkEventFocus *WXUNUSED(event),
1801 wxWindow *win )
1802 {
1803 DEBUG_MAIN_THREAD
1804
1805 if (win->m_imData)
1806 gtk_im_context_focus_in(win->m_imData->context);
1807
1808 g_focusWindowLast =
1809 g_focusWindow = win;
1810
1811 wxLogTrace(TRACE_FOCUS,
1812 _T("%s: focus in"), win->GetName().c_str());
1813
1814 #if wxUSE_CARET
1815 // caret needs to be informed about focus change
1816 wxCaret *caret = win->GetCaret();
1817 if ( caret )
1818 {
1819 caret->OnSetFocus();
1820 }
1821 #endif // wxUSE_CARET
1822
1823 gboolean ret = FALSE;
1824
1825 // does the window itself think that it has the focus?
1826 if ( !win->m_hasFocus )
1827 {
1828 // not yet, notify it
1829 win->m_hasFocus = true;
1830
1831 (void)DoSendFocusEvents(win);
1832
1833 ret = TRUE;
1834 }
1835
1836 // Disable default focus handling for custom windows
1837 // since the default GTK+ handler issues a repaint
1838 if (win->m_wxwindow)
1839 return ret;
1840
1841 return FALSE;
1842 }
1843
1844 //-----------------------------------------------------------------------------
1845 // "focus_out_event"
1846 //-----------------------------------------------------------------------------
1847
1848 static gboolean
1849 gtk_window_focus_out_callback( GtkWidget *widget,
1850 GdkEventFocus *gdk_event,
1851 wxWindowGTK *win )
1852 {
1853 DEBUG_MAIN_THREAD
1854
1855 if (win->m_imData)
1856 gtk_im_context_focus_out(win->m_imData->context);
1857
1858 wxLogTrace( TRACE_FOCUS,
1859 _T("%s: focus out"), win->GetName().c_str() );
1860
1861
1862 wxWindowGTK *winFocus = wxFindFocusedChild(win);
1863 if ( winFocus )
1864 win = winFocus;
1865
1866 g_focusWindow = (wxWindowGTK *)NULL;
1867
1868 #if wxUSE_CARET
1869 // caret needs to be informed about focus change
1870 wxCaret *caret = win->GetCaret();
1871 if ( caret )
1872 {
1873 caret->OnKillFocus();
1874 }
1875 #endif // wxUSE_CARET
1876
1877 // don't send the window a kill focus event if it thinks that it doesn't
1878 // have focus already
1879 if ( win->m_hasFocus )
1880 {
1881 // the event handler might delete the window when it loses focus, so
1882 // check whether this is a custom window before calling it
1883 const bool has_wxwindow = win->m_wxwindow != NULL;
1884
1885 win->m_hasFocus = false;
1886
1887 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
1888 event.SetEventObject( win );
1889
1890 (void)win->GTKProcessEvent( event );
1891
1892 // Disable default focus handling for custom windows
1893 // since the default GTK+ handler issues a repaint
1894 if ( has_wxwindow )
1895 return TRUE;
1896 }
1897
1898 // continue with normal processing
1899 return FALSE;
1900 }
1901
1902 static gboolean
1903 wx_window_focus_callback(GtkWidget *widget,
1904 GtkDirectionType direction,
1905 wxWindowGTK *win)
1906 {
1907 // the default handler for focus signal in GtkPizza (or, rather, in
1908 // GtkScrolledWindow from which GtkPizza inherits this behaviour) sets
1909 // focus to the window itself even if it doesn't accept focus, i.e. has no
1910 // GTK_CAN_FOCUS in its style -- work around this by forcibly preventing
1911 // the signal from reaching gtk_scrolled_window_focus() if we don't have
1912 // any children which might accept focus (we know we don't accept the focus
1913 // ourselves as this signal is only connected in this case)
1914 if ( win->GetChildren().empty() )
1915 g_signal_stop_emission_by_name(widget, "focus");
1916
1917 // we didn't change the focus
1918 return FALSE;
1919 }
1920
1921 //-----------------------------------------------------------------------------
1922 // "enter_notify_event"
1923 //-----------------------------------------------------------------------------
1924
1925 static gboolean
1926 gtk_window_enter_callback( GtkWidget *widget,
1927 GdkEventCrossing *gdk_event,
1928 wxWindowGTK *win )
1929 {
1930 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1931
1932 // Event was emitted after a grab
1933 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
1934
1935 int x = 0;
1936 int y = 0;
1937 GdkModifierType state = (GdkModifierType)0;
1938
1939 gdk_window_get_pointer( widget->window, &x, &y, &state );
1940
1941 wxMouseEvent event( wxEVT_ENTER_WINDOW );
1942 InitMouseEvent(win, event, gdk_event);
1943 wxPoint pt = win->GetClientAreaOrigin();
1944 event.m_x = x + pt.x;
1945 event.m_y = y + pt.y;
1946
1947 if ( !g_captureWindow )
1948 {
1949 wxSetCursorEvent cevent( event.m_x, event.m_y );
1950 if (win->GTKProcessEvent( cevent ))
1951 {
1952 win->SetCursor( cevent.GetCursor() );
1953 }
1954 }
1955
1956 return win->GTKProcessEvent(event);
1957 }
1958
1959 //-----------------------------------------------------------------------------
1960 // "leave_notify_event"
1961 //-----------------------------------------------------------------------------
1962
1963 static gboolean
1964 gtk_window_leave_callback( GtkWidget *widget,
1965 GdkEventCrossing *gdk_event,
1966 wxWindowGTK *win )
1967 {
1968 wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
1969
1970 // Event was emitted after an ungrab
1971 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
1972
1973 wxMouseEvent event( wxEVT_LEAVE_WINDOW );
1974 event.SetTimestamp( gdk_event->time );
1975 event.SetEventObject( win );
1976
1977 int x = 0;
1978 int y = 0;
1979 GdkModifierType state = (GdkModifierType)0;
1980
1981 gdk_window_get_pointer( widget->window, &x, &y, &state );
1982
1983 event.m_shiftDown = (state & GDK_SHIFT_MASK) != 0;
1984 event.m_controlDown = (state & GDK_CONTROL_MASK) != 0;
1985 event.m_altDown = (state & GDK_MOD1_MASK) != 0;
1986 event.m_metaDown = (state & GDK_MOD2_MASK) != 0;
1987 event.m_leftDown = (state & GDK_BUTTON1_MASK) != 0;
1988 event.m_middleDown = (state & GDK_BUTTON2_MASK) != 0;
1989 event.m_rightDown = (state & GDK_BUTTON3_MASK) != 0;
1990
1991 wxPoint pt = win->GetClientAreaOrigin();
1992 event.m_x = x + pt.x;
1993 event.m_y = y + pt.y;
1994
1995 return win->GTKProcessEvent(event);
1996 }
1997
1998 //-----------------------------------------------------------------------------
1999 // "value_changed" from scrollbar
2000 //-----------------------------------------------------------------------------
2001
2002 static void
2003 gtk_scrollbar_value_changed(GtkRange* range, wxWindow* win)
2004 {
2005 wxEventType eventType = win->GetScrollEventType(range);
2006 if (eventType != wxEVT_NULL)
2007 {
2008 // Convert scroll event type to scrollwin event type
2009 eventType += wxEVT_SCROLLWIN_TOP - wxEVT_SCROLL_TOP;
2010
2011 // find the scrollbar which generated the event
2012 wxWindowGTK::ScrollDir dir = win->ScrollDirFromRange(range);
2013
2014 // generate the corresponding wx event
2015 const int orient = wxWindow::OrientFromScrollDir(dir);
2016 wxScrollWinEvent event(eventType, win->GetScrollPos(orient), orient);
2017 event.SetEventObject(win);
2018
2019 win->m_blockValueChanged[dir] = true;
2020 win->GTKProcessEvent(event);
2021 win->m_blockValueChanged[dir] = false;
2022 }
2023 }
2024
2025 //-----------------------------------------------------------------------------
2026 // "button_press_event" from scrollbar
2027 //-----------------------------------------------------------------------------
2028
2029 static gboolean
2030 gtk_scrollbar_button_press_event(GtkRange*, GdkEventButton*, wxWindow* win)
2031 {
2032 DEBUG_MAIN_THREAD
2033
2034 g_blockEventsOnScroll = true;
2035 win->m_mouseButtonDown = true;
2036
2037 return false;
2038 }
2039
2040 //-----------------------------------------------------------------------------
2041 // "event_after" from scrollbar
2042 //-----------------------------------------------------------------------------
2043
2044 static void
2045 gtk_scrollbar_event_after(GtkRange* range, GdkEvent* event, wxWindow* win)
2046 {
2047 if (event->type == GDK_BUTTON_RELEASE)
2048 {
2049 g_signal_handlers_block_by_func(range, (void*)gtk_scrollbar_event_after, win);
2050
2051 const int orient = wxWindow::OrientFromScrollDir(
2052 win->ScrollDirFromRange(range));
2053 wxScrollWinEvent event(wxEVT_SCROLLWIN_THUMBRELEASE, win->GetScrollPos(orient), orient);
2054 event.SetEventObject(win);
2055 win->GTKProcessEvent(event);
2056 }
2057 }
2058
2059 //-----------------------------------------------------------------------------
2060 // "button_release_event" from scrollbar
2061 //-----------------------------------------------------------------------------
2062
2063 static gboolean
2064 gtk_scrollbar_button_release_event(GtkRange* range, GdkEventButton*, wxWindow* win)
2065 {
2066 DEBUG_MAIN_THREAD
2067
2068 g_blockEventsOnScroll = false;
2069 win->m_mouseButtonDown = false;
2070 // If thumb tracking
2071 if (win->m_isScrolling)
2072 {
2073 win->m_isScrolling = false;
2074 // Hook up handler to send thumb release event after this emission is finished.
2075 // To allow setting scroll position from event handler, sending event must
2076 // be deferred until after the GtkRange handler for this signal has run
2077 g_signal_handlers_unblock_by_func(range, (void*)gtk_scrollbar_event_after, win);
2078 }
2079
2080 return false;
2081 }
2082
2083 //-----------------------------------------------------------------------------
2084 // "realize" from m_widget
2085 //-----------------------------------------------------------------------------
2086
2087 static void
2088 gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win )
2089 {
2090 DEBUG_MAIN_THREAD
2091
2092 if (win->m_imData)
2093 {
2094 GtkPizza *pizza = GTK_PIZZA( m_widget );
2095 gtk_im_context_set_client_window( win->m_imData->context,
2096 pizza->bin_window );
2097 }
2098
2099 // We cannot set colours and fonts before the widget
2100 // been realized, so we do this directly after realization
2101 // or otherwise in idle time
2102
2103 if (win->m_needsStyleChange)
2104 {
2105 win->SetBackgroundStyle(win->GetBackgroundStyle());
2106 win->m_needsStyleChange = false;
2107 }
2108
2109 wxWindowCreateEvent event( win );
2110 event.SetEventObject( win );
2111 win->GTKProcessEvent( event );
2112 }
2113
2114 //-----------------------------------------------------------------------------
2115 // "size_allocate"
2116 //-----------------------------------------------------------------------------
2117
2118 static
2119 void gtk_window_size_callback( GtkWidget *WXUNUSED(widget),
2120 GtkAllocation *alloc,
2121 wxWindow *win )
2122 {
2123 int client_width = 0;
2124 int client_height = 0;
2125 win->GetClientSize( &client_width, &client_height );
2126 if ((client_width == win->m_oldClientWidth) && (client_height == win->m_oldClientHeight))
2127 return;
2128
2129 if ( !client_width && !client_height )
2130 {
2131 // the window is currently unmapped, don't generate size events
2132 return;
2133 }
2134
2135 win->m_oldClientWidth = client_width;
2136 win->m_oldClientHeight = client_height;
2137
2138 if (!win->m_nativeSizeEvent)
2139 {
2140 wxSizeEvent event( win->GetSize(), win->GetId() );
2141 event.SetEventObject( win );
2142 win->GTKProcessEvent( event );
2143 }
2144 }
2145
2146 } // extern "C"
2147
2148 // ----------------------------------------------------------------------------
2149 // this wxWindowBase function is implemented here (in platform-specific file)
2150 // because it is static and so couldn't be made virtual
2151 // ----------------------------------------------------------------------------
2152
2153 wxWindow *wxWindowBase::DoFindFocus()
2154 {
2155 // the cast is necessary when we compile in wxUniversal mode
2156 return (wxWindow *)g_focusWindow;
2157 }
2158
2159 //-----------------------------------------------------------------------------
2160 // InsertChild for wxWindowGTK.
2161 //-----------------------------------------------------------------------------
2162
2163 /* Callback for wxWindowGTK. This very strange beast has to be used because
2164 * C++ has no virtual methods in a constructor. We have to emulate a
2165 * virtual function here as wxNotebook requires a different way to insert
2166 * a child in it. I had opted for creating a wxNotebookPage window class
2167 * which would have made this superfluous (such in the MDI window system),
2168 * but no-one was listening to me... */
2169
2170 static void wxInsertChildInWindow( wxWindowGTK* parent, wxWindowGTK* child )
2171 {
2172 /* the window might have been scrolled already, do we
2173 have to adapt the position */
2174 GtkPizza *pizza = GTK_PIZZA(parent->m_wxwindow);
2175 child->m_x += gtk_pizza_get_xoffset( pizza );
2176 child->m_y += gtk_pizza_get_yoffset( pizza );
2177
2178 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
2179 child->m_widget,
2180 child->m_x,
2181 child->m_y,
2182 child->m_width,
2183 child->m_height );
2184 }
2185
2186 //-----------------------------------------------------------------------------
2187 // global functions
2188 //-----------------------------------------------------------------------------
2189
2190 wxWindow *wxGetActiveWindow()
2191 {
2192 return wxWindow::FindFocus();
2193 }
2194
2195
2196 wxMouseState wxGetMouseState()
2197 {
2198 wxMouseState ms;
2199
2200 gint x;
2201 gint y;
2202 GdkModifierType mask;
2203
2204 gdk_window_get_pointer(NULL, &x, &y, &mask);
2205
2206 ms.SetX(x);
2207 ms.SetY(y);
2208 ms.SetLeftDown(mask & GDK_BUTTON1_MASK);
2209 ms.SetMiddleDown(mask & GDK_BUTTON2_MASK);
2210 ms.SetRightDown(mask & GDK_BUTTON3_MASK);
2211
2212 ms.SetControlDown(mask & GDK_CONTROL_MASK);
2213 ms.SetShiftDown(mask & GDK_SHIFT_MASK);
2214 ms.SetAltDown(mask & GDK_MOD1_MASK);
2215 ms.SetMetaDown(mask & GDK_MOD2_MASK);
2216
2217 return ms;
2218 }
2219
2220 //-----------------------------------------------------------------------------
2221 // wxWindowGTK
2222 //-----------------------------------------------------------------------------
2223
2224 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2225 // method
2226 #ifdef __WXUNIVERSAL__
2227 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK, wxWindowBase)
2228 #else // __WXGTK__
2229 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
2230 #endif // __WXUNIVERSAL__/__WXGTK__
2231
2232 void wxWindowGTK::Init()
2233 {
2234 // GTK specific
2235 m_widget = (GtkWidget *) NULL;
2236 m_wxwindow = (GtkWidget *) NULL;
2237 m_focusWidget = (GtkWidget *) NULL;
2238
2239 // position/size
2240 m_x = 0;
2241 m_y = 0;
2242 m_width = 0;
2243 m_height = 0;
2244
2245 m_sizeSet = false;
2246 m_hasVMT = false;
2247 m_isBeingDeleted = false;
2248
2249 m_showOnIdle= false;
2250
2251 m_noExpose = false;
2252 m_nativeSizeEvent = false;
2253
2254 m_hasScrolling = false;
2255 m_isScrolling = false;
2256 m_mouseButtonDown = false;
2257 m_blockScrollEvent = false;
2258
2259 // initialize scrolling stuff
2260 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2261 {
2262 m_scrollBar[dir] = NULL;
2263 m_scrollPos[dir] = 0;
2264 m_blockValueChanged[dir] = false;
2265 }
2266
2267 m_oldClientWidth =
2268 m_oldClientHeight = 0;
2269
2270 m_resizing = false;
2271
2272 m_insertCallback = (wxInsertChildFunction) NULL;
2273
2274 m_hasFocus = false;
2275
2276 m_clipPaintRegion = false;
2277
2278 m_needsStyleChange = false;
2279
2280 m_cursor = *wxSTANDARD_CURSOR;
2281
2282 m_imData = NULL;
2283 m_dirtyTabOrder = false;
2284 }
2285
2286 wxWindowGTK::wxWindowGTK()
2287 {
2288 Init();
2289 }
2290
2291 wxWindowGTK::wxWindowGTK( wxWindow *parent,
2292 wxWindowID id,
2293 const wxPoint &pos,
2294 const wxSize &size,
2295 long style,
2296 const wxString &name )
2297 {
2298 Init();
2299
2300 Create( parent, id, pos, size, style, name );
2301 }
2302
2303 bool wxWindowGTK::Create( wxWindow *parent,
2304 wxWindowID id,
2305 const wxPoint &pos,
2306 const wxSize &size,
2307 long style,
2308 const wxString &name )
2309 {
2310 if (!PreCreation( parent, pos, size ) ||
2311 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
2312 {
2313 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2314 return false;
2315 }
2316
2317 m_insertCallback = wxInsertChildInWindow;
2318
2319
2320 if (!HasFlag(wxHSCROLL) && !HasFlag(wxVSCROLL))
2321 {
2322 m_wxwindow = gtk_pizza_new_no_scroll();
2323
2324 #ifndef __WXUNIVERSAL__
2325 if (HasFlag(wxSIMPLE_BORDER))
2326 gtk_container_set_border_width((GtkContainer*)m_wxwindow, 1);
2327 else if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
2328 gtk_container_set_border_width((GtkContainer*)m_wxwindow, 2);
2329 #endif // __WXUNIVERSAL__
2330
2331 m_widget = m_wxwindow;
2332 }
2333 else
2334 {
2335 m_wxwindow = gtk_pizza_new();
2336
2337 #ifndef __WXUNIVERSAL__
2338 if (HasFlag(wxSIMPLE_BORDER))
2339 gtk_container_set_border_width((GtkContainer*)m_wxwindow, 1);
2340 else if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
2341 gtk_container_set_border_width((GtkContainer*)m_wxwindow, 2);
2342 #endif // __WXUNIVERSAL__
2343
2344 m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
2345
2346 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
2347
2348 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
2349 scroll_class->scrollbar_spacing = 0;
2350
2351 // There is a conflict with default bindings at GTK+
2352 // level between scrolled windows and notebooks both of which want to use
2353 // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
2354 // direction and notebooks for changing pages -- we decide that if we don't
2355 // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
2356 // means we can get working keyboard navigation in notebooks
2357 if ( !HasFlag(wxHSCROLL) )
2358 {
2359 GtkBindingSet *
2360 bindings = gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget));
2361 if ( bindings )
2362 {
2363 gtk_binding_entry_remove(bindings, GDK_Page_Up, GDK_CONTROL_MASK);
2364 gtk_binding_entry_remove(bindings, GDK_Page_Down, GDK_CONTROL_MASK);
2365 }
2366 }
2367
2368 if (HasFlag(wxALWAYS_SHOW_SB))
2369 {
2370 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS );
2371
2372 scrolledWindow->hscrollbar_visible = TRUE;
2373 scrolledWindow->vscrollbar_visible = TRUE;
2374 }
2375 else
2376 {
2377 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
2378 }
2379
2380 m_scrollBar[ScrollDir_Horz] = GTK_RANGE(scrolledWindow->hscrollbar);
2381 m_scrollBar[ScrollDir_Vert] = GTK_RANGE(scrolledWindow->vscrollbar);
2382 if (GetLayoutDirection() == wxLayout_RightToLeft)
2383 gtk_range_set_inverted( m_scrollBar[ScrollDir_Horz], TRUE );
2384
2385 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
2386
2387 // connect various scroll-related events
2388 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
2389 {
2390 // these handlers block mouse events to any window during scrolling
2391 // such as motion events and prevent GTK and wxWidgets from fighting
2392 // over where the slider should be
2393 g_signal_connect(m_scrollBar[dir], "button_press_event",
2394 G_CALLBACK(gtk_scrollbar_button_press_event), this);
2395 g_signal_connect(m_scrollBar[dir], "button_release_event",
2396 G_CALLBACK(gtk_scrollbar_button_release_event), this);
2397
2398 gulong handler_id = g_signal_connect(m_scrollBar[dir], "event_after",
2399 G_CALLBACK(gtk_scrollbar_event_after), this);
2400 g_signal_handler_block(m_scrollBar[dir], handler_id);
2401
2402 // these handlers get notified when scrollbar slider moves
2403 g_signal_connect(m_scrollBar[dir], "value_changed",
2404 G_CALLBACK(gtk_scrollbar_value_changed), this);
2405 }
2406
2407 gtk_widget_show( m_wxwindow );
2408 }
2409
2410 if (m_parent)
2411 m_parent->DoAddChild( this );
2412
2413 m_focusWidget = m_wxwindow;
2414
2415 PostCreation();
2416
2417 return true;
2418 }
2419
2420 wxWindowGTK::~wxWindowGTK()
2421 {
2422 SendDestroyEvent();
2423
2424 if (g_focusWindow == this)
2425 g_focusWindow = NULL;
2426
2427 if ( g_delayedFocus == this )
2428 g_delayedFocus = NULL;
2429
2430 m_isBeingDeleted = true;
2431 m_hasVMT = false;
2432
2433 // destroy children before destroying this window itself
2434 DestroyChildren();
2435
2436 // unhook focus handlers to prevent stray events being
2437 // propagated to this (soon to be) dead object
2438 if (m_focusWidget != NULL)
2439 {
2440 g_signal_handlers_disconnect_by_func (m_focusWidget,
2441 (gpointer) gtk_window_focus_in_callback,
2442 this);
2443 g_signal_handlers_disconnect_by_func (m_focusWidget,
2444 (gpointer) gtk_window_focus_out_callback,
2445 this);
2446 }
2447
2448 if (m_widget)
2449 Show( false );
2450
2451 // delete before the widgets to avoid a crash on solaris
2452 delete m_imData;
2453
2454 if (m_wxwindow && (m_wxwindow != m_widget))
2455 {
2456 gtk_widget_destroy( m_wxwindow );
2457 m_wxwindow = (GtkWidget*) NULL;
2458 }
2459
2460 if (m_widget)
2461 {
2462 gtk_widget_destroy( m_widget );
2463 m_widget = (GtkWidget*) NULL;
2464 }
2465 }
2466
2467 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
2468 {
2469 if ( GTKNeedsParent() )
2470 {
2471 wxCHECK_MSG( parent, false, wxT("Must have non-NULL parent") );
2472 }
2473
2474 // Use either the given size, or the default if -1 is given.
2475 // See wxWindowBase for these functions.
2476 m_width = WidthDefault(size.x) ;
2477 m_height = HeightDefault(size.y);
2478
2479 m_x = (int)pos.x;
2480 m_y = (int)pos.y;
2481
2482 return true;
2483 }
2484
2485 void wxWindowGTK::PostCreation()
2486 {
2487 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2488
2489 if (m_wxwindow)
2490 {
2491 if (!m_noExpose)
2492 {
2493 // these get reported to wxWidgets -> wxPaintEvent
2494
2495 g_signal_connect (m_wxwindow, "expose_event",
2496 G_CALLBACK (gtk_window_expose_callback), this);
2497
2498 if (GetLayoutDirection() == wxLayout_LeftToRight)
2499 gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), HasFlag( wxFULL_REPAINT_ON_RESIZE ) );
2500 }
2501
2502 // Create input method handler
2503 m_imData = new wxGtkIMData;
2504
2505 // Cannot handle drawing preedited text yet
2506 gtk_im_context_set_use_preedit( m_imData->context, FALSE );
2507
2508 g_signal_connect (m_imData->context, "commit",
2509 G_CALLBACK (gtk_wxwindow_commit_cb), this);
2510
2511 // these are called when the "sunken" or "raised" borders are drawn
2512 g_signal_connect (m_widget, "expose_event",
2513 G_CALLBACK (gtk_window_own_expose_callback), this);
2514 }
2515
2516 // focus handling
2517
2518 if (!GTK_IS_WINDOW(m_widget))
2519 {
2520 if (m_focusWidget == NULL)
2521 m_focusWidget = m_widget;
2522
2523 if (m_wxwindow)
2524 {
2525 g_signal_connect (m_focusWidget, "focus_in_event",
2526 G_CALLBACK (gtk_window_focus_in_callback), this);
2527 g_signal_connect (m_focusWidget, "focus_out_event",
2528 G_CALLBACK (gtk_window_focus_out_callback), this);
2529 }
2530 else
2531 {
2532 g_signal_connect_after (m_focusWidget, "focus_in_event",
2533 G_CALLBACK (gtk_window_focus_in_callback), this);
2534 g_signal_connect_after (m_focusWidget, "focus_out_event",
2535 G_CALLBACK (gtk_window_focus_out_callback), this);
2536 }
2537 }
2538
2539 if ( !AcceptsFocusFromKeyboard() )
2540 {
2541 SetCanFocus(false);
2542
2543 g_signal_connect(m_widget, "focus",
2544 G_CALLBACK(wx_window_focus_callback), this);
2545 }
2546
2547 // connect to the various key and mouse handlers
2548
2549 GtkWidget *connect_widget = GetConnectWidget();
2550
2551 ConnectWidget( connect_widget );
2552
2553 /* We cannot set colours, fonts and cursors before the widget has
2554 been realized, so we do this directly after realization */
2555 g_signal_connect (connect_widget, "realize",
2556 G_CALLBACK (gtk_window_realized_callback), this);
2557
2558 if (m_wxwindow)
2559 {
2560 // Catch native resize events
2561 g_signal_connect (m_wxwindow, "size_allocate",
2562 G_CALLBACK (gtk_window_size_callback), this);
2563 }
2564
2565 if (GTK_IS_COMBO(m_widget))
2566 {
2567 GtkCombo *gcombo = GTK_COMBO(m_widget);
2568
2569 g_signal_connect (gcombo->entry, "size_request",
2570 G_CALLBACK (wxgtk_combo_size_request_callback),
2571 this);
2572 }
2573 #ifdef GTK_IS_FILE_CHOOSER_BUTTON
2574 else if (!gtk_check_version(2,6,0) && GTK_IS_FILE_CHOOSER_BUTTON(m_widget))
2575 {
2576 // If we connect to the "size_request" signal of a GtkFileChooserButton
2577 // then that control won't be sized properly when placed inside sizers
2578 // (this can be tested removing this elseif and running XRC or WIDGETS samples)
2579 // FIXME: what should be done here ?
2580 }
2581 #endif
2582 else if ( !IsTopLevel() ) // top level windows use their own callback
2583 {
2584 // This is needed if we want to add our windows into native
2585 // GTK controls, such as the toolbar. With this callback, the
2586 // toolbar gets to know the correct size (the one set by the
2587 // programmer). Sadly, it misbehaves for wxComboBox.
2588 g_signal_connect (m_widget, "size_request",
2589 G_CALLBACK (wxgtk_window_size_request_callback),
2590 this);
2591 }
2592
2593 InheritAttributes();
2594
2595 m_hasVMT = true;
2596
2597 SetLayoutDirection(wxLayout_Default);
2598
2599 // unless the window was created initially hidden (i.e. Hide() had been
2600 // called before Create()), we should show it at GTK+ level as well
2601 if ( IsShown() )
2602 gtk_widget_show( m_widget );
2603 }
2604
2605 void wxWindowGTK::ConnectWidget( GtkWidget *widget )
2606 {
2607 g_signal_connect (widget, "key_press_event",
2608 G_CALLBACK (gtk_window_key_press_callback), this);
2609 g_signal_connect (widget, "key_release_event",
2610 G_CALLBACK (gtk_window_key_release_callback), this);
2611 g_signal_connect (widget, "button_press_event",
2612 G_CALLBACK (gtk_window_button_press_callback), this);
2613 g_signal_connect (widget, "button_release_event",
2614 G_CALLBACK (gtk_window_button_release_callback), this);
2615 g_signal_connect (widget, "motion_notify_event",
2616 G_CALLBACK (gtk_window_motion_notify_callback), this);
2617 g_signal_connect (widget, "scroll_event",
2618 G_CALLBACK (window_scroll_event), this);
2619 g_signal_connect (widget, "popup_menu",
2620 G_CALLBACK (wxgtk_window_popup_menu_callback), this);
2621 g_signal_connect (widget, "enter_notify_event",
2622 G_CALLBACK (gtk_window_enter_callback), this);
2623 g_signal_connect (widget, "leave_notify_event",
2624 G_CALLBACK (gtk_window_leave_callback), this);
2625 }
2626
2627 bool wxWindowGTK::Destroy()
2628 {
2629 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2630
2631 m_hasVMT = false;
2632
2633 return wxWindowBase::Destroy();
2634 }
2635
2636 void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
2637 {
2638 // inform the parent to perform the move
2639 gtk_pizza_set_size( GTK_PIZZA(m_parent->m_wxwindow), m_widget, x, y, width, height );
2640
2641 }
2642
2643 void wxWindowGTK::ConstrainSize()
2644 {
2645 #ifdef __WXGPE__
2646 // GPE's window manager doesn't like size hints at all, esp. when the user
2647 // has to use the virtual keyboard, so don't constrain size there
2648 if (!IsTopLevel())
2649 #endif
2650 {
2651 const wxSize minSize = GetMinSize();
2652 const wxSize maxSize = GetMaxSize();
2653 if (minSize.x > 0 && m_width < minSize.x) m_width = minSize.x;
2654 if (minSize.y > 0 && m_height < minSize.y) m_height = minSize.y;
2655 if (maxSize.x > 0 && m_width > maxSize.x) m_width = maxSize.x;
2656 if (maxSize.y > 0 && m_height > maxSize.y) m_height = maxSize.y;
2657 }
2658 }
2659
2660 void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
2661 {
2662 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2663 wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") );
2664
2665 if (m_resizing) return; /* I don't like recursions */
2666 m_resizing = true;
2667
2668 int currentX, currentY;
2669 GetPosition(&currentX, &currentY);
2670 if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2671 x = currentX;
2672 if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2673 y = currentY;
2674 AdjustForParentClientOrigin(x, y, sizeFlags);
2675
2676 // calculate the best size if we should auto size the window
2677 if ( ((sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1) ||
2678 ((sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1) )
2679 {
2680 const wxSize sizeBest = GetBestSize();
2681 if ( (sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1 )
2682 width = sizeBest.x;
2683 if ( (sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1 )
2684 height = sizeBest.y;
2685 }
2686
2687 if (width != -1)
2688 m_width = width;
2689 if (height != -1)
2690 m_height = height;
2691
2692 ConstrainSize();
2693
2694 #if wxUSE_TOOLBAR_NATIVE
2695 if (wxDynamicCast(GetParent(), wxToolBar))
2696 {
2697 // don't take the x,y values, they're wrong because toolbar sets them
2698 GtkWidget *widget = m_widget;
2699 gtk_widget_set_size_request (widget, m_width, m_height);
2700 }
2701 else
2702 #endif
2703 if (m_parent->m_wxwindow == NULL) // i.e. wxNotebook
2704 {
2705 // don't set the size for children of wxNotebook, just take the values.
2706 m_x = x;
2707 m_y = y;
2708 m_width = width;
2709 m_height = height;
2710 }
2711 else
2712 {
2713 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
2714 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
2715 {
2716 if (x != -1) m_x = x + gtk_pizza_get_xoffset( pizza );
2717 if (y != -1) m_y = y + gtk_pizza_get_yoffset( pizza );
2718 }
2719 else
2720 {
2721 m_x = x + gtk_pizza_get_xoffset( pizza );
2722 m_y = y + gtk_pizza_get_yoffset( pizza );
2723 }
2724
2725 int left_border = 0;
2726 int right_border = 0;
2727 int top_border = 0;
2728 int bottom_border = 0;
2729
2730 /* the default button has a border around it */
2731 if (GTK_WIDGET_CAN_DEFAULT(m_widget))
2732 {
2733 GtkBorder *default_border = NULL;
2734 gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
2735 if (default_border)
2736 {
2737 left_border += default_border->left;
2738 right_border += default_border->right;
2739 top_border += default_border->top;
2740 bottom_border += default_border->bottom;
2741 g_free( default_border );
2742 }
2743 }
2744
2745 DoMoveWindow( m_x-top_border,
2746 m_y-left_border,
2747 m_width+left_border+right_border,
2748 m_height+top_border+bottom_border );
2749 }
2750
2751 if (m_hasScrolling)
2752 {
2753 /* Sometimes the client area changes size without the
2754 whole windows's size changing, but if the whole
2755 windows's size doesn't change, no wxSizeEvent will
2756 normally be sent. Here we add an extra test if
2757 the client test has been changed and this will
2758 be used then. */
2759 GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
2760 }
2761
2762 /*
2763 wxPrintf( "OnSize sent from " );
2764 if (GetClassInfo() && GetClassInfo()->GetClassName())
2765 wxPrintf( GetClassInfo()->GetClassName() );
2766 wxPrintf( " %d %d %d %d\n", (int)m_x, (int)m_y, (int)m_width, (int)m_height );
2767 */
2768
2769 if (!m_nativeSizeEvent)
2770 {
2771 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
2772 event.SetEventObject( this );
2773 GetEventHandler()->ProcessEvent( event );
2774 }
2775
2776 m_resizing = false;
2777 }
2778
2779 bool wxWindowGTK::GtkShowFromOnIdle()
2780 {
2781 if (IsShown() && m_showOnIdle && !GTK_WIDGET_VISIBLE (m_widget))
2782 {
2783 GtkAllocation alloc;
2784 alloc.x = m_x;
2785 alloc.y = m_y;
2786 alloc.width = m_width;
2787 alloc.height = m_height;
2788 gtk_widget_size_allocate( m_widget, &alloc );
2789 gtk_widget_show( m_widget );
2790 wxShowEvent eventShow(GetId(), true);
2791 eventShow.SetEventObject(this);
2792 GetEventHandler()->ProcessEvent(eventShow);
2793 m_showOnIdle = false;
2794 return true;
2795 }
2796
2797 return false;
2798 }
2799
2800 void wxWindowGTK::OnInternalIdle()
2801 {
2802 // Check if we have to show window now
2803 if (GtkShowFromOnIdle()) return;
2804
2805 if ( m_dirtyTabOrder )
2806 {
2807 m_dirtyTabOrder = false;
2808 RealizeTabOrder();
2809 }
2810
2811 // Update style if the window was not yet realized
2812 // and SetBackgroundStyle(wxBG_STYLE_CUSTOM) was called
2813 if (m_needsStyleChange)
2814 {
2815 SetBackgroundStyle(GetBackgroundStyle());
2816 m_needsStyleChange = false;
2817 }
2818
2819 wxCursor cursor = m_cursor;
2820 if (g_globalCursor.Ok()) cursor = g_globalCursor;
2821
2822 if (cursor.Ok())
2823 {
2824 /* I now set the cursor anew in every OnInternalIdle call
2825 as setting the cursor in a parent window also effects the
2826 windows above so that checking for the current cursor is
2827 not possible. */
2828
2829 if (m_wxwindow && (m_wxwindow != m_widget))
2830 {
2831 GdkWindow *window = GTK_PIZZA(m_wxwindow)->bin_window;
2832 if (window)
2833 gdk_window_set_cursor( window, cursor.GetCursor() );
2834
2835 if (!g_globalCursor.Ok())
2836 cursor = *wxSTANDARD_CURSOR;
2837
2838 window = m_widget->window;
2839 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
2840 gdk_window_set_cursor( window, cursor.GetCursor() );
2841
2842 }
2843 else if ( m_widget )
2844 {
2845 GdkWindow *window = m_widget->window;
2846 if ( window && !GTK_WIDGET_NO_WINDOW(m_widget) )
2847 gdk_window_set_cursor( window, cursor.GetCursor() );
2848 }
2849 }
2850
2851 if (wxUpdateUIEvent::CanUpdate(this))
2852 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
2853 }
2854
2855 void wxWindowGTK::DoGetSize( int *width, int *height ) const
2856 {
2857 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2858
2859 if (width) (*width) = m_width;
2860 if (height) (*height) = m_height;
2861 }
2862
2863 void wxWindowGTK::DoSetClientSize( int width, int height )
2864 {
2865 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2866
2867 if (m_wxwindow)
2868 {
2869 int dw = 0;
2870 int dh = 0;
2871
2872 if (m_hasScrolling)
2873 {
2874 GetScrollbarWidth(m_widget, dw, dh);
2875 }
2876
2877 const int border = GTK_CONTAINER(m_wxwindow)->border_width;
2878 dw += 2 * border;
2879 dh += 2 * border;
2880
2881 width += dw;
2882 height += dh;
2883 }
2884
2885 SetSize(width, height);
2886 }
2887
2888 void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
2889 {
2890 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2891
2892 int w = m_width;
2893 int h = m_height;
2894
2895 if (m_wxwindow)
2896 {
2897 int dw = 0;
2898 int dh = 0;
2899
2900 if (m_hasScrolling)
2901 GetScrollbarWidth(m_widget, dw, dh);
2902
2903 const int border = GTK_CONTAINER(m_wxwindow)->border_width;
2904 dw += 2 * border;
2905 dh += 2 * border;
2906
2907 w -= dw;
2908 h -= dh;
2909 if (w < 0)
2910 w = 0;
2911 if (h < 0)
2912 h = 0;
2913 }
2914
2915 if (width) *width = w;
2916 if (height) *height = h;
2917 }
2918
2919 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
2920 {
2921 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2922
2923 int dx = 0;
2924 int dy = 0;
2925 if (m_parent && m_parent->m_wxwindow)
2926 {
2927 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
2928 dx = gtk_pizza_get_xoffset( pizza );
2929 dy = gtk_pizza_get_yoffset( pizza );
2930 }
2931
2932 if (m_x == -1 && m_y == -1)
2933 {
2934 GdkWindow *source = (GdkWindow *) NULL;
2935 if (m_wxwindow)
2936 source = GTK_PIZZA(m_wxwindow)->bin_window;
2937 else
2938 source = m_widget->window;
2939
2940 if (source)
2941 {
2942 int org_x = 0;
2943 int org_y = 0;
2944 gdk_window_get_origin( source, &org_x, &org_y );
2945
2946 if (m_parent)
2947 m_parent->ScreenToClient(&org_x, &org_y);
2948
2949 wx_const_cast(wxWindowGTK*, this)->m_x = org_x;
2950 wx_const_cast(wxWindowGTK*, this)->m_y = org_y;
2951 }
2952 }
2953
2954 if (x) (*x) = m_x - dx;
2955 if (y) (*y) = m_y - dy;
2956 }
2957
2958 void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
2959 {
2960 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2961
2962 if (!m_widget->window) return;
2963
2964 GdkWindow *source = (GdkWindow *) NULL;
2965 if (m_wxwindow)
2966 source = GTK_PIZZA(m_wxwindow)->bin_window;
2967 else
2968 source = m_widget->window;
2969
2970 int org_x = 0;
2971 int org_y = 0;
2972 gdk_window_get_origin( source, &org_x, &org_y );
2973
2974 if (!m_wxwindow)
2975 {
2976 if (GTK_WIDGET_NO_WINDOW (m_widget))
2977 {
2978 org_x += m_widget->allocation.x;
2979 org_y += m_widget->allocation.y;
2980 }
2981 }
2982
2983
2984 if (x)
2985 {
2986 if (GetLayoutDirection() == wxLayout_RightToLeft)
2987 *x = (GetClientSize().x - *x) + org_x;
2988 else
2989 *x += org_x;
2990 }
2991
2992 if (y) *y += org_y;
2993 }
2994
2995 void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
2996 {
2997 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
2998
2999 if (!m_widget->window) return;
3000
3001 GdkWindow *source = (GdkWindow *) NULL;
3002 if (m_wxwindow)
3003 source = GTK_PIZZA(m_wxwindow)->bin_window;
3004 else
3005 source = m_widget->window;
3006
3007 int org_x = 0;
3008 int org_y = 0;
3009 gdk_window_get_origin( source, &org_x, &org_y );
3010
3011 if (!m_wxwindow)
3012 {
3013 if (GTK_WIDGET_NO_WINDOW (m_widget))
3014 {
3015 org_x += m_widget->allocation.x;
3016 org_y += m_widget->allocation.y;
3017 }
3018 }
3019
3020 if (x)
3021 {
3022 if (GetLayoutDirection() == wxLayout_RightToLeft)
3023 *x = (GetClientSize().x - *x) - org_x;
3024 else
3025 *x -= org_x;
3026 }
3027 if (y) *y -= org_y;
3028 }
3029
3030 bool wxWindowGTK::Show( bool show )
3031 {
3032 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3033
3034 if (!wxWindowBase::Show(show))
3035 {
3036 // nothing to do
3037 return false;
3038 }
3039
3040 if (show)
3041 {
3042 if (!m_showOnIdle)
3043 {
3044 gtk_widget_show( m_widget );
3045 wxShowEvent eventShow(GetId(), show);
3046 eventShow.SetEventObject(this);
3047 GetEventHandler()->ProcessEvent(eventShow);
3048 }
3049 }
3050 else
3051 {
3052 gtk_widget_hide( m_widget );
3053 wxShowEvent eventShow(GetId(), show);
3054 eventShow.SetEventObject(this);
3055 GetEventHandler()->ProcessEvent(eventShow);
3056 }
3057
3058 return true;
3059 }
3060
3061 void wxWindowGTK::DoEnable( bool enable )
3062 {
3063 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3064
3065 gtk_widget_set_sensitive( m_widget, enable );
3066 if (m_wxwindow && (m_wxwindow != m_widget))
3067 gtk_widget_set_sensitive( m_wxwindow, enable );
3068 }
3069
3070 int wxWindowGTK::GetCharHeight() const
3071 {
3072 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
3073
3074 wxFont font = GetFont();
3075 wxCHECK_MSG( font.Ok(), 12, wxT("invalid font") );
3076
3077 PangoContext *context = NULL;
3078 if (m_widget)
3079 context = gtk_widget_get_pango_context( m_widget );
3080
3081 if (!context)
3082 return 0;
3083
3084 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3085 PangoLayout *layout = pango_layout_new(context);
3086 pango_layout_set_font_description(layout, desc);
3087 pango_layout_set_text(layout, "H", 1);
3088 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3089
3090 PangoRectangle rect;
3091 pango_layout_line_get_extents(line, NULL, &rect);
3092
3093 g_object_unref (layout);
3094
3095 return (int) PANGO_PIXELS(rect.height);
3096 }
3097
3098 int wxWindowGTK::GetCharWidth() const
3099 {
3100 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
3101
3102 wxFont font = GetFont();
3103 wxCHECK_MSG( font.Ok(), 8, wxT("invalid font") );
3104
3105 PangoContext *context = NULL;
3106 if (m_widget)
3107 context = gtk_widget_get_pango_context( m_widget );
3108
3109 if (!context)
3110 return 0;
3111
3112 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3113 PangoLayout *layout = pango_layout_new(context);
3114 pango_layout_set_font_description(layout, desc);
3115 pango_layout_set_text(layout, "g", 1);
3116 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3117
3118 PangoRectangle rect;
3119 pango_layout_line_get_extents(line, NULL, &rect);
3120
3121 g_object_unref (layout);
3122
3123 return (int) PANGO_PIXELS(rect.width);
3124 }
3125
3126 void wxWindowGTK::GetTextExtent( const wxString& string,
3127 int *x,
3128 int *y,
3129 int *descent,
3130 int *externalLeading,
3131 const wxFont *theFont ) const
3132 {
3133 wxFont fontToUse = theFont ? *theFont : GetFont();
3134
3135 wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") );
3136
3137 if (string.empty())
3138 {
3139 if (x) (*x) = 0;
3140 if (y) (*y) = 0;
3141 return;
3142 }
3143
3144 PangoContext *context = NULL;
3145 if (m_widget)
3146 context = gtk_widget_get_pango_context( m_widget );
3147
3148 if (!context)
3149 {
3150 if (x) (*x) = 0;
3151 if (y) (*y) = 0;
3152 return;
3153 }
3154
3155 PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
3156 PangoLayout *layout = pango_layout_new(context);
3157 pango_layout_set_font_description(layout, desc);
3158 {
3159 const wxCharBuffer data = wxGTK_CONV( string );
3160 if ( data )
3161 pango_layout_set_text(layout, data, strlen(data));
3162 }
3163
3164 PangoRectangle rect;
3165 pango_layout_get_extents(layout, NULL, &rect);
3166
3167 if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width);
3168 if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height);
3169 if (descent)
3170 {
3171 PangoLayoutIter *iter = pango_layout_get_iter(layout);
3172 int baseline = pango_layout_iter_get_baseline(iter);
3173 pango_layout_iter_free(iter);
3174 *descent = *y - PANGO_PIXELS(baseline);
3175 }
3176 if (externalLeading) (*externalLeading) = 0; // ??
3177
3178 g_object_unref (layout);
3179 }
3180
3181 bool wxWindowGTK::GTKSetDelayedFocusIfNeeded()
3182 {
3183 if ( g_delayedFocus == this )
3184 {
3185 if ( GTK_WIDGET_REALIZED(m_widget) )
3186 {
3187 gtk_widget_grab_focus(m_widget);
3188 g_delayedFocus = NULL;
3189
3190 return true;
3191 }
3192 }
3193
3194 return false;
3195 }
3196
3197 void wxWindowGTK::SetFocus()
3198 {
3199 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3200 if ( m_hasFocus )
3201 {
3202 // don't do anything if we already have focus
3203 return;
3204 }
3205
3206 if (m_wxwindow)
3207 {
3208 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
3209 {
3210 gtk_widget_grab_focus (m_wxwindow);
3211 }
3212 }
3213 else if (m_widget)
3214 {
3215 if (GTK_IS_CONTAINER(m_widget))
3216 {
3217 if (IsKindOf(CLASSINFO(wxRadioButton)))
3218 {
3219 gtk_widget_grab_focus (m_widget);
3220 return;
3221 }
3222
3223 gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
3224 }
3225 else
3226 if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
3227 {
3228
3229 if (!GTK_WIDGET_REALIZED(m_widget))
3230 {
3231 // we can't set the focus to the widget now so we remember that
3232 // it should be focused and will do it later, during the idle
3233 // time, as soon as we can
3234 wxLogTrace(TRACE_FOCUS,
3235 _T("Delaying setting focus to %s(%s)"),
3236 GetClassInfo()->GetClassName(), GetLabel().c_str());
3237
3238 g_delayedFocus = this;
3239 }
3240 else
3241 {
3242 wxLogTrace(TRACE_FOCUS,
3243 _T("Setting focus to %s(%s)"),
3244 GetClassInfo()->GetClassName(), GetLabel().c_str());
3245
3246 gtk_widget_grab_focus (m_widget);
3247 }
3248 }
3249 else
3250 {
3251 wxLogTrace(TRACE_FOCUS,
3252 _T("Can't set focus to %s(%s)"),
3253 GetClassInfo()->GetClassName(), GetLabel().c_str());
3254 }
3255 }
3256 }
3257
3258 void wxWindowGTK::SetCanFocus(bool canFocus)
3259 {
3260 if ( canFocus )
3261 GTK_WIDGET_SET_FLAGS(m_widget, GTK_CAN_FOCUS);
3262 else
3263 GTK_WIDGET_UNSET_FLAGS(m_widget, GTK_CAN_FOCUS);
3264
3265 if ( m_wxwindow && (m_widget != m_wxwindow) )
3266 {
3267 if ( canFocus )
3268 GTK_WIDGET_SET_FLAGS(m_wxwindow, GTK_CAN_FOCUS);
3269 else
3270 GTK_WIDGET_UNSET_FLAGS(m_wxwindow, GTK_CAN_FOCUS);
3271 }
3272 }
3273
3274 bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
3275 {
3276 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3277
3278 wxWindowGTK *oldParent = m_parent,
3279 *newParent = (wxWindowGTK *)newParentBase;
3280
3281 wxASSERT( GTK_IS_WIDGET(m_widget) );
3282
3283 if ( !wxWindowBase::Reparent(newParent) )
3284 return false;
3285
3286 wxASSERT( GTK_IS_WIDGET(m_widget) );
3287
3288 /* prevent GTK from deleting the widget arbitrarily */
3289 gtk_widget_ref( m_widget );
3290
3291 if (oldParent)
3292 {
3293 gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
3294 }
3295
3296 wxASSERT( GTK_IS_WIDGET(m_widget) );
3297
3298 if (newParent)
3299 {
3300 if (GTK_WIDGET_VISIBLE (newParent->m_widget))
3301 {
3302 m_showOnIdle = true;
3303 gtk_widget_hide( m_widget );
3304 }
3305
3306 /* insert GTK representation */
3307 (*(newParent->m_insertCallback))(newParent, this);
3308 }
3309
3310 /* reverse: prevent GTK from deleting the widget arbitrarily */
3311 gtk_widget_unref( m_widget );
3312
3313 SetLayoutDirection(wxLayout_Default);
3314
3315 return true;
3316 }
3317
3318 void wxWindowGTK::DoAddChild(wxWindowGTK *child)
3319 {
3320 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
3321
3322 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
3323
3324 wxASSERT_MSG( (m_insertCallback != NULL), wxT("invalid child insertion function") );
3325
3326 /* add to list */
3327 AddChild( child );
3328
3329 /* insert GTK representation */
3330 (*m_insertCallback)(this, child);
3331 }
3332
3333 void wxWindowGTK::AddChild(wxWindowBase *child)
3334 {
3335 wxWindowBase::AddChild(child);
3336 m_dirtyTabOrder = true;
3337 wxTheApp->WakeUpIdle();
3338 }
3339
3340 void wxWindowGTK::RemoveChild(wxWindowBase *child)
3341 {
3342 wxWindowBase::RemoveChild(child);
3343 m_dirtyTabOrder = true;
3344 wxTheApp->WakeUpIdle();
3345 }
3346
3347 /* static */
3348 wxLayoutDirection wxWindowGTK::GTKGetLayout(GtkWidget *widget)
3349 {
3350 return gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL
3351 ? wxLayout_RightToLeft
3352 : wxLayout_LeftToRight;
3353 }
3354
3355 /* static */
3356 void wxWindowGTK::GTKSetLayout(GtkWidget *widget, wxLayoutDirection dir)
3357 {
3358 wxASSERT_MSG( dir != wxLayout_Default, _T("invalid layout direction") );
3359
3360 gtk_widget_set_direction(widget,
3361 dir == wxLayout_RightToLeft ? GTK_TEXT_DIR_RTL
3362 : GTK_TEXT_DIR_LTR);
3363 }
3364
3365 wxLayoutDirection wxWindowGTK::GetLayoutDirection() const
3366 {
3367 return GTKGetLayout(m_widget);
3368 }
3369
3370 void wxWindowGTK::SetLayoutDirection(wxLayoutDirection dir)
3371 {
3372 if ( dir == wxLayout_Default )
3373 {
3374 const wxWindow *const parent = GetParent();
3375 if ( parent )
3376 {
3377 // inherit layout from parent.
3378 dir = parent->GetLayoutDirection();
3379 }
3380 else // no parent, use global default layout
3381 {
3382 dir = wxTheApp->GetLayoutDirection();
3383 }
3384 }
3385
3386 if ( dir == wxLayout_Default )
3387 return;
3388
3389 GTKSetLayout(m_widget, dir);
3390
3391 if (m_wxwindow && (m_wxwindow != m_widget))
3392 GTKSetLayout(m_wxwindow, dir);
3393 }
3394
3395 wxCoord
3396 wxWindowGTK::AdjustForLayoutDirection(wxCoord x,
3397 wxCoord WXUNUSED(width),
3398 wxCoord WXUNUSED(widthTotal)) const
3399 {
3400 // We now mirrors the coordinates of RTL windows in GtkPizza
3401 return x;
3402 }
3403
3404 void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, MoveKind move)
3405 {
3406 wxWindowBase::DoMoveInTabOrder(win, move);
3407 m_dirtyTabOrder = true;
3408 wxTheApp->WakeUpIdle();
3409 }
3410
3411 bool wxWindowGTK::DoNavigateIn(int flags)
3412 {
3413 if ( flags & wxNavigationKeyEvent::WinChange )
3414 {
3415 wxFAIL_MSG( _T("not implemented") );
3416
3417 return false;
3418 }
3419 else // navigate inside the container
3420 {
3421 wxWindow *parent = wxGetTopLevelParent(this);
3422 wxCHECK_MSG( parent, false, _T("every window must have a TLW parent") );
3423
3424 GtkDirectionType dir;
3425 dir = flags & wxNavigationKeyEvent::IsForward ? GTK_DIR_TAB_FORWARD
3426 : GTK_DIR_TAB_BACKWARD;
3427
3428 gboolean rc;
3429 g_signal_emit_by_name(parent->m_widget, "focus", dir, &rc);
3430
3431 return rc == TRUE;
3432 }
3433 }
3434
3435 bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3436 {
3437 // none needed by default
3438 return false;
3439 }
3440
3441 void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget* WXUNUSED(w))
3442 {
3443 // nothing to do by default since none is needed
3444 }
3445
3446 void wxWindowGTK::RealizeTabOrder()
3447 {
3448 if (m_wxwindow)
3449 {
3450 if ( !m_children.empty() )
3451 {
3452 // we don't only construct the correct focus chain but also use
3453 // this opportunity to update the mnemonic widgets for the widgets
3454 // that need them
3455
3456 GList *chain = NULL;
3457 wxWindowGTK* mnemonicWindow = NULL;
3458
3459 for ( wxWindowList::const_iterator i = m_children.begin();
3460 i != m_children.end();
3461 ++i )
3462 {
3463 wxWindowGTK *win = *i;
3464
3465 if ( mnemonicWindow )
3466 {
3467 if ( win->AcceptsFocusFromKeyboard() )
3468 {
3469 // wxComboBox et al. needs to focus on on a different
3470 // widget than m_widget, so if the main widget isn't
3471 // focusable try the connect widget
3472 GtkWidget* w = win->m_widget;
3473 if ( !GTK_WIDGET_CAN_FOCUS(w) )
3474 {
3475 w = win->GetConnectWidget();
3476 if ( !GTK_WIDGET_CAN_FOCUS(w) )
3477 w = NULL;
3478 }
3479
3480 if ( w )
3481 {
3482 mnemonicWindow->GTKWidgetDoSetMnemonic(w);
3483 mnemonicWindow = NULL;
3484 }
3485 }
3486 }
3487 else if ( win->GTKWidgetNeedsMnemonic() )
3488 {
3489 mnemonicWindow = win;
3490 }
3491
3492 chain = g_list_prepend(chain, win->m_widget);
3493 }
3494
3495 chain = g_list_reverse(chain);
3496
3497 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain);
3498 g_list_free(chain);
3499 }
3500 else // no children
3501 {
3502 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow));
3503 }
3504 }
3505 }
3506
3507 void wxWindowGTK::Raise()
3508 {
3509 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3510
3511 if (m_wxwindow && m_wxwindow->window)
3512 {
3513 gdk_window_raise( m_wxwindow->window );
3514 }
3515 else if (m_widget->window)
3516 {
3517 gdk_window_raise( m_widget->window );
3518 }
3519 }
3520
3521 void wxWindowGTK::Lower()
3522 {
3523 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3524
3525 if (m_wxwindow && m_wxwindow->window)
3526 {
3527 gdk_window_lower( m_wxwindow->window );
3528 }
3529 else if (m_widget->window)
3530 {
3531 gdk_window_lower( m_widget->window );
3532 }
3533 }
3534
3535 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
3536 {
3537 if ( !wxWindowBase::SetCursor(cursor.Ok() ? cursor : *wxSTANDARD_CURSOR) )
3538 return false;
3539
3540 GTKUpdateCursor();
3541
3542 return true;
3543 }
3544
3545 void wxWindowGTK::GTKUpdateCursor()
3546 {
3547 wxCursor cursor(g_globalCursor.Ok() ? g_globalCursor : GetCursor());
3548 if ( cursor.Ok() )
3549 {
3550 wxArrayGdkWindows windowsThis;
3551 GdkWindow * const winThis = GTKGetWindow(windowsThis);
3552 if ( winThis )
3553 {
3554 gdk_window_set_cursor(winThis, cursor.GetCursor());
3555 }
3556 else
3557 {
3558 const size_t count = windowsThis.size();
3559 for ( size_t n = 0; n < count; n++ )
3560 {
3561 GdkWindow *win = windowsThis[n];
3562 if ( !win )
3563 {
3564 wxFAIL_MSG(_T("NULL window returned by GTKGetWindow()?"));
3565 continue;
3566 }
3567
3568 gdk_window_set_cursor(win, cursor.GetCursor());
3569 }
3570 }
3571 }
3572 }
3573
3574 void wxWindowGTK::WarpPointer( int x, int y )
3575 {
3576 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3577
3578 // We provide this function ourselves as it is
3579 // missing in GDK (top of this file).
3580
3581 GdkWindow *window = (GdkWindow*) NULL;
3582 if (m_wxwindow)
3583 window = GTK_PIZZA(m_wxwindow)->bin_window;
3584 else
3585 window = GetConnectWidget()->window;
3586
3587 if (window)
3588 gdk_window_warp_pointer( window, x, y );
3589 }
3590
3591 wxWindowGTK::ScrollDir wxWindowGTK::ScrollDirFromRange(GtkRange *range) const
3592 {
3593 // find the scrollbar which generated the event
3594 for ( int dir = 0; dir < ScrollDir_Max; dir++ )
3595 {
3596 if ( range == m_scrollBar[dir] )
3597 return (ScrollDir)dir;
3598 }
3599
3600 wxFAIL_MSG( _T("event from unknown scrollbar received") );
3601
3602 return ScrollDir_Max;
3603 }
3604
3605 bool wxWindowGTK::DoScrollByUnits(ScrollDir dir, ScrollUnit unit, int units)
3606 {
3607 bool changed = false;
3608 GtkRange* range = m_scrollBar[dir];
3609 if ( range && units )
3610 {
3611 GtkAdjustment* adj = range->adjustment;
3612 gdouble inc = unit == ScrollUnit_Line ? adj->step_increment
3613 : adj->page_increment;
3614
3615 const int posOld = int(adj->value + 0.5);
3616 gtk_range_set_value(range, posOld + units*inc);
3617
3618 changed = int(adj->value + 0.5) != posOld;
3619 }
3620
3621 return changed;
3622 }
3623
3624 bool wxWindowGTK::ScrollLines(int lines)
3625 {
3626 return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Line, lines);
3627 }
3628
3629 bool wxWindowGTK::ScrollPages(int pages)
3630 {
3631 return DoScrollByUnits(ScrollDir_Vert, ScrollUnit_Page, pages);
3632 }
3633
3634 void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
3635 {
3636 if (!m_widget)
3637 return;
3638 if (!m_widget->window)
3639 return;
3640
3641 if (m_wxwindow)
3642 {
3643 if (!GTK_PIZZA(m_wxwindow)->bin_window) return;
3644
3645 GdkRectangle gdk_rect,
3646 *p;
3647 if (rect)
3648 {
3649 gdk_rect.x = rect->x;
3650 gdk_rect.y = rect->y;
3651 gdk_rect.width = rect->width;
3652 gdk_rect.height = rect->height;
3653 if (GetLayoutDirection() == wxLayout_RightToLeft)
3654 gdk_rect.x = GetClientSize().x - gdk_rect.x - gdk_rect.width;
3655
3656 p = &gdk_rect;
3657 }
3658 else // invalidate everything
3659 {
3660 p = NULL;
3661 }
3662
3663 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, p, TRUE );
3664 }
3665 }
3666
3667 void wxWindowGTK::Update()
3668 {
3669 GtkUpdate();
3670
3671 // when we call Update() we really want to update the window immediately on
3672 // screen, even if it means flushing the entire queue and hence slowing down
3673 // everything -- but it should still be done, it's just that Update() should
3674 // be called very rarely
3675 gdk_flush();
3676 }
3677
3678 void wxWindowGTK::GtkUpdate()
3679 {
3680 if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window)
3681 gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE );
3682 if (m_widget && m_widget->window && (m_wxwindow != m_widget))
3683 gdk_window_process_updates( m_widget->window, FALSE );
3684
3685 // for consistency with other platforms (and also because it's convenient
3686 // to be able to update an entire TLW by calling Update() only once), we
3687 // should also update all our children here
3688 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
3689 node;
3690 node = node->GetNext() )
3691 {
3692 node->GetData()->GtkUpdate();
3693 }
3694 }
3695
3696 bool wxWindowGTK::DoIsExposed( int x, int y ) const
3697 {
3698 return m_updateRegion.Contains(x, y) != wxOutRegion;
3699 }
3700
3701
3702 bool wxWindowGTK::DoIsExposed( int x, int y, int w, int h ) const
3703 {
3704 if (GetLayoutDirection() == wxLayout_RightToLeft)
3705 return m_updateRegion.Contains(x-w, y, w, h) != wxOutRegion;
3706 else
3707 return m_updateRegion.Contains(x, y, w, h) != wxOutRegion;
3708 }
3709
3710 void wxWindowGTK::GtkSendPaintEvents()
3711 {
3712 if (!m_wxwindow)
3713 {
3714 m_updateRegion.Clear();
3715 return;
3716 }
3717
3718 // Clip to paint region in wxClientDC
3719 m_clipPaintRegion = true;
3720
3721 m_nativeUpdateRegion = m_updateRegion;
3722
3723 if (GetLayoutDirection() == wxLayout_RightToLeft)
3724 {
3725 // Transform m_updateRegion under RTL
3726 m_updateRegion.Clear();
3727
3728 gint width;
3729 gdk_window_get_geometry( GTK_PIZZA(m_wxwindow)->bin_window,
3730 NULL, NULL, &width, NULL, NULL );
3731
3732 wxRegionIterator upd( m_nativeUpdateRegion );
3733 while (upd)
3734 {
3735 wxRect rect;
3736 rect.x = upd.GetX();
3737 rect.y = upd.GetY();
3738 rect.width = upd.GetWidth();
3739 rect.height = upd.GetHeight();
3740
3741 rect.x = width - rect.x - rect.width;
3742 m_updateRegion.Union( rect );
3743
3744 ++upd;
3745 }
3746 }
3747
3748 // widget to draw on
3749 GtkPizza *pizza = GTK_PIZZA (m_wxwindow);
3750
3751 if (GetThemeEnabled() && (GetBackgroundStyle() == wxBG_STYLE_SYSTEM))
3752 {
3753 // find ancestor from which to steal background
3754 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
3755 if (!parent)
3756 parent = (wxWindow*)this;
3757
3758 if (GTK_WIDGET_MAPPED(parent->m_widget))
3759 {
3760 wxRegionIterator upd( m_nativeUpdateRegion );
3761 while (upd)
3762 {
3763 GdkRectangle rect;
3764 rect.x = upd.GetX();
3765 rect.y = upd.GetY();
3766 rect.width = upd.GetWidth();
3767 rect.height = upd.GetHeight();
3768
3769 gtk_paint_flat_box( parent->m_widget->style,
3770 pizza->bin_window,
3771 (GtkStateType)GTK_WIDGET_STATE(m_wxwindow),
3772 GTK_SHADOW_NONE,
3773 &rect,
3774 parent->m_widget,
3775 (char *)"base",
3776 0, 0, -1, -1 );
3777
3778 ++upd;
3779 }
3780 }
3781 }
3782 else
3783 {
3784 wxWindowDC dc( (wxWindow*)this );
3785 dc.SetClippingRegion( m_updateRegion );
3786
3787 wxEraseEvent erase_event( GetId(), &dc );
3788 erase_event.SetEventObject( this );
3789
3790 GetEventHandler()->ProcessEvent(erase_event);
3791 }
3792
3793 wxNcPaintEvent nc_paint_event( GetId() );
3794 nc_paint_event.SetEventObject( this );
3795 GetEventHandler()->ProcessEvent( nc_paint_event );
3796
3797 wxPaintEvent paint_event( GetId() );
3798 paint_event.SetEventObject( this );
3799 GetEventHandler()->ProcessEvent( paint_event );
3800
3801 m_clipPaintRegion = false;
3802
3803 m_updateRegion.Clear();
3804 m_nativeUpdateRegion.Clear();
3805 }
3806
3807 void wxWindowGTK::SetDoubleBuffered( bool on )
3808 {
3809 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3810
3811 if ( m_wxwindow )
3812 gtk_widget_set_double_buffered( m_wxwindow, on );
3813 }
3814
3815 bool wxWindowGTK::IsDoubleBuffered() const
3816 {
3817 return GTK_WIDGET_DOUBLE_BUFFERED( m_wxwindow );
3818 }
3819
3820 void wxWindowGTK::ClearBackground()
3821 {
3822 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3823 }
3824
3825 #if wxUSE_TOOLTIPS
3826 void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
3827 {
3828 wxWindowBase::DoSetToolTip(tip);
3829
3830 if (m_tooltip)
3831 m_tooltip->Apply( (wxWindow *)this );
3832 }
3833
3834 void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
3835 {
3836 if (tip)
3837 {
3838 wxString tmp( tip );
3839 gtk_tooltips_set_tip( tips, GetConnectWidget(), wxGTK_CONV(tmp), (gchar*) NULL );
3840 }
3841 else
3842 {
3843 gtk_tooltips_set_tip( tips, GetConnectWidget(), NULL, NULL);
3844 }
3845 }
3846 #endif // wxUSE_TOOLTIPS
3847
3848 bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
3849 {
3850 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3851
3852 if (!wxWindowBase::SetBackgroundColour(colour))
3853 return false;
3854
3855 if (colour.Ok())
3856 {
3857 // We need the pixel value e.g. for background clearing.
3858 m_backgroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
3859 }
3860
3861 // apply style change (forceStyle=true so that new style is applied
3862 // even if the bg colour changed from valid to wxNullColour)
3863 if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM)
3864 ApplyWidgetStyle(true);
3865
3866 return true;
3867 }
3868
3869 bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
3870 {
3871 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3872
3873 if (!wxWindowBase::SetForegroundColour(colour))
3874 {
3875 return false;
3876 }
3877
3878 if (colour.Ok())
3879 {
3880 // We need the pixel value e.g. for background clearing.
3881 m_foregroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
3882 }
3883
3884 // apply style change (forceStyle=true so that new style is applied
3885 // even if the bg colour changed from valid to wxNullColour):
3886 ApplyWidgetStyle(true);
3887
3888 return true;
3889 }
3890
3891 PangoContext *wxWindowGTK::GtkGetPangoDefaultContext()
3892 {
3893 return gtk_widget_get_pango_context( m_widget );
3894 }
3895
3896 GtkRcStyle *wxWindowGTK::CreateWidgetStyle(bool forceStyle)
3897 {
3898 // do we need to apply any changes at all?
3899 if ( !forceStyle &&
3900 !m_font.Ok() &&
3901 !m_foregroundColour.Ok() && !m_backgroundColour.Ok() )
3902 {
3903 return NULL;
3904 }
3905
3906 GtkRcStyle *style = gtk_rc_style_new();
3907
3908 if ( m_font.Ok() )
3909 {
3910 style->font_desc =
3911 pango_font_description_copy( m_font.GetNativeFontInfo()->description );
3912 }
3913
3914 int flagsNormal = 0,
3915 flagsPrelight = 0,
3916 flagsActive = 0,
3917 flagsInsensitive = 0;
3918
3919 if ( m_foregroundColour.Ok() )
3920 {
3921 const GdkColor *fg = m_foregroundColour.GetColor();
3922
3923 style->fg[GTK_STATE_NORMAL] =
3924 style->text[GTK_STATE_NORMAL] = *fg;
3925 flagsNormal |= GTK_RC_FG | GTK_RC_TEXT;
3926
3927 style->fg[GTK_STATE_PRELIGHT] =
3928 style->text[GTK_STATE_PRELIGHT] = *fg;
3929 flagsPrelight |= GTK_RC_FG | GTK_RC_TEXT;
3930
3931 style->fg[GTK_STATE_ACTIVE] =
3932 style->text[GTK_STATE_ACTIVE] = *fg;
3933 flagsActive |= GTK_RC_FG | GTK_RC_TEXT;
3934 }
3935
3936 if ( m_backgroundColour.Ok() )
3937 {
3938 const GdkColor *bg = m_backgroundColour.GetColor();
3939
3940 style->bg[GTK_STATE_NORMAL] =
3941 style->base[GTK_STATE_NORMAL] = *bg;
3942 flagsNormal |= GTK_RC_BG | GTK_RC_BASE;
3943
3944 style->bg[GTK_STATE_PRELIGHT] =
3945 style->base[GTK_STATE_PRELIGHT] = *bg;
3946 flagsPrelight |= GTK_RC_BG | GTK_RC_BASE;
3947
3948 style->bg[GTK_STATE_ACTIVE] =
3949 style->base[GTK_STATE_ACTIVE] = *bg;
3950 flagsActive |= GTK_RC_BG | GTK_RC_BASE;
3951
3952 style->bg[GTK_STATE_INSENSITIVE] =
3953 style->base[GTK_STATE_INSENSITIVE] = *bg;
3954 flagsInsensitive |= GTK_RC_BG | GTK_RC_BASE;
3955 }
3956
3957 style->color_flags[GTK_STATE_NORMAL] = (GtkRcFlags)flagsNormal;
3958 style->color_flags[GTK_STATE_PRELIGHT] = (GtkRcFlags)flagsPrelight;
3959 style->color_flags[GTK_STATE_ACTIVE] = (GtkRcFlags)flagsActive;
3960 style->color_flags[GTK_STATE_INSENSITIVE] = (GtkRcFlags)flagsInsensitive;
3961
3962 return style;
3963 }
3964
3965 void wxWindowGTK::ApplyWidgetStyle(bool forceStyle)
3966 {
3967 GtkRcStyle *style = CreateWidgetStyle(forceStyle);
3968 if ( style )
3969 {
3970 DoApplyWidgetStyle(style);
3971 gtk_rc_style_unref(style);
3972 }
3973
3974 // Style change may affect GTK+'s size calculation:
3975 InvalidateBestSize();
3976 }
3977
3978 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
3979 {
3980 if (m_wxwindow)
3981 gtk_widget_modify_style(m_wxwindow, style);
3982 else
3983 gtk_widget_modify_style(m_widget, style);
3984 }
3985
3986 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
3987 {
3988 wxWindowBase::SetBackgroundStyle(style);
3989
3990 if (style == wxBG_STYLE_CUSTOM)
3991 {
3992 GdkWindow *window = (GdkWindow*) NULL;
3993 if (m_wxwindow)
3994 window = GTK_PIZZA(m_wxwindow)->bin_window;
3995 else
3996 window = GetConnectWidget()->window;
3997
3998 if (window)
3999 {
4000 // Make sure GDK/X11 doesn't refresh the window
4001 // automatically.
4002 gdk_window_set_back_pixmap( window, None, False );
4003 #ifdef __X__
4004 Display* display = GDK_WINDOW_DISPLAY(window);
4005 XFlush(display);
4006 #endif
4007 m_needsStyleChange = false;
4008 }
4009 else
4010 // Do in OnIdle, because the window is not yet available
4011 m_needsStyleChange = true;
4012
4013 // Don't apply widget style, or we get a grey background
4014 }
4015 else
4016 {
4017 // apply style change (forceStyle=true so that new style is applied
4018 // even if the bg colour changed from valid to wxNullColour):
4019 ApplyWidgetStyle(true);
4020 }
4021 return true;
4022 }
4023
4024 #if wxUSE_DRAG_AND_DROP
4025
4026 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
4027 {
4028 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4029
4030 GtkWidget *dnd_widget = GetConnectWidget();
4031
4032 if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
4033
4034 if (m_dropTarget) delete m_dropTarget;
4035 m_dropTarget = dropTarget;
4036
4037 if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
4038 }
4039
4040 #endif // wxUSE_DRAG_AND_DROP
4041
4042 GtkWidget* wxWindowGTK::GetConnectWidget()
4043 {
4044 GtkWidget *connect_widget = m_widget;
4045 if (m_wxwindow) connect_widget = m_wxwindow;
4046
4047 return connect_widget;
4048 }
4049
4050 bool wxWindowGTK::GTKIsOwnWindow(GdkWindow *window) const
4051 {
4052 wxArrayGdkWindows windowsThis;
4053 GdkWindow * const winThis = GTKGetWindow(windowsThis);
4054
4055 return winThis ? window == winThis
4056 : windowsThis.Index(window) != wxNOT_FOUND;
4057 }
4058
4059 GdkWindow *wxWindowGTK::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
4060 {
4061 return m_wxwindow ? GTK_PIZZA(m_wxwindow)->bin_window : m_widget->window;
4062 }
4063
4064 bool wxWindowGTK::SetFont( const wxFont &font )
4065 {
4066 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
4067
4068 if (!wxWindowBase::SetFont(font))
4069 return false;
4070
4071 // apply style change (forceStyle=true so that new style is applied
4072 // even if the font changed from valid to wxNullFont):
4073 ApplyWidgetStyle(true);
4074
4075 return true;
4076 }
4077
4078 void wxWindowGTK::DoCaptureMouse()
4079 {
4080 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4081
4082 GdkWindow *window = (GdkWindow*) NULL;
4083 if (m_wxwindow)
4084 window = GTK_PIZZA(m_wxwindow)->bin_window;
4085 else
4086 window = GetConnectWidget()->window;
4087
4088 wxCHECK_RET( window, _T("CaptureMouse() failed") );
4089
4090 const wxCursor* cursor = &m_cursor;
4091 if (!cursor->Ok())
4092 cursor = wxSTANDARD_CURSOR;
4093
4094 gdk_pointer_grab( window, FALSE,
4095 (GdkEventMask)
4096 (GDK_BUTTON_PRESS_MASK |
4097 GDK_BUTTON_RELEASE_MASK |
4098 GDK_POINTER_MOTION_HINT_MASK |
4099 GDK_POINTER_MOTION_MASK),
4100 (GdkWindow *) NULL,
4101 cursor->GetCursor(),
4102 (guint32)GDK_CURRENT_TIME );
4103 g_captureWindow = this;
4104 g_captureWindowHasMouse = true;
4105 }
4106
4107 void wxWindowGTK::DoReleaseMouse()
4108 {
4109 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4110
4111 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
4112
4113 g_captureWindow = (wxWindowGTK*) NULL;
4114
4115 GdkWindow *window = (GdkWindow*) NULL;
4116 if (m_wxwindow)
4117 window = GTK_PIZZA(m_wxwindow)->bin_window;
4118 else
4119 window = GetConnectWidget()->window;
4120
4121 if (!window)
4122 return;
4123
4124 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
4125 }
4126
4127 /* static */
4128 wxWindow *wxWindowBase::GetCapture()
4129 {
4130 return (wxWindow *)g_captureWindow;
4131 }
4132
4133 bool wxWindowGTK::IsRetained() const
4134 {
4135 return false;
4136 }
4137
4138 void wxWindowGTK::BlockScrollEvent()
4139 {
4140 wxASSERT(!m_blockScrollEvent);
4141 m_blockScrollEvent = true;
4142 }
4143
4144 void wxWindowGTK::UnblockScrollEvent()
4145 {
4146 wxASSERT(m_blockScrollEvent);
4147 m_blockScrollEvent = false;
4148 }
4149
4150 void wxWindowGTK::SetScrollbar(int orient,
4151 int pos,
4152 int thumbVisible,
4153 int range,
4154 bool WXUNUSED(update))
4155 {
4156 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4157 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4158 wxCHECK_RET( m_wxwindow != m_widget, wxT("no scrolling for this wxWindow, use wxHSCROLL or wxVSCROLL") );
4159
4160 if (range > 0)
4161 {
4162 m_hasScrolling = true;
4163 }
4164 else
4165 {
4166 // GtkRange requires upper > lower
4167 range =
4168 thumbVisible = 1;
4169 }
4170
4171 if (pos > range - thumbVisible)
4172 pos = range - thumbVisible;
4173 if (pos < 0)
4174 pos = 0;
4175 GtkAdjustment* adj = m_scrollBar[ScrollDirFromOrient(orient)]->adjustment;
4176 adj->step_increment = 1;
4177 adj->page_increment =
4178 adj->page_size = thumbVisible;
4179 adj->upper = range;
4180 SetScrollPos(orient, pos);
4181 gtk_adjustment_changed(adj);
4182 }
4183
4184 void wxWindowGTK::SetScrollPos(int orient, int pos, bool WXUNUSED(refresh))
4185 {
4186 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4187 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4188 wxCHECK_RET( m_wxwindow != m_widget, wxT("no scrolling for this wxWindow, use wxHSCROLL or wxVSCROLL") );
4189
4190 // This check is more than an optimization. Without it, the slider
4191 // will not move smoothly while tracking when using wxScrollHelper.
4192 if (GetScrollPos(orient) != pos)
4193 {
4194 const int dir = ScrollDirFromOrient(orient);
4195 GtkAdjustment* adj = m_scrollBar[dir]->adjustment;
4196 const int max = int(adj->upper - adj->page_size);
4197 if (pos > max)
4198 pos = max;
4199 if (pos < 0)
4200 pos = 0;
4201 m_scrollPos[dir] = adj->value = pos;
4202
4203 // If a "value_changed" signal emission is not already in progress
4204 if (!m_blockValueChanged[dir])
4205 {
4206 gtk_adjustment_value_changed(adj);
4207 }
4208 }
4209 }
4210
4211 int wxWindowGTK::GetScrollThumb(int orient) const
4212 {
4213 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4214 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4215 wxCHECK_MSG( m_wxwindow != m_widget, 0, wxT("no scrolling for this wxWindow, use wxHSCROLL or wxVSCROLL") );
4216
4217 return int(m_scrollBar[ScrollDirFromOrient(orient)]->adjustment->page_size);
4218 }
4219
4220 int wxWindowGTK::GetScrollPos( int orient ) const
4221 {
4222 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4223 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4224 wxCHECK_MSG( m_wxwindow != m_widget, 0, wxT("no scrolling for this wxWindow, use wxHSCROLL or wxVSCROLL") );
4225
4226 return int(m_scrollBar[ScrollDirFromOrient(orient)]->adjustment->value + 0.5);
4227 }
4228
4229 int wxWindowGTK::GetScrollRange( int orient ) const
4230 {
4231 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4232 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4233 wxCHECK_MSG( m_wxwindow != m_widget, 0, wxT("no scrolling for this wxWindow, use wxHSCROLL or wxVSCROLL") );
4234
4235 return int(m_scrollBar[ScrollDirFromOrient(orient)]->adjustment->upper);
4236 }
4237
4238 // Determine if increment is the same as +/-x, allowing for some small
4239 // difference due to possible inexactness in floating point arithmetic
4240 static inline bool IsScrollIncrement(double increment, double x)
4241 {
4242 wxASSERT(increment > 0);
4243 const double tolerance = 1.0 / 1024;
4244 return fabs(increment - fabs(x)) < tolerance;
4245 }
4246
4247 wxEventType wxWindowGTK::GetScrollEventType(GtkRange* range)
4248 {
4249 DEBUG_MAIN_THREAD
4250
4251 wxASSERT(range == m_scrollBar[0] || range == m_scrollBar[1]);
4252
4253 const int barIndex = range == m_scrollBar[1];
4254 GtkAdjustment* adj = range->adjustment;
4255
4256 const int value = int(adj->value + 0.5);
4257
4258 // save previous position
4259 const double oldPos = m_scrollPos[barIndex];
4260 // update current position
4261 m_scrollPos[barIndex] = adj->value;
4262 // If event should be ignored, or integral position has not changed
4263 if (!m_hasVMT || g_blockEventsOnDrag || value == int(oldPos + 0.5))
4264 {
4265 return wxEVT_NULL;
4266 }
4267
4268 wxEventType eventType = wxEVT_SCROLL_THUMBTRACK;
4269 if (!m_isScrolling)
4270 {
4271 // Difference from last change event
4272 const double diff = adj->value - oldPos;
4273 const bool isDown = diff > 0;
4274
4275 if (IsScrollIncrement(adj->step_increment, diff))
4276 {
4277 eventType = isDown ? wxEVT_SCROLL_LINEDOWN : wxEVT_SCROLL_LINEUP;
4278 }
4279 else if (IsScrollIncrement(adj->page_increment, diff))
4280 {
4281 eventType = isDown ? wxEVT_SCROLL_PAGEDOWN : wxEVT_SCROLL_PAGEUP;
4282 }
4283 else if (m_mouseButtonDown)
4284 {
4285 // Assume track event
4286 m_isScrolling = true;
4287 }
4288 }
4289 return eventType;
4290 }
4291
4292 void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
4293 {
4294 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4295
4296 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4297
4298 // No scrolling requested.
4299 if ((dx == 0) && (dy == 0)) return;
4300
4301 m_clipPaintRegion = true;
4302
4303 if (GetLayoutDirection() == wxLayout_RightToLeft)
4304 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), dx, -dy );
4305 else
4306 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy );
4307
4308 m_clipPaintRegion = false;
4309
4310 #if wxUSE_CARET
4311 bool restoreCaret = (GetCaret() != NULL && GetCaret()->IsVisible());
4312 if (restoreCaret)
4313 {
4314 wxRect caretRect(GetCaret()->GetPosition(), GetCaret()->GetSize());
4315 if (dx > 0)
4316 caretRect.width += dx;
4317 else
4318 {
4319 caretRect.x += dx; caretRect.width -= dx;
4320 }
4321 if (dy > 0)
4322 caretRect.height += dy;
4323 else
4324 {
4325 caretRect.y += dy; caretRect.height -= dy;
4326 }
4327
4328 RefreshRect(caretRect);
4329 }
4330 #endif // wxUSE_CARET
4331 }
4332
4333 void wxWindowGTK::GtkScrolledWindowSetBorder(GtkWidget* w, int wxstyle)
4334 {
4335 //RN: Note that static controls usually have no border on gtk, so maybe
4336 //it makes sense to treat that as simply no border at the wx level
4337 //as well...
4338 if (!(wxstyle & wxNO_BORDER) && !(wxstyle & wxBORDER_STATIC))
4339 {
4340 GtkShadowType gtkstyle;
4341
4342 if(wxstyle & wxBORDER_RAISED)
4343 gtkstyle = GTK_SHADOW_OUT;
4344 else if (wxstyle & wxBORDER_SUNKEN)
4345 gtkstyle = GTK_SHADOW_IN;
4346 else if (wxstyle & wxBORDER_DOUBLE)
4347 gtkstyle = GTK_SHADOW_ETCHED_IN;
4348 else //default
4349 gtkstyle = GTK_SHADOW_IN;
4350
4351 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w),
4352 gtkstyle );
4353 }
4354 }
4355
4356 void wxWindowGTK::SetWindowStyleFlag( long style )
4357 {
4358 // Updates the internal variable. NB: Now m_windowStyle bits carry the _new_ style values already
4359 wxWindowBase::SetWindowStyleFlag(style);
4360 }
4361
4362 // Find the wxWindow at the current mouse position, also returning the mouse
4363 // position.
4364 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4365 {
4366 pt = wxGetMousePosition();
4367 wxWindow* found = wxFindWindowAtPoint(pt);
4368 return found;
4369 }
4370
4371 // Get the current mouse position.
4372 wxPoint wxGetMousePosition()
4373 {
4374 /* This crashes when used within wxHelpContext,
4375 so we have to use the X-specific implementation below.
4376 gint x, y;
4377 GdkModifierType *mask;
4378 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4379
4380 return wxPoint(x, y);
4381 */
4382
4383 int x, y;
4384 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
4385
4386 Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY();
4387 Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
4388 Window rootReturn, childReturn;
4389 int rootX, rootY, winX, winY;
4390 unsigned int maskReturn;
4391
4392 XQueryPointer (display,
4393 rootWindow,
4394 &rootReturn,
4395 &childReturn,
4396 &rootX, &rootY, &winX, &winY, &maskReturn);
4397 return wxPoint(rootX, rootY);
4398
4399 }
4400
4401 // Needed for implementing e.g. combobox on wxGTK within a modal dialog.
4402 void wxAddGrab(wxWindow* window)
4403 {
4404 gtk_grab_add( (GtkWidget*) window->GetHandle() );
4405 }
4406
4407 void wxRemoveGrab(wxWindow* window)
4408 {
4409 gtk_grab_remove( (GtkWidget*) window->GetHandle() );
4410 }