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