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