Split this out from other changes to keep things sane..
[wxWidgets.git] / src / gtk / window.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: gtk/window.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling, Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10
11 #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 bool hasMouse = winUnderMouse == gdk_event->window;
1839 if ( hasMouse != g_captureWindowHasMouse )
1840 {
1841 // the mouse changed window
1842 g_captureWindowHasMouse = hasMouse;
1843
1844 wxMouseEvent event(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW
1845 : wxEVT_LEAVE_WINDOW);
1846 InitMouseEvent(win, event, gdk_event);
1847 event.SetEventObject(win);
1848 win->GetEventHandler()->ProcessEvent(event);
1849 }
1850 }
1851 else // no capture
1852 {
1853 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1854 }
1855
1856 if (win->GetEventHandler()->ProcessEvent( event ))
1857 {
1858 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "motion_notify_event" );
1859 return TRUE;
1860 }
1861
1862 return FALSE;
1863 }
1864
1865 //-----------------------------------------------------------------------------
1866 // "focus_in_event"
1867 //-----------------------------------------------------------------------------
1868
1869 // send the wxChildFocusEvent and wxFocusEvent, common code of
1870 // gtk_window_focus_in_callback() and SetFocus()
1871 static bool DoSendFocusEvents(wxWindow *win)
1872 {
1873 // Notify the parent keeping track of focus for the kbd navigation
1874 // purposes that we got it.
1875 wxChildFocusEvent eventChildFocus(win);
1876 (void)win->GetEventHandler()->ProcessEvent(eventChildFocus);
1877
1878 wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
1879 eventFocus.SetEventObject(win);
1880
1881 return win->GetEventHandler()->ProcessEvent(eventFocus);
1882 }
1883
1884 static gint gtk_window_focus_in_callback( GtkWidget *widget,
1885 GdkEvent *WXUNUSED(event),
1886 wxWindow *win )
1887 {
1888 DEBUG_MAIN_THREAD
1889
1890 if (g_isIdle)
1891 wxapp_install_idle_handler();
1892
1893 if (!win->m_hasVMT) return FALSE;
1894 if (g_blockEventsOnDrag) return FALSE;
1895
1896 switch ( g_sendActivateEvent )
1897 {
1898 case -1:
1899 // we've got focus from outside, synthetize wxActivateEvent
1900 g_sendActivateEvent = 1;
1901 break;
1902
1903 case 0:
1904 // another our window just lost focus, it was already ours before
1905 // - don't send any wxActivateEvent
1906 g_sendActivateEvent = -1;
1907 break;
1908 }
1909
1910 g_focusWindowLast =
1911 g_focusWindow = win;
1912
1913 wxLogTrace(TRACE_FOCUS,
1914 _T("%s: focus in"), win->GetName().c_str());
1915
1916 #ifdef HAVE_XIM
1917 if (win->m_ic)
1918 gdk_im_begin(win->m_ic, win->m_wxwindow->window);
1919 #endif
1920
1921 #if wxUSE_CARET
1922 // caret needs to be informed about focus change
1923 wxCaret *caret = win->GetCaret();
1924 if ( caret )
1925 {
1926 caret->OnSetFocus();
1927 }
1928 #endif // wxUSE_CARET
1929
1930 g_activeFrameLostFocus = FALSE;
1931
1932 wxWindowGTK *active = wxGetTopLevelParent(win);
1933 if ( active != g_activeFrame )
1934 {
1935 if ( g_activeFrame )
1936 {
1937 wxLogTrace(wxT("activate"), wxT("Deactivating frame %p (from focus_in)"), g_activeFrame);
1938 wxActivateEvent event(wxEVT_ACTIVATE, FALSE, g_activeFrame->GetId());
1939 event.SetEventObject(g_activeFrame);
1940 g_activeFrame->GetEventHandler()->ProcessEvent(event);
1941 }
1942
1943 wxLogTrace(wxT("activate"), wxT("Activating frame %p (from focus_in)"), active);
1944 g_activeFrame = active;
1945 wxActivateEvent event(wxEVT_ACTIVATE, TRUE, g_activeFrame->GetId());
1946 event.SetEventObject(g_activeFrame);
1947 g_activeFrame->GetEventHandler()->ProcessEvent(event);
1948
1949 // Don't send focus events in addition to activate
1950 // if (win == g_activeFrame)
1951 // return TRUE;
1952 }
1953
1954 // does the window itself think that it has the focus?
1955 if ( !win->m_hasFocus )
1956 {
1957 // not yet, notify it
1958 win->m_hasFocus = TRUE;
1959
1960 if ( DoSendFocusEvents(win) )
1961 {
1962 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_in_event" );
1963 return TRUE;
1964 }
1965 }
1966
1967 return FALSE;
1968 }
1969
1970 //-----------------------------------------------------------------------------
1971 // "focus_out_event"
1972 //-----------------------------------------------------------------------------
1973
1974 static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk_event, wxWindowGTK *win )
1975 {
1976 DEBUG_MAIN_THREAD
1977
1978 if (g_isIdle)
1979 wxapp_install_idle_handler();
1980
1981 if (!win->m_hasVMT) return FALSE;
1982 if (g_blockEventsOnDrag) return FALSE;
1983
1984 wxLogTrace( TRACE_FOCUS,
1985 _T("%s: focus out"), win->GetName().c_str() );
1986
1987 if ( !g_activeFrameLostFocus && g_activeFrame )
1988 {
1989 // VZ: commenting this out because it does happen (although not easy
1990 // to reproduce, I only see it when using wxMiniFrame and not
1991 // always) and makes using Mahogany quite annoying
1992 #if 0
1993 wxASSERT_MSG( wxGetTopLevelParent(win) == g_activeFrame,
1994 wxT("unfocusing window that hasn't gained focus properly") );
1995 #endif // 0
1996
1997 g_activeFrameLostFocus = TRUE;
1998 }
1999
2000 // if the focus goes out of our app alltogether, OnIdle() will send
2001 // wxActivateEvent, otherwise gtk_window_focus_in_callback() will reset
2002 // g_sendActivateEvent to -1
2003 g_sendActivateEvent = 0;
2004
2005 wxWindowGTK *winFocus = wxFindFocusedChild(win);
2006 if ( winFocus )
2007 win = winFocus;
2008
2009 g_focusWindow = (wxWindowGTK *)NULL;
2010
2011 #ifdef HAVE_XIM
2012 if (win->m_ic)
2013 gdk_im_end();
2014 #endif
2015
2016 #if wxUSE_CARET
2017 // caret needs to be informed about focus change
2018 wxCaret *caret = win->GetCaret();
2019 if ( caret )
2020 {
2021 caret->OnKillFocus();
2022 }
2023 #endif // wxUSE_CARET
2024
2025 // don't send the window a kill focus event if it thinks that it doesn't
2026 // have focus already
2027 if ( win->m_hasFocus )
2028 {
2029 win->m_hasFocus = FALSE;
2030
2031 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
2032 event.SetEventObject( win );
2033
2034 if (win->GetEventHandler()->ProcessEvent( event ))
2035 {
2036 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_out_event" );
2037 return TRUE;
2038 }
2039 }
2040
2041 return FALSE;
2042 }
2043
2044 //-----------------------------------------------------------------------------
2045 // "enter_notify_event"
2046 //-----------------------------------------------------------------------------
2047
2048 static
2049 gint gtk_window_enter_callback( GtkWidget *widget,
2050 GdkEventCrossing *gdk_event,
2051 wxWindowGTK *win )
2052 {
2053 DEBUG_MAIN_THREAD
2054
2055 if (g_isIdle)
2056 wxapp_install_idle_handler();
2057
2058 if (!win->m_hasVMT) return FALSE;
2059 if (g_blockEventsOnDrag) return FALSE;
2060
2061 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
2062
2063 int x = 0;
2064 int y = 0;
2065 GdkModifierType state = (GdkModifierType)0;
2066
2067 gdk_window_get_pointer( widget->window, &x, &y, &state );
2068
2069 wxMouseEvent event( wxEVT_ENTER_WINDOW );
2070 InitMouseEvent(win, event, gdk_event);
2071 wxPoint pt = win->GetClientAreaOrigin();
2072 event.m_x = x + pt.x;
2073 event.m_y = y + pt.y;
2074
2075 if (win->GetEventHandler()->ProcessEvent( event ))
2076 {
2077 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "enter_notify_event" );
2078 return TRUE;
2079 }
2080
2081 return FALSE;
2082 }
2083
2084 //-----------------------------------------------------------------------------
2085 // "leave_notify_event"
2086 //-----------------------------------------------------------------------------
2087
2088 static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindowGTK *win )
2089 {
2090 DEBUG_MAIN_THREAD
2091
2092 if (g_isIdle)
2093 wxapp_install_idle_handler();
2094
2095 if (!win->m_hasVMT) return FALSE;
2096 if (g_blockEventsOnDrag) return FALSE;
2097
2098 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
2099
2100 wxMouseEvent event( wxEVT_LEAVE_WINDOW );
2101 event.SetTimestamp( gdk_event->time );
2102 event.SetEventObject( win );
2103
2104 int x = 0;
2105 int y = 0;
2106 GdkModifierType state = (GdkModifierType)0;
2107
2108 gdk_window_get_pointer( widget->window, &x, &y, &state );
2109
2110 event.m_shiftDown = (state & GDK_SHIFT_MASK) != 0;
2111 event.m_controlDown = (state & GDK_CONTROL_MASK) != 0;
2112 event.m_altDown = (state & GDK_MOD1_MASK) != 0;
2113 event.m_metaDown = (state & GDK_MOD2_MASK) != 0;
2114 event.m_leftDown = (state & GDK_BUTTON1_MASK) != 0;
2115 event.m_middleDown = (state & GDK_BUTTON2_MASK) != 0;
2116 event.m_rightDown = (state & GDK_BUTTON3_MASK) != 0;
2117
2118 wxPoint pt = win->GetClientAreaOrigin();
2119 event.m_x = x + pt.x;
2120 event.m_y = y + pt.y;
2121
2122 if (win->GetEventHandler()->ProcessEvent( event ))
2123 {
2124 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "leave_notify_event" );
2125 return TRUE;
2126 }
2127
2128 return FALSE;
2129 }
2130
2131 //-----------------------------------------------------------------------------
2132 // "value_changed" from m_vAdjust
2133 //-----------------------------------------------------------------------------
2134
2135 static void gtk_window_vscroll_callback( GtkAdjustment *adjust,
2136 SCROLLBAR_CBACK_ARG
2137 wxWindowGTK *win )
2138 {
2139 DEBUG_MAIN_THREAD
2140
2141 if (g_isIdle)
2142 wxapp_install_idle_handler();
2143
2144 if (g_blockEventsOnDrag) return;
2145
2146 if (!win->m_hasVMT) return;
2147
2148 float diff = adjust->value - win->m_oldVerticalPos;
2149 if (fabs(diff) < 0.2) return;
2150
2151 win->m_oldVerticalPos = adjust->value;
2152
2153 #ifndef __WXGTK20__
2154 GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(win->m_widget);
2155 #endif
2156 wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(sw->vscrollbar));
2157
2158 int value = (int)(adjust->value+0.5);
2159
2160 wxScrollWinEvent event( command, value, wxVERTICAL );
2161 event.SetEventObject( win );
2162 win->GetEventHandler()->ProcessEvent( event );
2163 }
2164
2165 //-----------------------------------------------------------------------------
2166 // "value_changed" from m_hAdjust
2167 //-----------------------------------------------------------------------------
2168
2169 static void gtk_window_hscroll_callback( GtkAdjustment *adjust,
2170 SCROLLBAR_CBACK_ARG
2171 wxWindowGTK *win )
2172 {
2173 DEBUG_MAIN_THREAD
2174
2175 if (g_isIdle)
2176 wxapp_install_idle_handler();
2177
2178 if (g_blockEventsOnDrag) return;
2179 if (!win->m_hasVMT) return;
2180
2181 float diff = adjust->value - win->m_oldHorizontalPos;
2182 if (fabs(diff) < 0.2) return;
2183
2184 #ifndef __WXGTK20__
2185 GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(win->m_widget);
2186 #endif
2187 wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(sw->hscrollbar));
2188
2189 win->m_oldHorizontalPos = adjust->value;
2190
2191 int value = (int)(adjust->value+0.5);
2192
2193 wxScrollWinEvent event( command, value, wxHORIZONTAL );
2194 event.SetEventObject( win );
2195 win->GetEventHandler()->ProcessEvent( event );
2196 }
2197
2198 //-----------------------------------------------------------------------------
2199 // "button_press_event" from scrollbar
2200 //-----------------------------------------------------------------------------
2201
2202 static gint gtk_scrollbar_button_press_callback( GtkRange *widget,
2203 GdkEventButton *gdk_event,
2204 wxWindowGTK *win)
2205 {
2206 DEBUG_MAIN_THREAD
2207
2208 if (g_isIdle)
2209 wxapp_install_idle_handler();
2210
2211
2212 g_blockEventsOnScroll = TRUE;
2213
2214 // FIXME: there is no 'slider' field in GTK+ 2.0 any more
2215 #ifndef __WXGTK20__
2216 win->m_isScrolling = (gdk_event->window == widget->slider);
2217 #endif
2218
2219 return FALSE;
2220 }
2221
2222 //-----------------------------------------------------------------------------
2223 // "button_release_event" from scrollbar
2224 //-----------------------------------------------------------------------------
2225
2226 static gint gtk_scrollbar_button_release_callback( GtkRange *widget,
2227 GdkEventButton *WXUNUSED(gdk_event),
2228 wxWindowGTK *win)
2229 {
2230 DEBUG_MAIN_THREAD
2231
2232 // don't test here as we can release the mouse while being over
2233 // a different window than the slider
2234 //
2235 // if (gdk_event->window != widget->slider) return FALSE;
2236
2237 g_blockEventsOnScroll = FALSE;
2238
2239 if (win->m_isScrolling)
2240 {
2241 wxEventType command = wxEVT_SCROLLWIN_THUMBRELEASE;
2242 int value = -1;
2243 int dir = -1;
2244
2245 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(win->m_widget);
2246 if (widget == GTK_RANGE(scrolledWindow->hscrollbar))
2247 {
2248 value = (int)(win->m_hAdjust->value+0.5);
2249 dir = wxHORIZONTAL;
2250 }
2251 if (widget == GTK_RANGE(scrolledWindow->vscrollbar))
2252 {
2253 value = (int)(win->m_vAdjust->value+0.5);
2254 dir = wxVERTICAL;
2255 }
2256
2257 wxScrollWinEvent event( command, value, dir );
2258 event.SetEventObject( win );
2259 win->GetEventHandler()->ProcessEvent( event );
2260 }
2261
2262 win->m_isScrolling = FALSE;
2263
2264 return FALSE;
2265 }
2266
2267 // ----------------------------------------------------------------------------
2268 // this wxWindowBase function is implemented here (in platform-specific file)
2269 // because it is static and so couldn't be made virtual
2270 // ----------------------------------------------------------------------------
2271
2272 wxWindow *wxWindowBase::FindFocus()
2273 {
2274 // the cast is necessary when we compile in wxUniversal mode
2275 return (wxWindow *)g_focusWindow;
2276 }
2277
2278 //-----------------------------------------------------------------------------
2279 // "destroy" event
2280 //-----------------------------------------------------------------------------
2281
2282 // VZ: Robert commented the code using out so it generates warnings: should
2283 // be either fixed or removed completely
2284 #if 0
2285
2286 static void gtk_window_destroy_callback( GtkWidget* widget, wxWindow *win )
2287 {
2288 wxWindowDestroyEvent event(win);
2289 win->GetEventHandler()->ProcessEvent(event);
2290 }
2291
2292 #endif // 0
2293
2294 //-----------------------------------------------------------------------------
2295 // "realize" from m_widget
2296 //-----------------------------------------------------------------------------
2297
2298 /* We cannot set colours and fonts before the widget has
2299 been realized, so we do this directly after realization. */
2300
2301 static gint
2302 gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win )
2303 {
2304 DEBUG_MAIN_THREAD
2305
2306 if (g_isIdle)
2307 wxapp_install_idle_handler();
2308
2309 if (win->m_delayedBackgroundColour)
2310 win->GtkSetBackgroundColour( win->GetBackgroundColour() );
2311
2312 if (win->m_delayedForegroundColour)
2313 win->GtkSetForegroundColour( win->GetForegroundColour() );
2314
2315 #ifdef __WXGTK20__
2316 if (win->m_imContext)
2317 {
2318 GtkPizza *pizza = GTK_PIZZA( m_widget );
2319 gtk_im_context_set_client_window( (GtkIMContext*) win->m_imContext, pizza->bin_window );
2320 }
2321 #endif
2322
2323 wxWindowCreateEvent event( win );
2324 event.SetEventObject( win );
2325 win->GetEventHandler()->ProcessEvent( event );
2326
2327 return FALSE;
2328 }
2329
2330 //-----------------------------------------------------------------------------
2331 // "size_allocate"
2332 //-----------------------------------------------------------------------------
2333
2334 static
2335 void gtk_window_size_callback( GtkWidget *WXUNUSED(widget),
2336 GtkAllocation *WXUNUSED(alloc),
2337 wxWindow *win )
2338 {
2339 if (g_isIdle)
2340 wxapp_install_idle_handler();
2341
2342 if (!win->m_hasScrolling) return;
2343
2344 int client_width = 0;
2345 int client_height = 0;
2346 win->GetClientSize( &client_width, &client_height );
2347 if ((client_width == win->m_oldClientWidth) && (client_height == win->m_oldClientHeight))
2348 return;
2349
2350 win->m_oldClientWidth = client_width;
2351 win->m_oldClientHeight = client_height;
2352
2353 if (!win->m_nativeSizeEvent)
2354 {
2355 wxSizeEvent event( win->GetSize(), win->GetId() );
2356 event.SetEventObject( win );
2357 win->GetEventHandler()->ProcessEvent( event );
2358 }
2359 }
2360
2361
2362 #ifdef HAVE_XIM
2363 #define WXUNUSED_UNLESS_XIM(param) param
2364 #else
2365 #define WXUNUSED_UNLESS_XIM(param) WXUNUSED(param)
2366 #endif
2367
2368 /* Resize XIM window */
2369
2370 static
2371 void gtk_wxwindow_size_callback( GtkWidget* WXUNUSED_UNLESS_XIM(widget),
2372 GtkAllocation* WXUNUSED_UNLESS_XIM(alloc),
2373 wxWindowGTK* WXUNUSED_UNLESS_XIM(win) )
2374 {
2375 if (g_isIdle)
2376 wxapp_install_idle_handler();
2377
2378 #ifdef HAVE_XIM
2379 if (!win->m_ic)
2380 return;
2381
2382 if (gdk_ic_get_style (win->m_ic) & GDK_IM_PREEDIT_POSITION)
2383 {
2384 gint width, height;
2385
2386 gdk_window_get_size (widget->window, &width, &height);
2387 win->m_icattr->preedit_area.width = width;
2388 win->m_icattr->preedit_area.height = height;
2389 gdk_ic_set_attr (win->m_ic, win->m_icattr, GDK_IC_PREEDIT_AREA);
2390 }
2391 #endif // HAVE_XIM
2392 }
2393
2394 //-----------------------------------------------------------------------------
2395 // "realize" from m_wxwindow
2396 //-----------------------------------------------------------------------------
2397
2398 /* Initialize XIM support */
2399
2400 static gint
2401 gtk_wxwindow_realized_callback( GtkWidget * WXUNUSED_UNLESS_XIM(widget),
2402 wxWindowGTK * WXUNUSED_UNLESS_XIM(win) )
2403 {
2404 if (g_isIdle)
2405 wxapp_install_idle_handler();
2406
2407 #ifdef HAVE_XIM
2408 if (win->m_ic) return FALSE;
2409 if (!widget) return FALSE;
2410 if (!gdk_im_ready()) return FALSE;
2411
2412 win->m_icattr = gdk_ic_attr_new();
2413 if (!win->m_icattr) return FALSE;
2414
2415 gint width, height;
2416 GdkEventMask mask;
2417 GdkColormap *colormap;
2418 GdkICAttr *attr = win->m_icattr;
2419 unsigned attrmask = GDK_IC_ALL_REQ;
2420 GdkIMStyle style;
2421 GdkIMStyle supported_style = (GdkIMStyle)
2422 (GDK_IM_PREEDIT_NONE |
2423 GDK_IM_PREEDIT_NOTHING |
2424 GDK_IM_PREEDIT_POSITION |
2425 GDK_IM_STATUS_NONE |
2426 GDK_IM_STATUS_NOTHING);
2427
2428 if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
2429 supported_style = (GdkIMStyle)(supported_style & ~GDK_IM_PREEDIT_POSITION);
2430
2431 attr->style = style = gdk_im_decide_style (supported_style);
2432 attr->client_window = widget->window;
2433
2434 if ((colormap = gtk_widget_get_colormap (widget)) !=
2435 gtk_widget_get_default_colormap ())
2436 {
2437 attrmask |= GDK_IC_PREEDIT_COLORMAP;
2438 attr->preedit_colormap = colormap;
2439 }
2440
2441 attrmask |= GDK_IC_PREEDIT_FOREGROUND;
2442 attrmask |= GDK_IC_PREEDIT_BACKGROUND;
2443 attr->preedit_foreground = widget->style->fg[GTK_STATE_NORMAL];
2444 attr->preedit_background = widget->style->base[GTK_STATE_NORMAL];
2445
2446 switch (style & GDK_IM_PREEDIT_MASK)
2447 {
2448 case GDK_IM_PREEDIT_POSITION:
2449 if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
2450 {
2451 g_warning ("over-the-spot style requires fontset");
2452 break;
2453 }
2454
2455 gdk_window_get_size (widget->window, &width, &height);
2456
2457 attrmask |= GDK_IC_PREEDIT_POSITION_REQ;
2458 attr->spot_location.x = 0;
2459 attr->spot_location.y = height;
2460 attr->preedit_area.x = 0;
2461 attr->preedit_area.y = 0;
2462 attr->preedit_area.width = width;
2463 attr->preedit_area.height = height;
2464 attr->preedit_fontset = widget->style->font;
2465
2466 break;
2467 }
2468
2469 win->m_ic = gdk_ic_new (attr, (GdkICAttributesType)attrmask);
2470
2471 if (win->m_ic == NULL)
2472 g_warning ("Can't create input context.");
2473 else
2474 {
2475 mask = gdk_window_get_events (widget->window);
2476 mask = (GdkEventMask)(mask | gdk_ic_get_events (win->m_ic));
2477 gdk_window_set_events (widget->window, mask);
2478
2479 if (GTK_WIDGET_HAS_FOCUS(widget))
2480 gdk_im_begin (win->m_ic, widget->window);
2481 }
2482 #endif // HAVE_XIM
2483
2484 return FALSE;
2485 }
2486
2487 //-----------------------------------------------------------------------------
2488 // InsertChild for wxWindowGTK.
2489 //-----------------------------------------------------------------------------
2490
2491 /* Callback for wxWindowGTK. This very strange beast has to be used because
2492 * C++ has no virtual methods in a constructor. We have to emulate a
2493 * virtual function here as wxNotebook requires a different way to insert
2494 * a child in it. I had opted for creating a wxNotebookPage window class
2495 * which would have made this superfluous (such in the MDI window system),
2496 * but no-one was listening to me... */
2497
2498 static void wxInsertChildInWindow( wxWindowGTK* parent, wxWindowGTK* child )
2499 {
2500 /* the window might have been scrolled already, do we
2501 have to adapt the position */
2502 GtkPizza *pizza = GTK_PIZZA(parent->m_wxwindow);
2503 child->m_x += pizza->xoffset;
2504 child->m_y += pizza->yoffset;
2505
2506 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
2507 GTK_WIDGET(child->m_widget),
2508 child->m_x,
2509 child->m_y,
2510 child->m_width,
2511 child->m_height );
2512 }
2513
2514 //-----------------------------------------------------------------------------
2515 // global functions
2516 //-----------------------------------------------------------------------------
2517
2518 wxWindow *wxGetActiveWindow()
2519 {
2520 return wxWindow::FindFocus();
2521 }
2522
2523 //-----------------------------------------------------------------------------
2524 // wxWindowGTK
2525 //-----------------------------------------------------------------------------
2526
2527 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2528 // method
2529 #ifdef __WXUNIVERSAL__
2530 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK, wxWindowBase)
2531 #else // __WXGTK__
2532 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
2533 #endif // __WXUNIVERSAL__/__WXGTK__
2534
2535 void wxWindowGTK::Init()
2536 {
2537 // common init
2538 InitBase();
2539
2540 // GTK specific
2541 m_widget = (GtkWidget *) NULL;
2542 m_wxwindow = (GtkWidget *) NULL;
2543 m_focusWidget = (GtkWidget *) NULL;
2544
2545 // position/size
2546 m_x = 0;
2547 m_y = 0;
2548 m_width = 0;
2549 m_height = 0;
2550
2551 m_sizeSet = FALSE;
2552 m_hasVMT = FALSE;
2553 m_needParent = TRUE;
2554 m_isBeingDeleted = FALSE;
2555
2556 m_noExpose = FALSE;
2557 m_nativeSizeEvent = FALSE;
2558
2559 m_hasScrolling = FALSE;
2560 m_isScrolling = FALSE;
2561
2562 m_hAdjust = (GtkAdjustment*) NULL;
2563 m_vAdjust = (GtkAdjustment*) NULL;
2564 m_oldHorizontalPos = 0.0;
2565 m_oldVerticalPos = 0.0;
2566
2567 m_resizing = FALSE;
2568 m_widgetStyle = (GtkStyle*) NULL;
2569
2570 m_insertCallback = (wxInsertChildFunction) NULL;
2571
2572 m_acceptsFocus = FALSE;
2573 m_hasFocus = FALSE;
2574
2575 m_clipPaintRegion = FALSE;
2576
2577 m_cursor = *wxSTANDARD_CURSOR;
2578
2579 m_delayedForegroundColour = FALSE;
2580 m_delayedBackgroundColour = FALSE;
2581
2582 #ifdef __WXGTK20__
2583 m_imContext = NULL;
2584 m_x11Context = NULL;
2585 #else
2586 #ifdef HAVE_XIM
2587 m_ic = (GdkIC*) NULL;
2588 m_icattr = (GdkICAttr*) NULL;
2589 #endif
2590 #endif
2591 }
2592
2593 wxWindowGTK::wxWindowGTK()
2594 {
2595 Init();
2596 }
2597
2598 wxWindowGTK::wxWindowGTK( wxWindow *parent,
2599 wxWindowID id,
2600 const wxPoint &pos,
2601 const wxSize &size,
2602 long style,
2603 const wxString &name )
2604 {
2605 Init();
2606
2607 Create( parent, id, pos, size, style, name );
2608 }
2609
2610 bool wxWindowGTK::Create( wxWindow *parent,
2611 wxWindowID id,
2612 const wxPoint &pos,
2613 const wxSize &size,
2614 long style,
2615 const wxString &name )
2616 {
2617 if (!PreCreation( parent, pos, size ) ||
2618 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
2619 {
2620 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2621 return FALSE;
2622 }
2623
2624 m_insertCallback = wxInsertChildInWindow;
2625
2626 // always needed for background clearing
2627 m_delayedBackgroundColour = TRUE;
2628
2629 m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
2630 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
2631
2632 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
2633
2634 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
2635 scroll_class->scrollbar_spacing = 0;
2636
2637 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
2638
2639 m_hAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->hscrollbar) );
2640 m_vAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->vscrollbar) );
2641
2642 m_wxwindow = gtk_pizza_new();
2643
2644 #ifndef __WXUNIVERSAL__
2645 GtkPizza *pizza = GTK_PIZZA(m_wxwindow);
2646
2647 if (HasFlag(wxRAISED_BORDER))
2648 {
2649 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_OUT );
2650 }
2651 else if (HasFlag(wxSUNKEN_BORDER))
2652 {
2653 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_IN );
2654 }
2655 else if (HasFlag(wxSIMPLE_BORDER))
2656 {
2657 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_THIN );
2658 }
2659 else
2660 {
2661 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_NONE );
2662 }
2663 #endif // __WXUNIVERSAL__
2664
2665 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
2666
2667 GTK_WIDGET_SET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
2668 m_acceptsFocus = TRUE;
2669
2670 // I _really_ don't want scrollbars in the beginning
2671 m_vAdjust->lower = 0.0;
2672 m_vAdjust->upper = 1.0;
2673 m_vAdjust->value = 0.0;
2674 m_vAdjust->step_increment = 1.0;
2675 m_vAdjust->page_increment = 1.0;
2676 m_vAdjust->page_size = 5.0;
2677 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "changed" );
2678 m_hAdjust->lower = 0.0;
2679 m_hAdjust->upper = 1.0;
2680 m_hAdjust->value = 0.0;
2681 m_hAdjust->step_increment = 1.0;
2682 m_hAdjust->page_increment = 1.0;
2683 m_hAdjust->page_size = 5.0;
2684 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "changed" );
2685
2686 // these handlers block mouse events to any window during scrolling such as
2687 // motion events and prevent GTK and wxWindows from fighting over where the
2688 // slider should be
2689
2690 gtk_signal_connect( GTK_OBJECT(scrolledWindow->vscrollbar), "button_press_event",
2691 (GtkSignalFunc)gtk_scrollbar_button_press_callback, (gpointer) this );
2692
2693 gtk_signal_connect( GTK_OBJECT(scrolledWindow->hscrollbar), "button_press_event",
2694 (GtkSignalFunc)gtk_scrollbar_button_press_callback, (gpointer) this );
2695
2696 gtk_signal_connect( GTK_OBJECT(scrolledWindow->vscrollbar), "button_release_event",
2697 (GtkSignalFunc)gtk_scrollbar_button_release_callback, (gpointer) this );
2698
2699 gtk_signal_connect( GTK_OBJECT(scrolledWindow->hscrollbar), "button_release_event",
2700 (GtkSignalFunc)gtk_scrollbar_button_release_callback, (gpointer) this );
2701
2702 // these handlers get notified when screen updates are required either when
2703 // scrolling or when the window size (and therefore scrollbar configuration)
2704 // has changed
2705
2706 gtk_signal_connect( GTK_OBJECT(m_hAdjust), "value_changed",
2707 (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this );
2708 gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed",
2709 (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this );
2710
2711 #ifdef __WXGTK20__
2712 // Create input method handler
2713 m_imContext = (GtkIMMulticontext*) gtk_im_multicontext_new ();
2714
2715 // Cannot handle drawing preedited text yet
2716 gtk_im_context_set_use_preedit( (GtkIMContext*) m_imContext, FALSE );
2717
2718 g_signal_connect (G_OBJECT (m_imContext), "commit",
2719 G_CALLBACK (gtk_wxwindow_commit_cb), this);
2720 #endif
2721
2722 gtk_widget_show( m_wxwindow );
2723
2724 if (m_parent)
2725 m_parent->DoAddChild( this );
2726
2727 m_focusWidget = m_wxwindow;
2728
2729 PostCreation();
2730
2731 Show( TRUE );
2732
2733 return TRUE;
2734 }
2735
2736 wxWindowGTK::~wxWindowGTK()
2737 {
2738 if (g_focusWindow == this)
2739 g_focusWindow = NULL;
2740
2741 if (g_activeFrame == this)
2742 g_activeFrame = NULL;
2743
2744 if ( g_delayedFocus == this )
2745 g_delayedFocus = NULL;
2746
2747 m_isBeingDeleted = TRUE;
2748 m_hasVMT = FALSE;
2749
2750 if (m_widget)
2751 Show( FALSE );
2752
2753 DestroyChildren();
2754
2755 if (m_parent)
2756 m_parent->RemoveChild( this );
2757
2758 #ifdef HAVE_XIM
2759 if (m_ic)
2760 gdk_ic_destroy (m_ic);
2761 if (m_icattr)
2762 gdk_ic_attr_destroy (m_icattr);
2763 #endif
2764
2765 if (m_widgetStyle)
2766 {
2767 #if DISABLE_STYLE_IF_BROKEN_THEME
2768 // don't delete if it's a pixmap theme style
2769 if (!m_widgetStyle->engine_data)
2770 gtk_style_unref( m_widgetStyle );
2771 #endif
2772 m_widgetStyle = (GtkStyle*) NULL;
2773 }
2774
2775 if (m_wxwindow)
2776 {
2777 gtk_widget_destroy( m_wxwindow );
2778 m_wxwindow = (GtkWidget*) NULL;
2779 }
2780
2781 if (m_widget)
2782 {
2783 gtk_widget_destroy( m_widget );
2784 m_widget = (GtkWidget*) NULL;
2785 }
2786 }
2787
2788 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
2789 {
2790 wxCHECK_MSG( !m_needParent || parent, FALSE, wxT("Need complete parent.") );
2791
2792 // This turns -1 into 30 so that a minimal window is
2793 // visible even although -1,-1 has been given as the
2794 // size of the window. the same trick is used in other
2795 // ports and should make debugging easier.
2796 m_width = WidthDefault(size.x) ;
2797 m_height = HeightDefault(size.y);
2798
2799 m_x = (int)pos.x;
2800 m_y = (int)pos.y;
2801
2802 // some reasonable defaults
2803 if (!parent)
2804 {
2805 if (m_x == -1)
2806 {
2807 m_x = (gdk_screen_width () - m_width) / 2;
2808 if (m_x < 10) m_x = 10;
2809 }
2810 if (m_y == -1)
2811 {
2812 m_y = (gdk_screen_height () - m_height) / 2;
2813 if (m_y < 10) m_y = 10;
2814 }
2815 }
2816
2817 return TRUE;
2818 }
2819
2820 void wxWindowGTK::PostCreation()
2821 {
2822 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2823
2824 if (m_wxwindow)
2825 {
2826 if (!m_noExpose)
2827 {
2828 // these get reported to wxWindows -> wxPaintEvent
2829
2830 gtk_pizza_set_external( GTK_PIZZA(m_wxwindow), TRUE );
2831
2832 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "expose_event",
2833 GTK_SIGNAL_FUNC(gtk_window_expose_callback), (gpointer)this );
2834
2835 #ifndef __WXGTK20__
2836 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "draw",
2837 GTK_SIGNAL_FUNC(gtk_window_draw_callback), (gpointer)this );
2838
2839 if (HasFlag(wxNO_FULL_REPAINT_ON_RESIZE))
2840 {
2841 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "event",
2842 GTK_SIGNAL_FUNC(gtk_window_event_event_callback), (gpointer)this );
2843 }
2844 #else
2845 // gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), HasFlag( wxNO_FULL_REPAINT_ON_RESIZE ) );
2846 #endif
2847
2848 #ifdef __WXGTK20__
2849 // Create input method handler
2850 m_imContext = (GtkIMMulticontext*) gtk_im_multicontext_new ();
2851
2852 // Cannot handle drawing preedited text yet
2853 gtk_im_context_set_use_preedit( (GtkIMContext*) m_imContext, FALSE );
2854
2855 g_signal_connect (G_OBJECT (m_imContext), "commit",
2856 G_CALLBACK (gtk_wxwindow_commit_cb), this);
2857 #endif
2858 }
2859
2860 // these are called when the "sunken" or "raised" borders are drawn
2861 gtk_signal_connect( GTK_OBJECT(m_widget), "expose_event",
2862 GTK_SIGNAL_FUNC(gtk_window_own_expose_callback), (gpointer)this );
2863
2864 #ifndef __WXGTK20__
2865 gtk_signal_connect( GTK_OBJECT(m_widget), "draw",
2866 GTK_SIGNAL_FUNC(gtk_window_own_draw_callback), (gpointer)this );
2867 #endif
2868 }
2869
2870 // focus handling
2871
2872 if (m_focusWidget == NULL)
2873 m_focusWidget = m_widget;
2874
2875 gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_in_event",
2876 GTK_SIGNAL_FUNC(gtk_window_focus_in_callback), (gpointer)this );
2877
2878 gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_out_event",
2879 GTK_SIGNAL_FUNC(gtk_window_focus_out_callback), (gpointer)this );
2880
2881 // connect to the various key and mouse handlers
2882
2883 GtkWidget *connect_widget = GetConnectWidget();
2884
2885 ConnectWidget( connect_widget );
2886
2887 /* We cannot set colours, fonts and cursors before the widget has
2888 been realized, so we do this directly after realization */
2889 gtk_signal_connect( GTK_OBJECT(connect_widget), "realize",
2890 GTK_SIGNAL_FUNC(gtk_window_realized_callback), (gpointer) this );
2891
2892 if (m_wxwindow)
2893 {
2894 // Catch native resize events
2895 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "size_allocate",
2896 GTK_SIGNAL_FUNC(gtk_window_size_callback), (gpointer)this );
2897
2898 // Initialize XIM support
2899 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "realize",
2900 GTK_SIGNAL_FUNC(gtk_wxwindow_realized_callback), (gpointer) this );
2901
2902 // And resize XIM window
2903 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "size_allocate",
2904 GTK_SIGNAL_FUNC(gtk_wxwindow_size_callback), (gpointer)this );
2905 }
2906
2907 if (!GTK_IS_COMBO(m_widget))
2908 {
2909 // This is needed if we want to add our windows into native
2910 // GTK control, such as the toolbar. With this callback, the
2911 // toolbar gets to know the correct size (the one set by the
2912 // programmer). Sadly, it misbehaves for wxComboBox. FIXME
2913 // when moving to GTK 2.0.
2914 gtk_signal_connect( GTK_OBJECT(m_widget), "size_request",
2915 GTK_SIGNAL_FUNC(gtk_window_size_request_callback), (gpointer) this );
2916 }
2917
2918 m_hasVMT = TRUE;
2919 }
2920
2921 void wxWindowGTK::ConnectWidget( GtkWidget *widget )
2922 {
2923 gtk_signal_connect( GTK_OBJECT(widget), "key_press_event",
2924 GTK_SIGNAL_FUNC(gtk_window_key_press_callback), (gpointer)this );
2925
2926 gtk_signal_connect( GTK_OBJECT(widget), "key_release_event",
2927 GTK_SIGNAL_FUNC(gtk_window_key_release_callback), (gpointer)this );
2928
2929 gtk_signal_connect( GTK_OBJECT(widget), "button_press_event",
2930 GTK_SIGNAL_FUNC(gtk_window_button_press_callback), (gpointer)this );
2931
2932 gtk_signal_connect( GTK_OBJECT(widget), "button_release_event",
2933 GTK_SIGNAL_FUNC(gtk_window_button_release_callback), (gpointer)this );
2934
2935 gtk_signal_connect( GTK_OBJECT(widget), "motion_notify_event",
2936 GTK_SIGNAL_FUNC(gtk_window_motion_notify_callback), (gpointer)this );
2937
2938 gtk_signal_connect( GTK_OBJECT(widget), "enter_notify_event",
2939 GTK_SIGNAL_FUNC(gtk_window_enter_callback), (gpointer)this );
2940
2941 gtk_signal_connect( GTK_OBJECT(widget), "leave_notify_event",
2942 GTK_SIGNAL_FUNC(gtk_window_leave_callback), (gpointer)this );
2943
2944 // This keeps crashing on me. RR.
2945 //
2946 // gtk_signal_connect( GTK_OBJECT(widget), "destroy",
2947 // GTK_SIGNAL_FUNC(gtk_window_destroy_callback), (gpointer)this );
2948 }
2949
2950 bool wxWindowGTK::Destroy()
2951 {
2952 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2953
2954 m_hasVMT = FALSE;
2955
2956 return wxWindowBase::Destroy();
2957 }
2958
2959 void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
2960 {
2961 gtk_pizza_set_size( GTK_PIZZA(m_parent->m_wxwindow), m_widget, x, y, width, height );
2962 }
2963
2964 void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
2965 {
2966 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2967 wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") );
2968
2969 /*
2970 printf( "DoSetSize: name %s, x,y,w,h: %d,%d,%d,%d \n", GetName().c_str(), x,y,width,height );
2971 */
2972
2973 if (m_resizing) return; /* I don't like recursions */
2974 m_resizing = TRUE;
2975
2976 int currentX, currentY;
2977 GetPosition(&currentX, &currentY);
2978 if (x == -1)
2979 x = currentX;
2980 if (y == -1)
2981 y = currentY;
2982 AdjustForParentClientOrigin(x, y, sizeFlags);
2983
2984 if (m_parent->m_wxwindow == NULL) /* i.e. wxNotebook */
2985 {
2986 /* don't set the size for children of wxNotebook, just take the values. */
2987 m_x = x;
2988 m_y = y;
2989 m_width = width;
2990 m_height = height;
2991 }
2992 else
2993 {
2994 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
2995 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
2996 {
2997 if (x != -1) m_x = x + pizza->xoffset;
2998 if (y != -1) m_y = y + pizza->yoffset;
2999 }
3000 else
3001 {
3002 m_x = x + pizza->xoffset;
3003 m_y = y + pizza->yoffset;
3004 }
3005 if (width != -1) m_width = width;
3006 if (height != -1) m_height = height;
3007
3008 if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH)
3009 {
3010 if (width == -1) m_width = 80;
3011 }
3012
3013 if ((sizeFlags & wxSIZE_AUTO_HEIGHT) == wxSIZE_AUTO_HEIGHT)
3014 {
3015 if (height == -1) m_height = 26;
3016 }
3017
3018 int minWidth = GetMinWidth(),
3019 minHeight = GetMinHeight(),
3020 maxWidth = GetMaxWidth(),
3021 maxHeight = GetMaxHeight();
3022
3023 if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth;
3024 if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight;
3025 if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth;
3026 if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
3027
3028 int border = 0;
3029 int bottom_border = 0;
3030
3031 #ifndef __WXGTK20__
3032 if (GTK_WIDGET_CAN_DEFAULT(m_widget))
3033 {
3034 /* the default button has a border around it */
3035 border = 6;
3036 bottom_border = 5;
3037 }
3038 #endif
3039
3040 DoMoveWindow( m_x-border,
3041 m_y-border,
3042 m_width+2*border,
3043 m_height+border+bottom_border );
3044 }
3045
3046 if (m_hasScrolling)
3047 {
3048 /* Sometimes the client area changes size without the
3049 whole windows's size changing, but if the whole
3050 windows's size doesn't change, no wxSizeEvent will
3051 normally be sent. Here we add an extra test if
3052 the client test has been changed and this will
3053 be used then. */
3054 GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
3055 }
3056
3057 /*
3058 wxPrintf( "OnSize sent from " );
3059 if (GetClassInfo() && GetClassInfo()->GetClassName())
3060 wxPrintf( GetClassInfo()->GetClassName() );
3061 wxPrintf( " %d %d %d %d\n", (int)m_x, (int)m_y, (int)m_width, (int)m_height );
3062 */
3063
3064 if (!m_nativeSizeEvent)
3065 {
3066 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
3067 event.SetEventObject( this );
3068 GetEventHandler()->ProcessEvent( event );
3069 }
3070
3071 m_resizing = FALSE;
3072 }
3073
3074 void wxWindowGTK::OnInternalIdle()
3075 {
3076 // Update invalidated regions.
3077 GtkUpdate();
3078
3079 // Synthetize activate events.
3080 if ( g_sendActivateEvent != -1 )
3081 {
3082 bool activate = g_sendActivateEvent != 0;
3083
3084 // do it only once
3085 g_sendActivateEvent = -1;
3086
3087 wxTheApp->SetActive(activate, (wxWindow *)g_focusWindowLast);
3088 }
3089
3090 if ( g_activeFrameLostFocus )
3091 {
3092 if ( g_activeFrame )
3093 {
3094 wxLogTrace(wxT("activate"), wxT("Deactivating frame %p (from idle)"), g_activeFrame);
3095 wxActivateEvent event(wxEVT_ACTIVATE, FALSE, g_activeFrame->GetId());
3096 event.SetEventObject(g_activeFrame);
3097 g_activeFrame->GetEventHandler()->ProcessEvent(event);
3098 g_activeFrame = NULL;
3099 }
3100 g_activeFrameLostFocus = FALSE;
3101 }
3102
3103 wxCursor cursor = m_cursor;
3104 if (g_globalCursor.Ok()) cursor = g_globalCursor;
3105
3106 if (cursor.Ok())
3107 {
3108 /* I now set the cursor anew in every OnInternalIdle call
3109 as setting the cursor in a parent window also effects the
3110 windows above so that checking for the current cursor is
3111 not possible. */
3112
3113 if (m_wxwindow)
3114 {
3115 GdkWindow *window = GTK_PIZZA(m_wxwindow)->bin_window;
3116 if (window)
3117 gdk_window_set_cursor( window, cursor.GetCursor() );
3118
3119 if (!g_globalCursor.Ok())
3120 cursor = *wxSTANDARD_CURSOR;
3121
3122 window = m_widget->window;
3123 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
3124 gdk_window_set_cursor( window, cursor.GetCursor() );
3125
3126 }
3127 else
3128 {
3129
3130 GdkWindow *window = m_widget->window;
3131 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
3132 gdk_window_set_cursor( window, cursor.GetCursor() );
3133
3134 }
3135 }
3136
3137 UpdateWindowUI();
3138 }
3139
3140 void wxWindowGTK::DoGetSize( int *width, int *height ) const
3141 {
3142 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3143
3144 if (width) (*width) = m_width;
3145 if (height) (*height) = m_height;
3146 }
3147
3148 void wxWindowGTK::DoSetClientSize( int width, int height )
3149 {
3150 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3151
3152 if (!m_wxwindow)
3153 {
3154 SetSize( width, height );
3155 }
3156 else
3157 {
3158 int dw = 0;
3159 int dh = 0;
3160
3161 #ifndef __WXUNIVERSAL__
3162 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3163 {
3164 /* when using GTK 1.2 we set the shadow border size to 2 */
3165 dw += 2 * 2;
3166 dh += 2 * 2;
3167 }
3168 if (HasFlag(wxSIMPLE_BORDER))
3169 {
3170 /* when using GTK 1.2 we set the simple border size to 1 */
3171 dw += 1 * 2;
3172 dh += 1 * 2;
3173 }
3174 #endif // __WXUNIVERSAL__
3175
3176 if (m_hasScrolling)
3177 {
3178 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3179
3180 GtkRequisition vscroll_req;
3181 vscroll_req.width = 2;
3182 vscroll_req.height = 2;
3183 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3184 (scroll_window->vscrollbar, &vscroll_req );
3185
3186 GtkRequisition hscroll_req;
3187 hscroll_req.width = 2;
3188 hscroll_req.height = 2;
3189 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3190 (scroll_window->hscrollbar, &hscroll_req );
3191
3192 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3193
3194 if (scroll_window->vscrollbar_visible)
3195 {
3196 dw += vscroll_req.width;
3197 dw += scroll_class->scrollbar_spacing;
3198 }
3199
3200 if (scroll_window->hscrollbar_visible)
3201 {
3202 dh += hscroll_req.height;
3203 dh += scroll_class->scrollbar_spacing;
3204 }
3205 }
3206
3207 SetSize( width+dw, height+dh );
3208 }
3209 }
3210
3211 void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
3212 {
3213 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3214
3215 if (!m_wxwindow)
3216 {
3217 if (width) (*width) = m_width;
3218 if (height) (*height) = m_height;
3219 }
3220 else
3221 {
3222 int dw = 0;
3223 int dh = 0;
3224
3225 #ifndef __WXUNIVERSAL__
3226 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3227 {
3228 /* when using GTK 1.2 we set the shadow border size to 2 */
3229 dw += 2 * 2;
3230 dh += 2 * 2;
3231 }
3232 if (HasFlag(wxSIMPLE_BORDER))
3233 {
3234 /* when using GTK 1.2 we set the simple border size to 1 */
3235 dw += 1 * 2;
3236 dh += 1 * 2;
3237 }
3238 #endif // __WXUNIVERSAL__
3239
3240 if (m_hasScrolling)
3241 {
3242 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3243
3244 GtkRequisition vscroll_req;
3245 vscroll_req.width = 2;
3246 vscroll_req.height = 2;
3247 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3248 (scroll_window->vscrollbar, &vscroll_req );
3249
3250 GtkRequisition hscroll_req;
3251 hscroll_req.width = 2;
3252 hscroll_req.height = 2;
3253 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3254 (scroll_window->hscrollbar, &hscroll_req );
3255
3256 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3257
3258 if (scroll_window->vscrollbar_visible)
3259 {
3260 dw += vscroll_req.width;
3261 dw += scroll_class->scrollbar_spacing;
3262 }
3263
3264 if (scroll_window->hscrollbar_visible)
3265 {
3266 dh += hscroll_req.height;
3267 dh += scroll_class->scrollbar_spacing;
3268 }
3269 }
3270
3271 if (width) (*width) = m_width - dw;
3272 if (height) (*height) = m_height - dh;
3273 }
3274
3275 /*
3276 printf( "GetClientSize, name %s ", GetName().c_str() );
3277 if (width) printf( " width = %d", (*width) );
3278 if (height) printf( " height = %d", (*height) );
3279 printf( "\n" );
3280 */
3281 }
3282
3283 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
3284 {
3285 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3286
3287 int dx = 0;
3288 int dy = 0;
3289 if (m_parent && m_parent->m_wxwindow)
3290 {
3291 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
3292 dx = pizza->xoffset;
3293 dy = pizza->yoffset;
3294 }
3295
3296 if (x) (*x) = m_x - dx;
3297 if (y) (*y) = m_y - dy;
3298 }
3299
3300 void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
3301 {
3302 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3303
3304 if (!m_widget->window) return;
3305
3306 GdkWindow *source = (GdkWindow *) NULL;
3307 if (m_wxwindow)
3308 source = GTK_PIZZA(m_wxwindow)->bin_window;
3309 else
3310 source = m_widget->window;
3311
3312 int org_x = 0;
3313 int org_y = 0;
3314 gdk_window_get_origin( source, &org_x, &org_y );
3315
3316 if (!m_wxwindow)
3317 {
3318 if (GTK_WIDGET_NO_WINDOW (m_widget))
3319 {
3320 org_x += m_widget->allocation.x;
3321 org_y += m_widget->allocation.y;
3322 }
3323 }
3324
3325 if (x) *x += org_x;
3326 if (y) *y += org_y;
3327 }
3328
3329 void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
3330 {
3331 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3332
3333 if (!m_widget->window) return;
3334
3335 GdkWindow *source = (GdkWindow *) NULL;
3336 if (m_wxwindow)
3337 source = GTK_PIZZA(m_wxwindow)->bin_window;
3338 else
3339 source = m_widget->window;
3340
3341 int org_x = 0;
3342 int org_y = 0;
3343 gdk_window_get_origin( source, &org_x, &org_y );
3344
3345 if (!m_wxwindow)
3346 {
3347 if (GTK_WIDGET_NO_WINDOW (m_widget))
3348 {
3349 org_x += m_widget->allocation.x;
3350 org_y += m_widget->allocation.y;
3351 }
3352 }
3353
3354 if (x) *x -= org_x;
3355 if (y) *y -= org_y;
3356 }
3357
3358 bool wxWindowGTK::Show( bool show )
3359 {
3360 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3361
3362 if (!wxWindowBase::Show(show))
3363 {
3364 // nothing to do
3365 return FALSE;
3366 }
3367
3368 if (show)
3369 gtk_widget_show( m_widget );
3370 else
3371 gtk_widget_hide( m_widget );
3372
3373 wxShowEvent eventShow(GetId(), show);
3374 eventShow.m_eventObject = this;
3375
3376 GetEventHandler()->ProcessEvent(eventShow);
3377
3378 return TRUE;
3379 }
3380
3381 static void wxWindowNotifyEnable(wxWindowGTK* win, bool enable)
3382 {
3383 win->OnParentEnable(enable);
3384
3385 // Recurse, so that children have the opportunity to Do The Right Thing
3386 // and reset colours that have been messed up by a parent's (really ancestor's)
3387 // Enable call
3388 for ( wxWindowList::Node *node = win->GetChildren().GetFirst();
3389 node;
3390 node = node->GetNext() )
3391 {
3392 wxWindow *child = node->GetData();
3393 if (!child->IsKindOf(CLASSINFO(wxDialog)) && !child->IsKindOf(CLASSINFO(wxFrame)))
3394 wxWindowNotifyEnable(child, enable);
3395 }
3396 }
3397
3398 bool wxWindowGTK::Enable( bool enable )
3399 {
3400 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3401
3402 if (!wxWindowBase::Enable(enable))
3403 {
3404 // nothing to do
3405 return FALSE;
3406 }
3407
3408 gtk_widget_set_sensitive( m_widget, enable );
3409 if ( m_wxwindow )
3410 gtk_widget_set_sensitive( m_wxwindow, enable );
3411
3412 wxWindowNotifyEnable(this, enable);
3413
3414 return TRUE;
3415 }
3416
3417 int wxWindowGTK::GetCharHeight() const
3418 {
3419 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
3420
3421 wxCHECK_MSG( m_font.Ok(), 12, wxT("invalid font") );
3422
3423 #ifdef __WXGTK20__
3424 PangoContext *context = NULL;
3425 if (m_widget)
3426 context = gtk_widget_get_pango_context( m_widget );
3427
3428 if (!context)
3429 return 0;
3430
3431 PangoFontDescription *desc = m_font.GetNativeFontInfo()->description;
3432 PangoLayout *layout = pango_layout_new(context);
3433 pango_layout_set_font_description(layout, desc);
3434 pango_layout_set_text(layout, "H", 1);
3435 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3436
3437 PangoRectangle rect;
3438 pango_layout_line_get_extents(line, NULL, &rect);
3439
3440 g_object_unref( G_OBJECT( layout ) );
3441
3442 return (int) (rect.height / PANGO_SCALE);
3443 #else
3444 GdkFont *font = m_font.GetInternalFont( 1.0 );
3445
3446 return font->ascent + font->descent;
3447 #endif
3448 }
3449
3450 int wxWindowGTK::GetCharWidth() const
3451 {
3452 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
3453
3454 wxCHECK_MSG( m_font.Ok(), 8, wxT("invalid font") );
3455
3456 #ifdef __WXGTK20__
3457 PangoContext *context = NULL;
3458 if (m_widget)
3459 context = gtk_widget_get_pango_context( m_widget );
3460
3461 if (!context)
3462 return 0;
3463
3464 PangoFontDescription *desc = m_font.GetNativeFontInfo()->description;
3465 PangoLayout *layout = pango_layout_new(context);
3466 pango_layout_set_font_description(layout, desc);
3467 pango_layout_set_text(layout, "H", 1);
3468 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3469
3470 PangoRectangle rect;
3471 pango_layout_line_get_extents(line, NULL, &rect);
3472
3473 g_object_unref( G_OBJECT( layout ) );
3474
3475 return (int) (rect.width / PANGO_SCALE);
3476 #else
3477 GdkFont *font = m_font.GetInternalFont( 1.0 );
3478
3479 return gdk_string_width( font, "H" );
3480 #endif
3481 }
3482
3483 void wxWindowGTK::GetTextExtent( const wxString& string,
3484 int *x,
3485 int *y,
3486 int *descent,
3487 int *externalLeading,
3488 const wxFont *theFont ) const
3489 {
3490 wxFont fontToUse = m_font;
3491 if (theFont) fontToUse = *theFont;
3492
3493 wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") );
3494
3495 if (string.IsEmpty())
3496 {
3497 if (x) (*x) = 0;
3498 if (y) (*y) = 0;
3499 return;
3500 }
3501
3502 #ifdef __WXGTK20__
3503 PangoContext *context = NULL;
3504 if (m_widget)
3505 context = gtk_widget_get_pango_context( m_widget );
3506
3507 if (!context)
3508 {
3509 if (x) (*x) = 0;
3510 if (y) (*y) = 0;
3511 return;
3512 }
3513
3514 PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
3515 PangoLayout *layout = pango_layout_new(context);
3516 pango_layout_set_font_description(layout, desc);
3517 {
3518 #if wxUSE_UNICODE
3519 const wxCharBuffer data = wxConvUTF8.cWC2MB( string );
3520 pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data ));
3521 #else
3522 const wxWCharBuffer wdata = wxConvLocal.cMB2WC( string );
3523 const wxCharBuffer data = wxConvUTF8.cWC2MB( wdata );
3524 pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data ));
3525 #endif
3526 }
3527 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3528
3529 PangoRectangle rect;
3530 pango_layout_line_get_extents(line, NULL, &rect);
3531
3532 if (x) (*x) = (wxCoord) (rect.width / PANGO_SCALE);
3533 if (y) (*y) = (wxCoord) (rect.height / PANGO_SCALE);
3534 if (descent)
3535 {
3536 // Do something about metrics here
3537 (*descent) = 0;
3538 }
3539 if (externalLeading) (*externalLeading) = 0; // ??
3540
3541 g_object_unref( G_OBJECT( layout ) );
3542 #else
3543 GdkFont *font = fontToUse.GetInternalFont( 1.0 );
3544 if (x) (*x) = gdk_string_width( font, wxGTK_CONV( string ) );
3545 if (y) (*y) = font->ascent + font->descent;
3546 if (descent) (*descent) = font->descent;
3547 if (externalLeading) (*externalLeading) = 0; // ??
3548 #endif
3549 }
3550
3551 void wxWindowGTK::SetFocus()
3552 {
3553 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3554
3555 if ( m_hasFocus )
3556 {
3557 // don't do anything if we already have focus
3558 return;
3559 }
3560
3561 if (m_wxwindow)
3562 {
3563 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
3564 {
3565 gtk_widget_grab_focus (m_wxwindow);
3566 }
3567 }
3568 else if (m_widget)
3569 {
3570 if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
3571 {
3572 if (!GTK_WIDGET_REALIZED(m_widget))
3573 {
3574 // we can't set the focus to the widget now so we remember that
3575 // it should be focused and will do it later, during the idle
3576 // time, as soon as we can
3577 wxLogTrace(TRACE_FOCUS,
3578 _T("Delaying setting focus to %s(%s)"),
3579 GetClassInfo()->GetClassName(), GetLabel().c_str());
3580
3581 g_delayedFocus = this;
3582 }
3583 else
3584 {
3585 wxLogTrace(TRACE_FOCUS,
3586 _T("Setting focus to %s(%s)"),
3587 GetClassInfo()->GetClassName(), GetLabel().c_str());
3588
3589 gtk_widget_grab_focus (m_widget);
3590 }
3591 }
3592 else if (GTK_IS_CONTAINER(m_widget))
3593 {
3594 SET_CONTAINER_FOCUS( m_widget, GTK_DIR_TAB_FORWARD );
3595 }
3596 else
3597 {
3598 wxLogTrace(TRACE_FOCUS,
3599 _T("Can't set focus to %s(%s)"),
3600 GetClassInfo()->GetClassName(), GetLabel().c_str());
3601 }
3602 }
3603 }
3604
3605 bool wxWindowGTK::AcceptsFocus() const
3606 {
3607 return m_acceptsFocus && wxWindowBase::AcceptsFocus();
3608 }
3609
3610 bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
3611 {
3612 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3613
3614 wxWindowGTK *oldParent = m_parent,
3615 *newParent = (wxWindowGTK *)newParentBase;
3616
3617 wxASSERT( GTK_IS_WIDGET(m_widget) );
3618
3619 if ( !wxWindowBase::Reparent(newParent) )
3620 return FALSE;
3621
3622 wxASSERT( GTK_IS_WIDGET(m_widget) );
3623
3624 /* prevent GTK from deleting the widget arbitrarily */
3625 gtk_widget_ref( m_widget );
3626
3627 if (oldParent)
3628 {
3629 gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
3630 }
3631
3632 wxASSERT( GTK_IS_WIDGET(m_widget) );
3633
3634 if (newParent)
3635 {
3636 /* insert GTK representation */
3637 (*(newParent->m_insertCallback))(newParent, this);
3638 }
3639
3640 /* reverse: prevent GTK from deleting the widget arbitrarily */
3641 gtk_widget_unref( m_widget );
3642
3643 return TRUE;
3644 }
3645
3646 void wxWindowGTK::DoAddChild(wxWindowGTK *child)
3647 {
3648 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
3649
3650 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
3651
3652 wxASSERT_MSG( (m_insertCallback != NULL), wxT("invalid child insertion function") );
3653
3654 /* add to list */
3655 AddChild( child );
3656
3657 /* insert GTK representation */
3658 (*m_insertCallback)(this, child);
3659 }
3660
3661 void wxWindowGTK::Raise()
3662 {
3663 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3664
3665 if (!m_widget->window) return;
3666
3667 gdk_window_raise( m_widget->window );
3668 }
3669
3670 void wxWindowGTK::Lower()
3671 {
3672 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3673
3674 if (!m_widget->window) return;
3675
3676 gdk_window_lower( m_widget->window );
3677 }
3678
3679 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
3680 {
3681 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3682
3683 if (cursor == m_cursor)
3684 return FALSE;
3685
3686 if (g_isIdle)
3687 wxapp_install_idle_handler();
3688
3689 if (cursor == wxNullCursor)
3690 return wxWindowBase::SetCursor( *wxSTANDARD_CURSOR );
3691 else
3692 return wxWindowBase::SetCursor( cursor );
3693 }
3694
3695 void wxWindowGTK::WarpPointer( int x, int y )
3696 {
3697 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3698
3699 // We provide this function ourselves as it is
3700 // missing in GDK (top of this file).
3701
3702 GdkWindow *window = (GdkWindow*) NULL;
3703 if (m_wxwindow)
3704 window = GTK_PIZZA(m_wxwindow)->bin_window;
3705 else
3706 window = GetConnectWidget()->window;
3707
3708 if (window)
3709 gdk_window_warp_pointer( window, x, y );
3710 }
3711
3712
3713 void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
3714 {
3715 if (!m_widget) return;
3716 if (!m_widget->window) return;
3717
3718 #ifndef __WXGTK20__
3719 if (g_isIdle)
3720 wxapp_install_idle_handler();
3721
3722 if (eraseBackground && m_wxwindow && m_wxwindow->window)
3723 {
3724 if (rect)
3725 {
3726 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3727 m_clearRegion.Union( rect->x, rect->y, rect->width, rect->height );
3728 }
3729 else
3730 {
3731 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3732 m_clearRegion.Clear();
3733 m_clearRegion.Union( 0, 0, m_wxwindow->allocation.width, m_wxwindow->allocation.height );
3734 }
3735 }
3736
3737 if (rect)
3738 {
3739 if (m_wxwindow)
3740 {
3741 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3742 m_updateRegion.Union( rect->x, rect->y, rect->width, rect->height );
3743 }
3744 else
3745 {
3746 GdkRectangle gdk_rect;
3747 gdk_rect.x = rect->x;
3748 gdk_rect.y = rect->y;
3749 gdk_rect.width = rect->width;
3750 gdk_rect.height = rect->height;
3751 gtk_widget_draw( m_widget, &gdk_rect );
3752 }
3753 }
3754 else
3755 {
3756 if (m_wxwindow)
3757 {
3758 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3759 m_updateRegion.Clear();
3760 m_updateRegion.Union( 0, 0, m_wxwindow->allocation.width, m_wxwindow->allocation.height );
3761 }
3762 else
3763 {
3764 gtk_widget_draw( m_widget, (GdkRectangle*) NULL );
3765 }
3766 }
3767 #else
3768 if (m_wxwindow)
3769 {
3770 if (rect)
3771 {
3772 GdkRectangle gdk_rect;
3773 gdk_rect.x = rect->x;
3774 gdk_rect.y = rect->y;
3775 gdk_rect.width = rect->width;
3776 gdk_rect.height = rect->height;
3777 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, &gdk_rect, TRUE );
3778 }
3779 else
3780 {
3781 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, NULL, TRUE );
3782 }
3783 }
3784 #endif
3785 }
3786
3787 void wxWindowGTK::Update()
3788 {
3789 GtkUpdate();
3790 }
3791
3792 void wxWindowGTK::GtkUpdate()
3793 {
3794 #ifdef __WXGTK20__
3795 if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window)
3796 gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE );
3797 #else
3798 if (!m_updateRegion.IsEmpty())
3799 GtkSendPaintEvents();
3800 #endif
3801 }
3802
3803 void wxWindowGTK::GtkSendPaintEvents()
3804 {
3805 if (!m_wxwindow)
3806 {
3807 #ifndef __WXGTK20__
3808 m_clearRegion.Clear();
3809 #endif
3810 m_updateRegion.Clear();
3811 return;
3812 }
3813
3814 // Clip to paint region in wxClientDC
3815 m_clipPaintRegion = TRUE;
3816
3817 #ifndef __WXGTK20__
3818 // widget to draw on
3819 GtkPizza *pizza = GTK_PIZZA (m_wxwindow);
3820
3821 // later for GTK 2.0, too.
3822 if (GetThemeEnabled())
3823 {
3824 // find ancestor from which to steal background
3825 wxWindow *parent = GetParent();
3826 while (parent && !parent->IsTopLevel())
3827 parent = parent->GetParent();
3828 if (!parent)
3829 parent = (wxWindow*)this;
3830
3831 wxRegionIterator upd( m_updateRegion );
3832 while (upd)
3833 {
3834 GdkRectangle rect;
3835 rect.x = upd.GetX();
3836 rect.y = upd.GetY();
3837 rect.width = upd.GetWidth();
3838 rect.height = upd.GetHeight();
3839
3840 gtk_paint_flat_box( parent->m_widget->style,
3841 pizza->bin_window,
3842 GTK_STATE_NORMAL,
3843 GTK_SHADOW_NONE,
3844 &rect,
3845 parent->m_widget,
3846 (char *)"base",
3847 0, 0, -1, -1 );
3848
3849 upd ++;
3850 }
3851 }
3852 else
3853 #endif
3854
3855 #ifdef __WXGTK20__
3856 {
3857 wxWindowDC dc( (wxWindow*)this );
3858 dc.SetClippingRegion( m_updateRegion );
3859
3860 wxEraseEvent erase_event( GetId(), &dc );
3861 erase_event.SetEventObject( this );
3862
3863 GetEventHandler()->ProcessEvent(erase_event);
3864 }
3865 #else
3866 // if (!m_clearRegion.IsEmpty()) // Always send an erase event under GTK 1.2
3867 {
3868 wxWindowDC dc( (wxWindow*)this );
3869 if (m_clearRegion.IsEmpty())
3870 dc.SetClippingRegion( m_updateRegion );
3871 else
3872 dc.SetClippingRegion( m_clearRegion );
3873
3874 wxEraseEvent erase_event( GetId(), &dc );
3875 erase_event.SetEventObject( this );
3876
3877 if (!GetEventHandler()->ProcessEvent(erase_event))
3878 {
3879 if (!g_eraseGC)
3880 {
3881 g_eraseGC = gdk_gc_new( pizza->bin_window );
3882 gdk_gc_set_fill( g_eraseGC, GDK_SOLID );
3883 }
3884 gdk_gc_set_foreground( g_eraseGC, m_backgroundColour.GetColor() );
3885
3886 wxRegionIterator upd( m_clearRegion );
3887 while (upd)
3888 {
3889 gdk_draw_rectangle( pizza->bin_window, g_eraseGC, 1,
3890 upd.GetX(), upd.GetY(), upd.GetWidth(), upd.GetHeight() );
3891 upd ++;
3892 }
3893 }
3894 m_clearRegion.Clear();
3895 }
3896 #endif
3897
3898 wxNcPaintEvent nc_paint_event( GetId() );
3899 nc_paint_event.SetEventObject( this );
3900 GetEventHandler()->ProcessEvent( nc_paint_event );
3901
3902 wxPaintEvent paint_event( GetId() );
3903 paint_event.SetEventObject( this );
3904 GetEventHandler()->ProcessEvent( paint_event );
3905
3906 m_clipPaintRegion = FALSE;
3907
3908 #ifndef __WXUNIVERSAL__
3909 #ifndef __WXGTK20__
3910 // The following code will result in all window-less widgets
3911 // being redrawn because the wxWindows class is allowed to
3912 // paint over the window-less widgets.
3913
3914 GList *children = pizza->children;
3915 while (children)
3916 {
3917 GtkPizzaChild *child = (GtkPizzaChild*) children->data;
3918 children = children->next;
3919
3920 if (GTK_WIDGET_NO_WINDOW (child->widget) &&
3921 GTK_WIDGET_DRAWABLE (child->widget))
3922 {
3923 // Get intersection of widget area and update region
3924 wxRegion region( m_updateRegion );
3925
3926 GdkEventExpose gdk_event;
3927 gdk_event.type = GDK_EXPOSE;
3928 gdk_event.window = pizza->bin_window;
3929 gdk_event.count = 0;
3930
3931 wxRegionIterator upd( m_updateRegion );
3932 while (upd)
3933 {
3934 GdkRectangle rect;
3935 rect.x = upd.GetX();
3936 rect.y = upd.GetY();
3937 rect.width = upd.GetWidth();
3938 rect.height = upd.GetHeight();
3939
3940 if (gtk_widget_intersect (child->widget, &rect, &gdk_event.area))
3941 {
3942 gtk_widget_event (child->widget, (GdkEvent*) &gdk_event);
3943 }
3944
3945 upd ++;
3946 }
3947 }
3948 }
3949 #endif
3950 #endif
3951
3952 m_updateRegion.Clear();
3953 }
3954
3955 void wxWindowGTK::Clear()
3956 {
3957 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3958
3959 #ifndef __WXGTK20__
3960 if (m_wxwindow && m_wxwindow->window)
3961 {
3962 m_clearRegion.Clear();
3963 wxSize size( GetClientSize() );
3964 m_clearRegion.Union( 0,0,size.x,size.y );
3965
3966 // Better do this in idle?
3967 GtkUpdate();
3968 }
3969 #endif
3970 }
3971
3972 #if wxUSE_TOOLTIPS
3973 void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
3974 {
3975 wxWindowBase::DoSetToolTip(tip);
3976
3977 if (m_tooltip)
3978 m_tooltip->Apply( (wxWindow *)this );
3979 }
3980
3981 void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
3982 {
3983 gtk_tooltips_set_tip( tips, GetConnectWidget(), wxConvCurrent->cWX2MB(tip), (gchar*) NULL );
3984 }
3985 #endif // wxUSE_TOOLTIPS
3986
3987 void wxWindowGTK::GtkSetBackgroundColour( const wxColour &colour )
3988 {
3989 GdkWindow *window = (GdkWindow*) NULL;
3990 if (m_wxwindow)
3991 window = GTK_PIZZA(m_wxwindow)->bin_window;
3992 else
3993 window = GetConnectWidget()->window;
3994
3995 wxASSERT( window );
3996
3997 // We need the pixel value e.g. for background clearing.
3998 m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
3999
4000 if (m_wxwindow)
4001 {
4002 // wxMSW doesn't clear the window here, either.
4003 gdk_window_set_background( window, m_backgroundColour.GetColor() );
4004 }
4005
4006 ApplyWidgetStyle();
4007 }
4008
4009 bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
4010 {
4011 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
4012
4013 if (!wxWindowBase::SetBackgroundColour(colour))
4014 return FALSE;
4015
4016 GdkWindow *window = (GdkWindow*) NULL;
4017 if (m_wxwindow)
4018 window = GTK_PIZZA(m_wxwindow)->bin_window;
4019 else
4020 window = GetConnectWidget()->window;
4021
4022 if (!window)
4023 {
4024 // indicate that a new style has been set
4025 // but it couldn't get applied as the
4026 // widget hasn't been realized yet.
4027 m_delayedBackgroundColour = TRUE;
4028 return TRUE;
4029 }
4030 else
4031 {
4032 GtkSetBackgroundColour( colour );
4033 }
4034
4035 return TRUE;
4036 }
4037
4038 void wxWindowGTK::GtkSetForegroundColour( const wxColour &colour )
4039 {
4040 GdkWindow *window = (GdkWindow*) NULL;
4041 if (m_wxwindow)
4042 window = GTK_PIZZA(m_wxwindow)->bin_window;
4043 else
4044 window = GetConnectWidget()->window;
4045
4046 wxASSERT( window );
4047
4048 ApplyWidgetStyle();
4049 }
4050
4051 bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
4052 {
4053 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
4054
4055 if (!wxWindowBase::SetForegroundColour(colour))
4056 {
4057 // don't leave if the GTK widget has just
4058 // been realized
4059 if (!m_delayedForegroundColour) return FALSE;
4060 }
4061
4062 GdkWindow *window = (GdkWindow*) NULL;
4063 if (m_wxwindow)
4064 window = GTK_PIZZA(m_wxwindow)->bin_window;
4065 else
4066 window = GetConnectWidget()->window;
4067
4068 if (!window)
4069 {
4070 // indicate that a new style has been set
4071 // but it couldn't get applied as the
4072 // widget hasn't been realized yet.
4073 m_delayedForegroundColour = TRUE;
4074 }
4075 else
4076 {
4077 GtkSetForegroundColour( colour );
4078 }
4079
4080 return TRUE;
4081 }
4082
4083 #ifdef __WXGTK20__
4084 PangoContext *wxWindowGTK::GtkGetPangoDefaultContext()
4085 {
4086 return gtk_widget_get_pango_context( m_widget );
4087 }
4088
4089 PangoContext *wxWindowGTK::GtkGetPangoX11Context()
4090 {
4091 if (!m_x11Context)
4092 m_x11Context = pango_x_get_context( gdk_display );
4093
4094 return m_x11Context;
4095 }
4096 #endif
4097
4098 GtkStyle *wxWindowGTK::GetWidgetStyle()
4099 {
4100 if (m_widgetStyle)
4101 {
4102 GtkStyle *remake = gtk_style_copy( m_widgetStyle );
4103
4104 // FIXME: no more klass in 2.0
4105 #ifndef __WXGTK20__
4106 remake->klass = m_widgetStyle->klass;
4107 #endif
4108
4109 gtk_style_unref( m_widgetStyle );
4110 m_widgetStyle = remake;
4111 }
4112 else
4113 {
4114 GtkStyle *def = gtk_rc_get_style( m_widget );
4115
4116 if (!def)
4117 def = gtk_widget_get_default_style();
4118
4119 m_widgetStyle = gtk_style_copy( def );
4120
4121 // FIXME: no more klass in 2.0
4122 #ifndef __WXGTK20__
4123 m_widgetStyle->klass = def->klass;
4124 #endif
4125 }
4126
4127 return m_widgetStyle;
4128 }
4129
4130 void wxWindowGTK::SetWidgetStyle()
4131 {
4132 #if DISABLE_STYLE_IF_BROKEN_THEME
4133 if (m_widget->style->engine_data)
4134 {
4135 static bool s_warningPrinted = FALSE;
4136 if (!s_warningPrinted)
4137 {
4138 printf( "wxWindows warning: Widget styles disabled due to buggy GTK theme.\n" );
4139 s_warningPrinted = TRUE;
4140 }
4141 m_widgetStyle = m_widget->style;
4142 return;
4143 }
4144 #endif
4145
4146 GtkStyle *style = GetWidgetStyle();
4147
4148 if (m_font != wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ))
4149 {
4150 SET_STYLE_FONT(style, m_font.GetInternalFont( 1.0 ));
4151 }
4152
4153 if (m_foregroundColour.Ok())
4154 {
4155 m_foregroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) );
4156 if (m_foregroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT))
4157 {
4158 style->fg[GTK_STATE_NORMAL] = *m_foregroundColour.GetColor();
4159 style->fg[GTK_STATE_PRELIGHT] = *m_foregroundColour.GetColor();
4160 style->fg[GTK_STATE_ACTIVE] = *m_foregroundColour.GetColor();
4161 }
4162 else
4163 {
4164 // Try to restore the gtk default style. This is still a little
4165 // oversimplified for what is probably really needed here for controls
4166 // other than buttons, but is better than not being able to (re)set a
4167 // control's foreground colour to *wxBLACK -- RL
4168 GtkStyle *def = gtk_rc_get_style( m_widget );
4169
4170 if (!def)
4171 def = gtk_widget_get_default_style();
4172
4173 style->fg[GTK_STATE_NORMAL] = def->fg[GTK_STATE_NORMAL];
4174 style->fg[GTK_STATE_PRELIGHT] = def->fg[GTK_STATE_PRELIGHT];
4175 style->fg[GTK_STATE_ACTIVE] = def->fg[GTK_STATE_ACTIVE];
4176 }
4177 }
4178
4179 if (m_backgroundColour.Ok())
4180 {
4181 m_backgroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) );
4182 if (m_backgroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE))
4183 {
4184 style->bg[GTK_STATE_NORMAL] = *m_backgroundColour.GetColor();
4185 style->base[GTK_STATE_NORMAL] = *m_backgroundColour.GetColor();
4186 style->bg[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor();
4187 style->base[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor();
4188 style->bg[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor();
4189 style->base[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor();
4190 style->bg[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor();
4191 style->base[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor();
4192 }
4193 else
4194 {
4195 // Try to restore the gtk default style. This is still a little
4196 // oversimplified for what is probably really needed here for controls
4197 // other than buttons, but is better than not being able to (re)set a
4198 // control's background colour to default grey and means resetting a
4199 // button to wxSYS_COLOUR_BTNFACE will restore its usual highlighting
4200 // behavior -- RL
4201 GtkStyle *def = gtk_rc_get_style( m_widget );
4202
4203 if (!def)
4204 def = gtk_widget_get_default_style();
4205
4206 style->bg[GTK_STATE_NORMAL] = def->bg[GTK_STATE_NORMAL];
4207 style->base[GTK_STATE_NORMAL] = def->base[GTK_STATE_NORMAL];
4208 style->bg[GTK_STATE_PRELIGHT] = def->bg[GTK_STATE_PRELIGHT];
4209 style->base[GTK_STATE_PRELIGHT] = def->base[GTK_STATE_PRELIGHT];
4210 style->bg[GTK_STATE_ACTIVE] = def->bg[GTK_STATE_ACTIVE];
4211 style->base[GTK_STATE_ACTIVE] = def->base[GTK_STATE_ACTIVE];
4212 style->bg[GTK_STATE_INSENSITIVE] = def->bg[GTK_STATE_INSENSITIVE];
4213 style->base[GTK_STATE_INSENSITIVE] = def->base[GTK_STATE_INSENSITIVE];
4214 }
4215 }
4216 }
4217
4218 void wxWindowGTK::ApplyWidgetStyle()
4219 {
4220 }
4221
4222 //-----------------------------------------------------------------------------
4223 // Pop-up menu stuff
4224 //-----------------------------------------------------------------------------
4225
4226 #if wxUSE_MENUS_NATIVE
4227
4228 extern "C"
4229 void gtk_pop_hide_callback( GtkWidget *WXUNUSED(widget), bool* is_waiting )
4230 {
4231 *is_waiting = FALSE;
4232 }
4233
4234 static void SetInvokingWindow( wxMenu *menu, wxWindowGTK *win )
4235 {
4236 menu->SetInvokingWindow( win );
4237 wxMenuItemList::Node *node = menu->GetMenuItems().GetFirst();
4238 while (node)
4239 {
4240 wxMenuItem *menuitem = node->GetData();
4241 if (menuitem->IsSubMenu())
4242 {
4243 SetInvokingWindow( menuitem->GetSubMenu(), win );
4244 }
4245
4246 node = node->GetNext();
4247 }
4248 }
4249
4250 // used to pass the coordinates from wxWindowGTK::DoPopupMenu() to
4251 // wxPopupMenuPositionCallback()
4252 //
4253 // should be safe even in the MT case as the user can hardly popup 2 menus
4254 // simultaneously, can he?
4255 static gint gs_pop_x = 0;
4256 static gint gs_pop_y = 0;
4257
4258 extern "C" void wxPopupMenuPositionCallback( GtkMenu *menu,
4259 gint *x, gint *y,
4260 #ifdef __WXGTK20__
4261 gboolean * WXUNUSED(whatever),
4262 #endif
4263 gpointer WXUNUSED(user_data) )
4264 {
4265 // ensure that the menu appears entirely on screen
4266 GtkRequisition req;
4267 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &req);
4268
4269 wxSize sizeScreen = wxGetDisplaySize();
4270
4271 gint xmax = sizeScreen.x - req.width,
4272 ymax = sizeScreen.y - req.height;
4273
4274 *x = gs_pop_x < xmax ? gs_pop_x : xmax;
4275 *y = gs_pop_y < ymax ? gs_pop_y : ymax;
4276 }
4277
4278 bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
4279 {
4280 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
4281
4282 wxCHECK_MSG( menu != NULL, FALSE, wxT("invalid popup-menu") );
4283
4284 SetInvokingWindow( menu, this );
4285
4286 menu->UpdateUI();
4287
4288 gs_pop_x = x;
4289 gs_pop_y = y;
4290 ClientToScreen( &gs_pop_x, &gs_pop_y );
4291
4292 bool is_waiting = TRUE;
4293
4294 gtk_signal_connect( GTK_OBJECT(menu->m_menu),
4295 "hide",
4296 GTK_SIGNAL_FUNC(gtk_pop_hide_callback),
4297 (gpointer)&is_waiting );
4298
4299 gtk_menu_popup(
4300 GTK_MENU(menu->m_menu),
4301 (GtkWidget *) NULL, // parent menu shell
4302 (GtkWidget *) NULL, // parent menu item
4303 wxPopupMenuPositionCallback, // function to position it
4304 NULL, // client data
4305 0, // button used to activate it
4306 gs_timeLastClick // the time of activation
4307 );
4308
4309 while (is_waiting)
4310 {
4311 while (gtk_events_pending())
4312 gtk_main_iteration();
4313 }
4314
4315 return TRUE;
4316 }
4317
4318 #endif // wxUSE_MENUS_NATIVE
4319
4320 #if wxUSE_DRAG_AND_DROP
4321
4322 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
4323 {
4324 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4325
4326 GtkWidget *dnd_widget = GetConnectWidget();
4327
4328 if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
4329
4330 if (m_dropTarget) delete m_dropTarget;
4331 m_dropTarget = dropTarget;
4332
4333 if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
4334 }
4335
4336 #endif // wxUSE_DRAG_AND_DROP
4337
4338 GtkWidget* wxWindowGTK::GetConnectWidget()
4339 {
4340 GtkWidget *connect_widget = m_widget;
4341 if (m_wxwindow) connect_widget = m_wxwindow;
4342
4343 return connect_widget;
4344 }
4345
4346 bool wxWindowGTK::IsOwnGtkWindow( GdkWindow *window )
4347 {
4348 if (m_wxwindow)
4349 return (window == GTK_PIZZA(m_wxwindow)->bin_window);
4350
4351 return (window == m_widget->window);
4352 }
4353
4354 bool wxWindowGTK::SetFont( const wxFont &font )
4355 {
4356 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
4357
4358 if (!wxWindowBase::SetFont(font))
4359 {
4360 return FALSE;
4361 }
4362
4363 wxColour sysbg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
4364 if ( sysbg == m_backgroundColour )
4365 {
4366 m_backgroundColour = wxNullColour;
4367 ApplyWidgetStyle();
4368 m_backgroundColour = sysbg;
4369 }
4370 else
4371 {
4372 ApplyWidgetStyle();
4373 }
4374
4375 return TRUE;
4376 }
4377
4378 void wxWindowGTK::DoCaptureMouse()
4379 {
4380 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4381
4382 GdkWindow *window = (GdkWindow*) NULL;
4383 if (m_wxwindow)
4384 window = GTK_PIZZA(m_wxwindow)->bin_window;
4385 else
4386 window = GetConnectWidget()->window;
4387
4388 wxCHECK_RET( window, _T("CaptureMouse() failed") );
4389
4390 wxCursor* cursor = & m_cursor;
4391 if (!cursor->Ok())
4392 cursor = wxSTANDARD_CURSOR;
4393
4394 gdk_pointer_grab( window, FALSE,
4395 (GdkEventMask)
4396 (GDK_BUTTON_PRESS_MASK |
4397 GDK_BUTTON_RELEASE_MASK |
4398 GDK_POINTER_MOTION_HINT_MASK |
4399 GDK_POINTER_MOTION_MASK),
4400 (GdkWindow *) NULL,
4401 cursor->GetCursor(),
4402 (guint32)GDK_CURRENT_TIME );
4403 g_captureWindow = this;
4404 g_captureWindowHasMouse = TRUE;
4405 }
4406
4407 void wxWindowGTK::DoReleaseMouse()
4408 {
4409 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4410
4411 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
4412
4413 g_captureWindow = (wxWindowGTK*) NULL;
4414
4415 GdkWindow *window = (GdkWindow*) NULL;
4416 if (m_wxwindow)
4417 window = GTK_PIZZA(m_wxwindow)->bin_window;
4418 else
4419 window = GetConnectWidget()->window;
4420
4421 if (!window)
4422 return;
4423
4424 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
4425 }
4426
4427 /* static */
4428 wxWindow *wxWindowBase::GetCapture()
4429 {
4430 return (wxWindow *)g_captureWindow;
4431 }
4432
4433 bool wxWindowGTK::IsRetained() const
4434 {
4435 return FALSE;
4436 }
4437
4438 void wxWindowGTK::SetScrollbar( int orient, int pos, int thumbVisible,
4439 int range, bool refresh )
4440 {
4441 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4442
4443 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4444
4445 m_hasScrolling = TRUE;
4446
4447 if (orient == wxHORIZONTAL)
4448 {
4449 float fpos = (float)pos;
4450 float frange = (float)range;
4451 float fthumb = (float)thumbVisible;
4452 if (fpos > frange-fthumb) fpos = frange-fthumb;
4453 if (fpos < 0.0) fpos = 0.0;
4454
4455 if ((fabs(frange-m_hAdjust->upper) < 0.2) &&
4456 (fabs(fthumb-m_hAdjust->page_size) < 0.2))
4457 {
4458 SetScrollPos( orient, pos, refresh );
4459 return;
4460 }
4461
4462 m_oldHorizontalPos = fpos;
4463
4464 m_hAdjust->lower = 0.0;
4465 m_hAdjust->upper = frange;
4466 m_hAdjust->value = fpos;
4467 m_hAdjust->step_increment = 1.0;
4468 m_hAdjust->page_increment = (float)(wxMax(fthumb,0));
4469 m_hAdjust->page_size = fthumb;
4470 }
4471 else
4472 {
4473 float fpos = (float)pos;
4474 float frange = (float)range;
4475 float fthumb = (float)thumbVisible;
4476 if (fpos > frange-fthumb) fpos = frange-fthumb;
4477 if (fpos < 0.0) fpos = 0.0;
4478
4479 if ((fabs(frange-m_vAdjust->upper) < 0.2) &&
4480 (fabs(fthumb-m_vAdjust->page_size) < 0.2))
4481 {
4482 SetScrollPos( orient, pos, refresh );
4483 return;
4484 }
4485
4486 m_oldVerticalPos = fpos;
4487
4488 m_vAdjust->lower = 0.0;
4489 m_vAdjust->upper = frange;
4490 m_vAdjust->value = fpos;
4491 m_vAdjust->step_increment = 1.0;
4492 m_vAdjust->page_increment = (float)(wxMax(fthumb,0));
4493 m_vAdjust->page_size = fthumb;
4494 }
4495
4496 if (orient == wxHORIZONTAL)
4497 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "changed" );
4498 else
4499 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "changed" );
4500 }
4501
4502 void wxWindowGTK::SetScrollPos( int orient, int pos, bool WXUNUSED(refresh) )
4503 {
4504 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4505
4506 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4507
4508 if (orient == wxHORIZONTAL)
4509 {
4510 float fpos = (float)pos;
4511 if (fpos > m_hAdjust->upper - m_hAdjust->page_size) fpos = m_hAdjust->upper - m_hAdjust->page_size;
4512 if (fpos < 0.0) fpos = 0.0;
4513 m_oldHorizontalPos = fpos;
4514
4515 if (fabs(fpos-m_hAdjust->value) < 0.2) return;
4516 m_hAdjust->value = fpos;
4517 }
4518 else
4519 {
4520 float fpos = (float)pos;
4521 if (fpos > m_vAdjust->upper - m_vAdjust->page_size) fpos = m_vAdjust->upper - m_vAdjust->page_size;
4522 if (fpos < 0.0) fpos = 0.0;
4523 m_oldVerticalPos = fpos;
4524
4525 if (fabs(fpos-m_vAdjust->value) < 0.2) return;
4526 m_vAdjust->value = fpos;
4527 }
4528
4529 if (m_wxwindow->window)
4530 {
4531 if (orient == wxHORIZONTAL)
4532 {
4533 gtk_signal_disconnect_by_func( GTK_OBJECT(m_hAdjust),
4534 (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this );
4535
4536 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "value_changed" );
4537
4538 gtk_signal_connect( GTK_OBJECT(m_hAdjust), "value_changed",
4539 (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this );
4540 }
4541 else
4542 {
4543 gtk_signal_disconnect_by_func( GTK_OBJECT(m_vAdjust),
4544 (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this );
4545
4546 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "value_changed" );
4547
4548 gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed",
4549 (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this );
4550 }
4551 }
4552 }
4553
4554 int wxWindowGTK::GetScrollThumb( int orient ) const
4555 {
4556 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4557
4558 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4559
4560 if (orient == wxHORIZONTAL)
4561 return (int)(m_hAdjust->page_size+0.5);
4562 else
4563 return (int)(m_vAdjust->page_size+0.5);
4564 }
4565
4566 int wxWindowGTK::GetScrollPos( int orient ) const
4567 {
4568 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4569
4570 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4571
4572 if (orient == wxHORIZONTAL)
4573 return (int)(m_hAdjust->value+0.5);
4574 else
4575 return (int)(m_vAdjust->value+0.5);
4576 }
4577
4578 int wxWindowGTK::GetScrollRange( int orient ) const
4579 {
4580 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4581
4582 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4583
4584 if (orient == wxHORIZONTAL)
4585 return (int)(m_hAdjust->upper+0.5);
4586 else
4587 return (int)(m_vAdjust->upper+0.5);
4588 }
4589
4590 void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
4591 {
4592 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4593
4594 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4595
4596 // No scrolling requested.
4597 if ((dx == 0) && (dy == 0)) return;
4598
4599 #ifndef __WXGTK20__
4600 if (!m_updateRegion.IsEmpty())
4601 {
4602 m_updateRegion.Offset( dx, dy );
4603
4604 int cw = 0;
4605 int ch = 0;
4606 GetClientSize( &cw, &ch );
4607 m_updateRegion.Intersect( 0, 0, cw, ch );
4608 }
4609
4610 if (!m_clearRegion.IsEmpty())
4611 {
4612 m_clearRegion.Offset( dx, dy );
4613
4614 int cw = 0;
4615 int ch = 0;
4616 GetClientSize( &cw, &ch );
4617 m_clearRegion.Intersect( 0, 0, cw, ch );
4618 }
4619 #endif
4620
4621 m_clipPaintRegion = TRUE;
4622
4623 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy );
4624
4625 m_clipPaintRegion = FALSE;
4626 }
4627
4628
4629 // Find the wxWindow at the current mouse position, also returning the mouse
4630 // position.
4631 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4632 {
4633 pt = wxGetMousePosition();
4634 wxWindow* found = wxFindWindowAtPoint(pt);
4635 return found;
4636 }
4637
4638 // Get the current mouse position.
4639 wxPoint wxGetMousePosition()
4640 {
4641 /* This crashes when used within wxHelpContext,
4642 so we have to use the X-specific implementation below.
4643 gint x, y;
4644 GdkModifierType *mask;
4645 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4646
4647 return wxPoint(x, y);
4648 */
4649
4650 int x, y;
4651 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
4652
4653 Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY();
4654 Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
4655 Window rootReturn, childReturn;
4656 int rootX, rootY, winX, winY;
4657 unsigned int maskReturn;
4658
4659 XQueryPointer (display,
4660 rootWindow,
4661 &rootReturn,
4662 &childReturn,
4663 &rootX, &rootY, &winX, &winY, &maskReturn);
4664 return wxPoint(rootX, rootY);
4665
4666 }
4667
4668 // ----------------------------------------------------------------------------
4669 // wxDCModule
4670 // ----------------------------------------------------------------------------
4671
4672 class wxWinModule : public wxModule
4673 {
4674 public:
4675 bool OnInit();
4676 void OnExit();
4677
4678 private:
4679 DECLARE_DYNAMIC_CLASS(wxWinModule)
4680 };
4681
4682 IMPLEMENT_DYNAMIC_CLASS(wxWinModule, wxModule)
4683
4684 bool wxWinModule::OnInit()
4685 {
4686 // g_eraseGC = gdk_gc_new( GDK_ROOT_PARENT() );
4687 // gdk_gc_set_fill( g_eraseGC, GDK_SOLID );
4688
4689 return TRUE;
4690 }
4691
4692 void wxWinModule::OnExit()
4693 {
4694 if (g_eraseGC)
4695 gdk_gc_unref( g_eraseGC );
4696 }
4697
4698 // vi:sts=4:sw=4:et