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