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