]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/window.cpp
dc244f803ac6e912935fc9696c18d6ba5295104a
[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 RealizeTabOrder();
2958
2959 // Update style if the window was not yet realized
2960 // and SetBackgroundStyle(wxBG_STYLE_CUSTOM) was called
2961 if (m_needsStyleChange)
2962 {
2963 SetBackgroundStyle(GetBackgroundStyle());
2964 m_needsStyleChange = false;
2965 }
2966
2967 // Update invalidated regions.
2968 GtkUpdate();
2969
2970 wxCursor cursor = m_cursor;
2971 if (g_globalCursor.Ok()) cursor = g_globalCursor;
2972
2973 if (cursor.Ok())
2974 {
2975 /* I now set the cursor anew in every OnInternalIdle call
2976 as setting the cursor in a parent window also effects the
2977 windows above so that checking for the current cursor is
2978 not possible. */
2979
2980 if (m_wxwindow)
2981 {
2982 GdkWindow *window = GTK_PIZZA(m_wxwindow)->bin_window;
2983 if (window)
2984 gdk_window_set_cursor( window, cursor.GetCursor() );
2985
2986 if (!g_globalCursor.Ok())
2987 cursor = *wxSTANDARD_CURSOR;
2988
2989 window = m_widget->window;
2990 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
2991 gdk_window_set_cursor( window, cursor.GetCursor() );
2992
2993 }
2994 else
2995 {
2996
2997 GdkWindow *window = m_widget->window;
2998 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
2999 gdk_window_set_cursor( window, cursor.GetCursor() );
3000
3001 }
3002 }
3003
3004 if (wxUpdateUIEvent::CanUpdate(this))
3005 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
3006 }
3007
3008 void wxWindowGTK::DoGetSize( int *width, int *height ) const
3009 {
3010 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3011
3012 if (width) (*width) = m_width;
3013 if (height) (*height) = m_height;
3014 }
3015
3016 void wxWindowGTK::DoSetClientSize( int width, int height )
3017 {
3018 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3019
3020 if (!m_wxwindow)
3021 {
3022 SetSize( width, height );
3023 }
3024 else
3025 {
3026 int dw = 0;
3027 int dh = 0;
3028
3029 #ifndef __WXUNIVERSAL__
3030 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3031 {
3032 /* when using GTK 1.2 we set the shadow border size to 2 */
3033 dw += 2 * 2;
3034 dh += 2 * 2;
3035 }
3036 if (HasFlag(wxSIMPLE_BORDER))
3037 {
3038 /* when using GTK 1.2 we set the simple border size to 1 */
3039 dw += 1 * 2;
3040 dh += 1 * 2;
3041 }
3042 #endif // __WXUNIVERSAL__
3043
3044 if (m_hasScrolling)
3045 {
3046 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3047
3048 GtkRequisition vscroll_req;
3049 vscroll_req.width = 2;
3050 vscroll_req.height = 2;
3051 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3052 (scroll_window->vscrollbar, &vscroll_req );
3053
3054 GtkRequisition hscroll_req;
3055 hscroll_req.width = 2;
3056 hscroll_req.height = 2;
3057 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3058 (scroll_window->hscrollbar, &hscroll_req );
3059
3060 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3061
3062 if (scroll_window->vscrollbar_visible)
3063 {
3064 dw += vscroll_req.width;
3065 dw += scroll_class->scrollbar_spacing;
3066 }
3067
3068 if (scroll_window->hscrollbar_visible)
3069 {
3070 dh += hscroll_req.height;
3071 dh += scroll_class->scrollbar_spacing;
3072 }
3073 }
3074
3075 SetSize( width+dw, height+dh );
3076 }
3077 }
3078
3079 void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
3080 {
3081 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3082
3083 if (!m_wxwindow)
3084 {
3085 if (width) (*width) = m_width;
3086 if (height) (*height) = m_height;
3087 }
3088 else
3089 {
3090 int dw = 0;
3091 int dh = 0;
3092
3093 #ifndef __WXUNIVERSAL__
3094 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3095 {
3096 /* when using GTK 1.2 we set the shadow border size to 2 */
3097 dw += 2 * 2;
3098 dh += 2 * 2;
3099 }
3100 if (HasFlag(wxSIMPLE_BORDER))
3101 {
3102 /* when using GTK 1.2 we set the simple border size to 1 */
3103 dw += 1 * 2;
3104 dh += 1 * 2;
3105 }
3106 #endif // __WXUNIVERSAL__
3107
3108 if (m_hasScrolling)
3109 {
3110 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3111
3112 GtkRequisition vscroll_req;
3113 vscroll_req.width = 2;
3114 vscroll_req.height = 2;
3115 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3116 (scroll_window->vscrollbar, &vscroll_req );
3117
3118 GtkRequisition hscroll_req;
3119 hscroll_req.width = 2;
3120 hscroll_req.height = 2;
3121 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3122 (scroll_window->hscrollbar, &hscroll_req );
3123
3124 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3125
3126 if (scroll_window->vscrollbar_visible)
3127 {
3128 dw += vscroll_req.width;
3129 dw += scroll_class->scrollbar_spacing;
3130 }
3131
3132 if (scroll_window->hscrollbar_visible)
3133 {
3134 dh += hscroll_req.height;
3135 dh += scroll_class->scrollbar_spacing;
3136 }
3137 }
3138
3139 if (width) (*width) = m_width - dw;
3140 if (height) (*height) = m_height - dh;
3141 }
3142
3143 /*
3144 printf( "GetClientSize, name %s ", GetName().c_str() );
3145 if (width) printf( " width = %d", (*width) );
3146 if (height) printf( " height = %d", (*height) );
3147 printf( "\n" );
3148 */
3149 }
3150
3151 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
3152 {
3153 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3154
3155 int dx = 0;
3156 int dy = 0;
3157 if (m_parent && m_parent->m_wxwindow)
3158 {
3159 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
3160 dx = pizza->xoffset;
3161 dy = pizza->yoffset;
3162 }
3163
3164 if (x) (*x) = m_x - dx;
3165 if (y) (*y) = m_y - dy;
3166 }
3167
3168 void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
3169 {
3170 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3171
3172 if (!m_widget->window) return;
3173
3174 GdkWindow *source = (GdkWindow *) NULL;
3175 if (m_wxwindow)
3176 source = GTK_PIZZA(m_wxwindow)->bin_window;
3177 else
3178 source = m_widget->window;
3179
3180 int org_x = 0;
3181 int org_y = 0;
3182 gdk_window_get_origin( source, &org_x, &org_y );
3183
3184 if (!m_wxwindow)
3185 {
3186 if (GTK_WIDGET_NO_WINDOW (m_widget))
3187 {
3188 org_x += m_widget->allocation.x;
3189 org_y += m_widget->allocation.y;
3190 }
3191 }
3192
3193 if (x) *x += org_x;
3194 if (y) *y += org_y;
3195 }
3196
3197 void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
3198 {
3199 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3200
3201 if (!m_widget->window) return;
3202
3203 GdkWindow *source = (GdkWindow *) NULL;
3204 if (m_wxwindow)
3205 source = GTK_PIZZA(m_wxwindow)->bin_window;
3206 else
3207 source = m_widget->window;
3208
3209 int org_x = 0;
3210 int org_y = 0;
3211 gdk_window_get_origin( source, &org_x, &org_y );
3212
3213 if (!m_wxwindow)
3214 {
3215 if (GTK_WIDGET_NO_WINDOW (m_widget))
3216 {
3217 org_x += m_widget->allocation.x;
3218 org_y += m_widget->allocation.y;
3219 }
3220 }
3221
3222 if (x) *x -= org_x;
3223 if (y) *y -= org_y;
3224 }
3225
3226 bool wxWindowGTK::Show( bool show )
3227 {
3228 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3229
3230 if (!wxWindowBase::Show(show))
3231 {
3232 // nothing to do
3233 return false;
3234 }
3235
3236 if (show)
3237 gtk_widget_show( m_widget );
3238 else
3239 gtk_widget_hide( m_widget );
3240
3241 wxShowEvent eventShow(GetId(), show);
3242 eventShow.SetEventObject(this);
3243
3244 GetEventHandler()->ProcessEvent(eventShow);
3245
3246 return true;
3247 }
3248
3249 static void wxWindowNotifyEnable(wxWindowGTK* win, bool enable)
3250 {
3251 win->OnParentEnable(enable);
3252
3253 // Recurse, so that children have the opportunity to Do The Right Thing
3254 // and reset colours that have been messed up by a parent's (really ancestor's)
3255 // Enable call
3256 for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
3257 node;
3258 node = node->GetNext() )
3259 {
3260 wxWindow *child = node->GetData();
3261 if (!child->IsKindOf(CLASSINFO(wxDialog)) && !child->IsKindOf(CLASSINFO(wxFrame)))
3262 wxWindowNotifyEnable(child, enable);
3263 }
3264 }
3265
3266 bool wxWindowGTK::Enable( bool enable )
3267 {
3268 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3269
3270 if (!wxWindowBase::Enable(enable))
3271 {
3272 // nothing to do
3273 return false;
3274 }
3275
3276 gtk_widget_set_sensitive( m_widget, enable );
3277 if ( m_wxwindow )
3278 gtk_widget_set_sensitive( m_wxwindow, enable );
3279
3280 wxWindowNotifyEnable(this, enable);
3281
3282 return true;
3283 }
3284
3285 int wxWindowGTK::GetCharHeight() const
3286 {
3287 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
3288
3289 wxFont font = GetFont();
3290 wxCHECK_MSG( font.Ok(), 12, wxT("invalid font") );
3291
3292 PangoContext *context = NULL;
3293 if (m_widget)
3294 context = gtk_widget_get_pango_context( m_widget );
3295
3296 if (!context)
3297 return 0;
3298
3299 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3300 PangoLayout *layout = pango_layout_new(context);
3301 pango_layout_set_font_description(layout, desc);
3302 pango_layout_set_text(layout, "H", 1);
3303 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3304
3305 PangoRectangle rect;
3306 pango_layout_line_get_extents(line, NULL, &rect);
3307
3308 g_object_unref( G_OBJECT( layout ) );
3309
3310 return (int) PANGO_PIXELS(rect.height);
3311 }
3312
3313 int wxWindowGTK::GetCharWidth() const
3314 {
3315 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
3316
3317 wxFont font = GetFont();
3318 wxCHECK_MSG( font.Ok(), 8, wxT("invalid font") );
3319
3320 PangoContext *context = NULL;
3321 if (m_widget)
3322 context = gtk_widget_get_pango_context( m_widget );
3323
3324 if (!context)
3325 return 0;
3326
3327 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3328 PangoLayout *layout = pango_layout_new(context);
3329 pango_layout_set_font_description(layout, desc);
3330 pango_layout_set_text(layout, "g", 1);
3331 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3332
3333 PangoRectangle rect;
3334 pango_layout_line_get_extents(line, NULL, &rect);
3335
3336 g_object_unref( G_OBJECT( layout ) );
3337
3338 return (int) PANGO_PIXELS(rect.width);
3339 }
3340
3341 void wxWindowGTK::GetTextExtent( const wxString& string,
3342 int *x,
3343 int *y,
3344 int *descent,
3345 int *externalLeading,
3346 const wxFont *theFont ) const
3347 {
3348 wxFont fontToUse = theFont ? *theFont : GetFont();
3349
3350 wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") );
3351
3352 if (string.empty())
3353 {
3354 if (x) (*x) = 0;
3355 if (y) (*y) = 0;
3356 return;
3357 }
3358
3359 PangoContext *context = NULL;
3360 if (m_widget)
3361 context = gtk_widget_get_pango_context( m_widget );
3362
3363 if (!context)
3364 {
3365 if (x) (*x) = 0;
3366 if (y) (*y) = 0;
3367 return;
3368 }
3369
3370 PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
3371 PangoLayout *layout = pango_layout_new(context);
3372 pango_layout_set_font_description(layout, desc);
3373 {
3374 #if wxUSE_UNICODE
3375 const wxCharBuffer data = wxConvUTF8.cWC2MB( string );
3376 pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data ));
3377 #else
3378 const wxWCharBuffer wdata = wxConvLocal.cMB2WC( string );
3379 const wxCharBuffer data = wxConvUTF8.cWC2MB( wdata );
3380 pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data ));
3381 #endif
3382 }
3383
3384 PangoRectangle rect;
3385 pango_layout_get_extents(layout, NULL, &rect);
3386
3387 if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width);
3388 if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height);
3389 if (descent)
3390 {
3391 PangoLayoutIter *iter = pango_layout_get_iter(layout);
3392 int baseline = pango_layout_iter_get_baseline(iter);
3393 pango_layout_iter_free(iter);
3394 *descent = *y - PANGO_PIXELS(baseline);
3395 }
3396 if (externalLeading) (*externalLeading) = 0; // ??
3397
3398 g_object_unref( G_OBJECT( layout ) );
3399 }
3400
3401 void wxWindowGTK::SetFocus()
3402 {
3403 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3404 if ( m_hasFocus )
3405 {
3406 // don't do anything if we already have focus
3407 return;
3408 }
3409
3410 if (m_wxwindow)
3411 {
3412 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
3413 {
3414 gtk_widget_grab_focus (m_wxwindow);
3415 }
3416 }
3417 else if (m_widget)
3418 {
3419 if (GTK_IS_CONTAINER(m_widget))
3420 {
3421 gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
3422 }
3423 else
3424 if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
3425 {
3426
3427 if (!GTK_WIDGET_REALIZED(m_widget))
3428 {
3429 // we can't set the focus to the widget now so we remember that
3430 // it should be focused and will do it later, during the idle
3431 // time, as soon as we can
3432 wxLogTrace(TRACE_FOCUS,
3433 _T("Delaying setting focus to %s(%s)"),
3434 GetClassInfo()->GetClassName(), GetLabel().c_str());
3435
3436 g_delayedFocus = this;
3437 }
3438 else
3439 {
3440 wxLogTrace(TRACE_FOCUS,
3441 _T("Setting focus to %s(%s)"),
3442 GetClassInfo()->GetClassName(), GetLabel().c_str());
3443
3444 gtk_widget_grab_focus (m_widget);
3445 }
3446 }
3447 else
3448 {
3449 wxLogTrace(TRACE_FOCUS,
3450 _T("Can't set focus to %s(%s)"),
3451 GetClassInfo()->GetClassName(), GetLabel().c_str());
3452 }
3453 }
3454 }
3455
3456 bool wxWindowGTK::AcceptsFocus() const
3457 {
3458 return m_acceptsFocus && wxWindowBase::AcceptsFocus();
3459 }
3460
3461 bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
3462 {
3463 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3464
3465 wxWindowGTK *oldParent = m_parent,
3466 *newParent = (wxWindowGTK *)newParentBase;
3467
3468 wxASSERT( GTK_IS_WIDGET(m_widget) );
3469
3470 if ( !wxWindowBase::Reparent(newParent) )
3471 return false;
3472
3473 wxASSERT( GTK_IS_WIDGET(m_widget) );
3474
3475 /* prevent GTK from deleting the widget arbitrarily */
3476 gtk_widget_ref( m_widget );
3477
3478 if (oldParent)
3479 {
3480 gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
3481 }
3482
3483 wxASSERT( GTK_IS_WIDGET(m_widget) );
3484
3485 if (newParent)
3486 {
3487 /* insert GTK representation */
3488 (*(newParent->m_insertCallback))(newParent, this);
3489 }
3490
3491 /* reverse: prevent GTK from deleting the widget arbitrarily */
3492 gtk_widget_unref( m_widget );
3493
3494 return true;
3495 }
3496
3497 void wxWindowGTK::DoAddChild(wxWindowGTK *child)
3498 {
3499 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
3500
3501 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
3502
3503 wxASSERT_MSG( (m_insertCallback != NULL), wxT("invalid child insertion function") );
3504
3505 /* add to list */
3506 AddChild( child );
3507
3508 /* insert GTK representation */
3509 (*m_insertCallback)(this, child);
3510 }
3511
3512 void wxWindowGTK::AddChild(wxWindowBase *child)
3513 {
3514 wxWindowBase::AddChild(child);
3515 m_dirtyTabOrder = true;
3516 if (g_isIdle)
3517 wxapp_install_idle_handler();
3518 }
3519
3520 void wxWindowGTK::RemoveChild(wxWindowBase *child)
3521 {
3522 wxWindowBase::RemoveChild(child);
3523 m_dirtyTabOrder = true;
3524 if (g_isIdle)
3525 wxapp_install_idle_handler();
3526 }
3527
3528 void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, MoveKind move)
3529 {
3530 wxWindowBase::DoMoveInTabOrder(win, move);
3531 m_dirtyTabOrder = true;
3532 if (g_isIdle)
3533 wxapp_install_idle_handler();
3534 }
3535
3536 void wxWindowGTK::RealizeTabOrder()
3537 {
3538 if (m_wxwindow)
3539 {
3540 if (m_children.size() > 0)
3541 {
3542 GList *chain = NULL;
3543
3544 for (wxWindowList::const_iterator i = m_children.begin();
3545 i != m_children.end(); ++i)
3546 {
3547 chain = g_list_prepend(chain, (*i)->m_widget);
3548 }
3549
3550 chain = g_list_reverse(chain);
3551
3552 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain);
3553 g_list_free(chain);
3554 }
3555 else
3556 {
3557 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow));
3558 }
3559 }
3560
3561 m_dirtyTabOrder = false;
3562 }
3563
3564 void wxWindowGTK::Raise()
3565 {
3566 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3567
3568 if (m_wxwindow && m_wxwindow->window)
3569 {
3570 gdk_window_raise( m_wxwindow->window );
3571 }
3572 else if (m_widget->window)
3573 {
3574 gdk_window_raise( m_widget->window );
3575 }
3576 }
3577
3578 void wxWindowGTK::Lower()
3579 {
3580 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3581
3582 if (m_wxwindow && m_wxwindow->window)
3583 {
3584 gdk_window_lower( m_wxwindow->window );
3585 }
3586 else if (m_widget->window)
3587 {
3588 gdk_window_lower( m_widget->window );
3589 }
3590 }
3591
3592 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
3593 {
3594 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3595
3596 if (cursor == m_cursor)
3597 return false;
3598
3599 if (g_isIdle)
3600 wxapp_install_idle_handler();
3601
3602 if (cursor == wxNullCursor)
3603 return wxWindowBase::SetCursor( *wxSTANDARD_CURSOR );
3604 else
3605 return wxWindowBase::SetCursor( cursor );
3606 }
3607
3608 void wxWindowGTK::WarpPointer( int x, int y )
3609 {
3610 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3611
3612 // We provide this function ourselves as it is
3613 // missing in GDK (top of this file).
3614
3615 GdkWindow *window = (GdkWindow*) NULL;
3616 if (m_wxwindow)
3617 window = GTK_PIZZA(m_wxwindow)->bin_window;
3618 else
3619 window = GetConnectWidget()->window;
3620
3621 if (window)
3622 gdk_window_warp_pointer( window, x, y );
3623 }
3624
3625
3626 void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
3627 {
3628 if (!m_widget)
3629 return;
3630 if (!m_widget->window)
3631 return;
3632
3633 if (m_wxwindow)
3634 {
3635 GdkRectangle gdk_rect,
3636 *p;
3637 if (rect)
3638 {
3639 gdk_rect.x = rect->x;
3640 gdk_rect.y = rect->y;
3641 gdk_rect.width = rect->width;
3642 gdk_rect.height = rect->height;
3643 p = &gdk_rect;
3644 }
3645 else // invalidate everything
3646 {
3647 p = NULL;
3648 }
3649
3650 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, p, TRUE );
3651 }
3652 }
3653
3654 void wxWindowGTK::Update()
3655 {
3656 GtkUpdate();
3657
3658 // when we call Update() we really want to update the window immediately on
3659 // screen, even if it means flushing the entire queue and hence slowing down
3660 // everything -- but it should still be done, it's just that Update() should
3661 // be called very rarely
3662 gdk_flush();
3663 }
3664
3665 void wxWindowGTK::GtkUpdate()
3666 {
3667 if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window)
3668 gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE );
3669
3670 // for consistency with other platforms (and also because it's convenient
3671 // to be able to update an entire TLW by calling Update() only once), we
3672 // should also update all our children here
3673 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
3674 node;
3675 node = node->GetNext() )
3676 {
3677 node->GetData()->GtkUpdate();
3678 }
3679 }
3680
3681 void wxWindowGTK::GtkSendPaintEvents()
3682 {
3683 if (!m_wxwindow)
3684 {
3685 m_updateRegion.Clear();
3686 return;
3687 }
3688
3689 // Clip to paint region in wxClientDC
3690 m_clipPaintRegion = true;
3691
3692 // widget to draw on
3693 GtkPizza *pizza = GTK_PIZZA (m_wxwindow);
3694
3695 if (GetThemeEnabled() && (GetBackgroundStyle() == wxBG_STYLE_SYSTEM))
3696 {
3697 // find ancestor from which to steal background
3698 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
3699 if (!parent)
3700 parent = (wxWindow*)this;
3701
3702 if (GTK_WIDGET_MAPPED(parent->m_widget))
3703 {
3704 wxRegionIterator upd( m_updateRegion );
3705 while (upd)
3706 {
3707 GdkRectangle rect;
3708 rect.x = upd.GetX();
3709 rect.y = upd.GetY();
3710 rect.width = upd.GetWidth();
3711 rect.height = upd.GetHeight();
3712
3713 gtk_paint_flat_box( parent->m_widget->style,
3714 pizza->bin_window,
3715 (GtkStateType)GTK_WIDGET_STATE(m_wxwindow),
3716 GTK_SHADOW_NONE,
3717 &rect,
3718 parent->m_widget,
3719 (char *)"base",
3720 0, 0, -1, -1 );
3721
3722 ++upd;
3723 }
3724 }
3725 }
3726 else
3727
3728 {
3729 wxWindowDC dc( (wxWindow*)this );
3730 dc.SetClippingRegion( m_updateRegion );
3731
3732 wxEraseEvent erase_event( GetId(), &dc );
3733 erase_event.SetEventObject( this );
3734
3735 GetEventHandler()->ProcessEvent(erase_event);
3736 }
3737
3738 wxNcPaintEvent nc_paint_event( GetId() );
3739 nc_paint_event.SetEventObject( this );
3740 GetEventHandler()->ProcessEvent( nc_paint_event );
3741
3742 wxPaintEvent paint_event( GetId() );
3743 paint_event.SetEventObject( this );
3744 GetEventHandler()->ProcessEvent( paint_event );
3745
3746 m_clipPaintRegion = false;
3747
3748 m_updateRegion.Clear();
3749 }
3750
3751 void wxWindowGTK::ClearBackground()
3752 {
3753 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3754 }
3755
3756 #if wxUSE_TOOLTIPS
3757 void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
3758 {
3759 wxWindowBase::DoSetToolTip(tip);
3760
3761 if (m_tooltip)
3762 m_tooltip->Apply( (wxWindow *)this );
3763 }
3764
3765 void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
3766 {
3767 wxString tmp( tip );
3768 gtk_tooltips_set_tip( tips, GetConnectWidget(), wxGTK_CONV(tmp), (gchar*) NULL );
3769 }
3770 #endif // wxUSE_TOOLTIPS
3771
3772 bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
3773 {
3774 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3775
3776 if (!wxWindowBase::SetBackgroundColour(colour))
3777 return false;
3778
3779 if (colour.Ok())
3780 {
3781 // We need the pixel value e.g. for background clearing.
3782 m_backgroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
3783 }
3784
3785 // apply style change (forceStyle=true so that new style is applied
3786 // even if the bg colour changed from valid to wxNullColour)
3787 if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM)
3788 ApplyWidgetStyle(true);
3789
3790 return true;
3791 }
3792
3793 bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
3794 {
3795 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3796
3797 if (!wxWindowBase::SetForegroundColour(colour))
3798 {
3799 return false;
3800 }
3801
3802 if (colour.Ok())
3803 {
3804 // We need the pixel value e.g. for background clearing.
3805 m_foregroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
3806 }
3807
3808 // apply style change (forceStyle=true so that new style is applied
3809 // even if the bg colour changed from valid to wxNullColour):
3810 ApplyWidgetStyle(true);
3811
3812 return true;
3813 }
3814
3815 PangoContext *wxWindowGTK::GtkGetPangoDefaultContext()
3816 {
3817 return gtk_widget_get_pango_context( m_widget );
3818 }
3819
3820 GtkRcStyle *wxWindowGTK::CreateWidgetStyle(bool forceStyle)
3821 {
3822 // do we need to apply any changes at all?
3823 if ( !forceStyle &&
3824 !m_font.Ok() &&
3825 !m_foregroundColour.Ok() && !m_backgroundColour.Ok() )
3826 {
3827 return NULL;
3828 }
3829
3830 GtkRcStyle *style = gtk_rc_style_new();
3831
3832 if ( m_font.Ok() )
3833 {
3834 style->font_desc =
3835 pango_font_description_copy( m_font.GetNativeFontInfo()->description );
3836 }
3837
3838 if ( m_foregroundColour.Ok() )
3839 {
3840 GdkColor *fg = m_foregroundColour.GetColor();
3841
3842 style->fg[GTK_STATE_NORMAL] = *fg;
3843 style->color_flags[GTK_STATE_NORMAL] = GTK_RC_FG;
3844
3845 style->fg[GTK_STATE_PRELIGHT] = *fg;
3846 style->color_flags[GTK_STATE_PRELIGHT] = GTK_RC_FG;
3847
3848 style->fg[GTK_STATE_ACTIVE] = *fg;
3849 style->color_flags[GTK_STATE_ACTIVE] = GTK_RC_FG;
3850 }
3851
3852 if ( m_backgroundColour.Ok() )
3853 {
3854 GdkColor *bg = m_backgroundColour.GetColor();
3855
3856 style->bg[GTK_STATE_NORMAL] = *bg;
3857 style->base[GTK_STATE_NORMAL] = *bg;
3858 style->color_flags[GTK_STATE_NORMAL] = (GtkRcFlags)
3859 (style->color_flags[GTK_STATE_NORMAL] | GTK_RC_BG | GTK_RC_BASE);
3860
3861 style->bg[GTK_STATE_PRELIGHT] = *bg;
3862 style->base[GTK_STATE_PRELIGHT] = *bg;
3863 style->color_flags[GTK_STATE_PRELIGHT] = (GtkRcFlags)
3864 (style->color_flags[GTK_STATE_PRELIGHT] | GTK_RC_BG | GTK_RC_BASE);
3865
3866 style->bg[GTK_STATE_ACTIVE] = *bg;
3867 style->base[GTK_STATE_ACTIVE] = *bg;
3868 style->color_flags[GTK_STATE_ACTIVE] = (GtkRcFlags)
3869 (style->color_flags[GTK_STATE_ACTIVE] | GTK_RC_BG | GTK_RC_BASE);
3870
3871 style->bg[GTK_STATE_INSENSITIVE] = *bg;
3872 style->base[GTK_STATE_INSENSITIVE] = *bg;
3873 style->color_flags[GTK_STATE_INSENSITIVE] = (GtkRcFlags)
3874 (style->color_flags[GTK_STATE_INSENSITIVE] | GTK_RC_BG | GTK_RC_BASE);
3875 }
3876
3877 return style;
3878 }
3879
3880 void wxWindowGTK::ApplyWidgetStyle(bool forceStyle)
3881 {
3882 GtkRcStyle *style = CreateWidgetStyle(forceStyle);
3883 if ( style )
3884 {
3885 DoApplyWidgetStyle(style);
3886 gtk_rc_style_unref(style);
3887 }
3888
3889 // Style change may affect GTK+'s size calculation:
3890 InvalidateBestSize();
3891 }
3892
3893 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
3894 {
3895 if (m_wxwindow)
3896 gtk_widget_modify_style(m_wxwindow, style);
3897 else
3898 gtk_widget_modify_style(m_widget, style);
3899 }
3900
3901 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
3902 {
3903 wxWindowBase::SetBackgroundStyle(style);
3904
3905 if (style == wxBG_STYLE_CUSTOM)
3906 {
3907 GdkWindow *window = (GdkWindow*) NULL;
3908 if (m_wxwindow)
3909 window = GTK_PIZZA(m_wxwindow)->bin_window;
3910 else
3911 window = GetConnectWidget()->window;
3912
3913 if (window)
3914 {
3915 // Make sure GDK/X11 doesn't refresh the window
3916 // automatically.
3917 gdk_window_set_back_pixmap( window, None, False );
3918 #ifdef __X__
3919 Display* display = GDK_WINDOW_DISPLAY(window);
3920 XFlush(display);
3921 #endif
3922 m_needsStyleChange = false;
3923 }
3924 else
3925 // Do in OnIdle, because the window is not yet available
3926 m_needsStyleChange = true;
3927
3928 // Don't apply widget style, or we get a grey background
3929 }
3930 else
3931 {
3932 // apply style change (forceStyle=true so that new style is applied
3933 // even if the bg colour changed from valid to wxNullColour):
3934 ApplyWidgetStyle(true);
3935 }
3936 return true;
3937 }
3938
3939 #if wxUSE_DRAG_AND_DROP
3940
3941 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
3942 {
3943 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3944
3945 GtkWidget *dnd_widget = GetConnectWidget();
3946
3947 if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
3948
3949 if (m_dropTarget) delete m_dropTarget;
3950 m_dropTarget = dropTarget;
3951
3952 if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
3953 }
3954
3955 #endif // wxUSE_DRAG_AND_DROP
3956
3957 GtkWidget* wxWindowGTK::GetConnectWidget()
3958 {
3959 GtkWidget *connect_widget = m_widget;
3960 if (m_wxwindow) connect_widget = m_wxwindow;
3961
3962 return connect_widget;
3963 }
3964
3965 bool wxWindowGTK::IsOwnGtkWindow( GdkWindow *window )
3966 {
3967 if (m_wxwindow)
3968 return (window == GTK_PIZZA(m_wxwindow)->bin_window);
3969
3970 return (window == m_widget->window);
3971 }
3972
3973 bool wxWindowGTK::SetFont( const wxFont &font )
3974 {
3975 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3976
3977 if (!wxWindowBase::SetFont(font))
3978 return false;
3979
3980 // apply style change (forceStyle=true so that new style is applied
3981 // even if the font changed from valid to wxNullFont):
3982 ApplyWidgetStyle(true);
3983
3984 return true;
3985 }
3986
3987 void wxWindowGTK::DoCaptureMouse()
3988 {
3989 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3990
3991 GdkWindow *window = (GdkWindow*) NULL;
3992 if (m_wxwindow)
3993 window = GTK_PIZZA(m_wxwindow)->bin_window;
3994 else
3995 window = GetConnectWidget()->window;
3996
3997 wxCHECK_RET( window, _T("CaptureMouse() failed") );
3998
3999 wxCursor* cursor = & m_cursor;
4000 if (!cursor->Ok())
4001 cursor = wxSTANDARD_CURSOR;
4002
4003 gdk_pointer_grab( window, FALSE,
4004 (GdkEventMask)
4005 (GDK_BUTTON_PRESS_MASK |
4006 GDK_BUTTON_RELEASE_MASK |
4007 GDK_POINTER_MOTION_HINT_MASK |
4008 GDK_POINTER_MOTION_MASK),
4009 (GdkWindow *) NULL,
4010 cursor->GetCursor(),
4011 (guint32)GDK_CURRENT_TIME );
4012 g_captureWindow = this;
4013 g_captureWindowHasMouse = true;
4014 }
4015
4016 void wxWindowGTK::DoReleaseMouse()
4017 {
4018 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4019
4020 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
4021
4022 g_captureWindow = (wxWindowGTK*) NULL;
4023
4024 GdkWindow *window = (GdkWindow*) NULL;
4025 if (m_wxwindow)
4026 window = GTK_PIZZA(m_wxwindow)->bin_window;
4027 else
4028 window = GetConnectWidget()->window;
4029
4030 if (!window)
4031 return;
4032
4033 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
4034 }
4035
4036 /* static */
4037 wxWindow *wxWindowBase::GetCapture()
4038 {
4039 return (wxWindow *)g_captureWindow;
4040 }
4041
4042 bool wxWindowGTK::IsRetained() const
4043 {
4044 return false;
4045 }
4046
4047 void wxWindowGTK::SetScrollbar( int orient, int pos, int thumbVisible,
4048 int range, bool refresh )
4049 {
4050 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4051
4052 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4053
4054 m_hasScrolling = true;
4055
4056 if (orient == wxHORIZONTAL)
4057 {
4058 float fpos = (float)pos;
4059 float frange = (float)range;
4060 float fthumb = (float)thumbVisible;
4061 if (fpos > frange-fthumb) fpos = frange-fthumb;
4062 if (fpos < 0.0) fpos = 0.0;
4063
4064 if ((fabs(frange-m_hAdjust->upper) < 0.2) &&
4065 (fabs(fthumb-m_hAdjust->page_size) < 0.2))
4066 {
4067 SetScrollPos( orient, pos, refresh );
4068 return;
4069 }
4070
4071 m_oldHorizontalPos = fpos;
4072
4073 m_hAdjust->lower = 0.0;
4074 m_hAdjust->upper = frange;
4075 m_hAdjust->value = fpos;
4076 m_hAdjust->step_increment = 1.0;
4077 m_hAdjust->page_increment = (float)(wxMax(fthumb,0));
4078 m_hAdjust->page_size = fthumb;
4079 }
4080 else
4081 {
4082 float fpos = (float)pos;
4083 float frange = (float)range;
4084 float fthumb = (float)thumbVisible;
4085 if (fpos > frange-fthumb) fpos = frange-fthumb;
4086 if (fpos < 0.0) fpos = 0.0;
4087
4088 if ((fabs(frange-m_vAdjust->upper) < 0.2) &&
4089 (fabs(fthumb-m_vAdjust->page_size) < 0.2))
4090 {
4091 SetScrollPos( orient, pos, refresh );
4092 return;
4093 }
4094
4095 m_oldVerticalPos = fpos;
4096
4097 m_vAdjust->lower = 0.0;
4098 m_vAdjust->upper = frange;
4099 m_vAdjust->value = fpos;
4100 m_vAdjust->step_increment = 1.0;
4101 m_vAdjust->page_increment = (float)(wxMax(fthumb,0));
4102 m_vAdjust->page_size = fthumb;
4103 }
4104
4105 if (orient == wxHORIZONTAL)
4106 g_signal_emit_by_name (m_hAdjust, "changed");
4107 else
4108 g_signal_emit_by_name (m_vAdjust, "changed");
4109 }
4110
4111 void wxWindowGTK::GtkUpdateScrollbar(int orient)
4112 {
4113 GtkAdjustment *adj = orient == wxHORIZONTAL ? m_hAdjust : m_vAdjust;
4114 gpointer fn = orient == wxHORIZONTAL
4115 ? (gpointer) gtk_window_hscroll_callback
4116 : (gpointer) gtk_window_vscroll_callback;
4117
4118 g_signal_handlers_disconnect_by_func (adj, fn, this);
4119 g_signal_emit_by_name (adj, "value_changed");
4120 g_signal_connect (adj, "value_changed", G_CALLBACK (fn), this);
4121 }
4122
4123 void wxWindowGTK::SetScrollPos( int orient, int pos, bool WXUNUSED(refresh) )
4124 {
4125 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4126 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4127
4128 GtkAdjustment *adj = orient == wxHORIZONTAL ? m_hAdjust : m_vAdjust;
4129
4130 float fpos = (float)pos;
4131 if (fpos > adj->upper - adj->page_size)
4132 fpos = adj->upper - adj->page_size;
4133 if (fpos < 0.0)
4134 fpos = 0.0;
4135 *(orient == wxHORIZONTAL ? &m_oldHorizontalPos : &m_oldVerticalPos) = fpos;
4136
4137 if (fabs(fpos-adj->value) < 0.2)
4138 return;
4139 adj->value = fpos;
4140
4141 if ( m_wxwindow->window )
4142 {
4143 }
4144 }
4145
4146 int wxWindowGTK::GetScrollThumb( int orient ) const
4147 {
4148 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4149
4150 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4151
4152 if (orient == wxHORIZONTAL)
4153 return (int)(m_hAdjust->page_size+0.5);
4154 else
4155 return (int)(m_vAdjust->page_size+0.5);
4156 }
4157
4158 int wxWindowGTK::GetScrollPos( int orient ) const
4159 {
4160 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4161
4162 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4163
4164 if (orient == wxHORIZONTAL)
4165 return (int)(m_hAdjust->value+0.5);
4166 else
4167 return (int)(m_vAdjust->value+0.5);
4168 }
4169
4170 int wxWindowGTK::GetScrollRange( int orient ) const
4171 {
4172 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4173
4174 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4175
4176 if (orient == wxHORIZONTAL)
4177 return (int)(m_hAdjust->upper+0.5);
4178 else
4179 return (int)(m_vAdjust->upper+0.5);
4180 }
4181
4182 void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
4183 {
4184 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4185
4186 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4187
4188 // No scrolling requested.
4189 if ((dx == 0) && (dy == 0)) return;
4190
4191 m_clipPaintRegion = true;
4192
4193 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy );
4194
4195 m_clipPaintRegion = false;
4196 }
4197
4198 void wxWindowGTK::SetWindowStyleFlag( long style )
4199 {
4200 // Updates the internal variable. NB: Now m_windowStyle bits carry the _new_ style values already
4201 wxWindowBase::SetWindowStyleFlag(style);
4202 }
4203
4204 // Find the wxWindow at the current mouse position, also returning the mouse
4205 // position.
4206 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4207 {
4208 pt = wxGetMousePosition();
4209 wxWindow* found = wxFindWindowAtPoint(pt);
4210 return found;
4211 }
4212
4213 // Get the current mouse position.
4214 wxPoint wxGetMousePosition()
4215 {
4216 /* This crashes when used within wxHelpContext,
4217 so we have to use the X-specific implementation below.
4218 gint x, y;
4219 GdkModifierType *mask;
4220 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4221
4222 return wxPoint(x, y);
4223 */
4224
4225 int x, y;
4226 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
4227
4228 Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY();
4229 Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
4230 Window rootReturn, childReturn;
4231 int rootX, rootY, winX, winY;
4232 unsigned int maskReturn;
4233
4234 XQueryPointer (display,
4235 rootWindow,
4236 &rootReturn,
4237 &childReturn,
4238 &rootX, &rootY, &winX, &winY, &maskReturn);
4239 return wxPoint(rootX, rootY);
4240
4241 }
4242
4243 // Needed for implementing e.g. combobox on wxGTK within a modal dialog.
4244 void wxAddGrab(wxWindow* window)
4245 {
4246 gtk_grab_add( (GtkWidget*) window->GetHandle() );
4247 }
4248
4249 void wxRemoveGrab(wxWindow* window)
4250 {
4251 gtk_grab_remove( (GtkWidget*) window->GetHandle() );
4252 }
4253
4254 // ----------------------------------------------------------------------------
4255 // wxWinModule
4256 // ----------------------------------------------------------------------------
4257
4258 class wxWinModule : public wxModule
4259 {
4260 public:
4261 bool OnInit();
4262 void OnExit();
4263
4264 private:
4265 DECLARE_DYNAMIC_CLASS(wxWinModule)
4266 };
4267
4268 IMPLEMENT_DYNAMIC_CLASS(wxWinModule, wxModule)
4269
4270 bool wxWinModule::OnInit()
4271 {
4272 // g_eraseGC = gdk_gc_new( GDK_ROOT_PARENT() );
4273 // gdk_gc_set_fill( g_eraseGC, GDK_SOLID );
4274
4275 return true;
4276 }
4277
4278 void wxWinModule::OnExit()
4279 {
4280 if (g_eraseGC)
4281 gdk_gc_unref( g_eraseGC );
4282 }
4283