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