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