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