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