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