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