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