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