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