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