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