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