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