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