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