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