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