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