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