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