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