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