]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/window.cpp
use wxGTK_CONV instead of directly using wxConvLocal (simplifies code avoiding needle...
[wxWidgets.git] / src / gtk / window.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/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_get_default_root_window();
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 g_object_unref (G_OBJECT (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_PAGEUP;
655 break;
656
657 case GDK_Next: // == GDK_Page_Down
658 key_code = WXK_PAGEDOWN;
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_PAGEUP : WXK_NUMPAD_PAGEUP;
738 break;
739
740 case GDK_KP_Next: // == GDK_KP_Page_Down
741 key_code = isChar ? WXK_PAGEDOWN : WXK_NUMPAD_PAGEDOWN;
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 const wxWxCharBuffer data(wxGTK_CONV_BACK(str));
1202 if( !data )
1203 return;
1204
1205 bool ret = false;
1206
1207 // Implement OnCharHook by checking ancestor top level windows
1208 wxWindow *parent = window;
1209 while (parent && !parent->IsTopLevel())
1210 parent = parent->GetParent();
1211
1212 for( const wxChar* pstr = data; *pstr; pstr++ )
1213 {
1214 #if wxUSE_UNICODE
1215 event.m_uniChar = *pstr;
1216 // Backward compatible for ISO-8859-1
1217 event.m_keyCode = *pstr < 256 ? event.m_uniChar : 0;
1218 wxLogTrace(TRACE_KEYS, _T("IM sent character '%c'"), event.m_uniChar);
1219 #else
1220 event.m_keyCode = *pstr;
1221 #endif // wxUSE_UNICODE
1222
1223 // To conform to the docs we need to translate Ctrl-alpha
1224 // characters to values in the range 1-26.
1225 if (event.ControlDown() && *pstr >= 'a' && *pstr <= 'z' )
1226 {
1227 event.m_keyCode = *pstr - 'a' + 1;
1228 #if wxUSE_UNICODE
1229 event.m_uniChar = event.m_keyCode;
1230 #endif
1231 }
1232
1233 if (parent)
1234 {
1235 event.SetEventType( wxEVT_CHAR_HOOK );
1236 ret = parent->GetEventHandler()->ProcessEvent( event );
1237 }
1238
1239 if (!ret)
1240 {
1241 event.SetEventType(wxEVT_CHAR);
1242 ret = window->GetEventHandler()->ProcessEvent( event );
1243 }
1244 }
1245 }
1246 }
1247
1248
1249 //-----------------------------------------------------------------------------
1250 // "key_release_event" from any window
1251 //-----------------------------------------------------------------------------
1252
1253 extern "C" {
1254 static gboolean
1255 gtk_window_key_release_callback( GtkWidget *widget,
1256 GdkEventKey *gdk_event,
1257 wxWindowGTK *win )
1258 {
1259 DEBUG_MAIN_THREAD
1260
1261 if (g_isIdle)
1262 wxapp_install_idle_handler();
1263
1264 if (!win->m_hasVMT)
1265 return FALSE;
1266
1267 if (g_blockEventsOnDrag)
1268 return FALSE;
1269
1270 wxKeyEvent event( wxEVT_KEY_UP );
1271 if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
1272 {
1273 // unknown key pressed, ignore (the event would be useless anyhow)
1274 return FALSE;
1275 }
1276
1277 if ( !win->GetEventHandler()->ProcessEvent( event ) )
1278 return FALSE;
1279
1280 g_signal_stop_emission_by_name (widget, "key_release_event");
1281 return TRUE;
1282 }
1283 }
1284
1285 // ============================================================================
1286 // the mouse events
1287 // ============================================================================
1288
1289 // ----------------------------------------------------------------------------
1290 // mouse event processing helpers
1291 // ----------------------------------------------------------------------------
1292
1293 // init wxMouseEvent with the info from GdkEventXXX struct
1294 template<typename T> void InitMouseEvent(wxWindowGTK *win,
1295 wxMouseEvent& event,
1296 T *gdk_event)
1297 {
1298 event.SetTimestamp( gdk_event->time );
1299 event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK);
1300 event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK);
1301 event.m_altDown = (gdk_event->state & GDK_MOD1_MASK);
1302 event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK);
1303 event.m_leftDown = (gdk_event->state & GDK_BUTTON1_MASK);
1304 event.m_middleDown = (gdk_event->state & GDK_BUTTON2_MASK);
1305 event.m_rightDown = (gdk_event->state & GDK_BUTTON3_MASK);
1306 if (event.GetEventType() == wxEVT_MOUSEWHEEL)
1307 {
1308 event.m_linesPerAction = 3;
1309 event.m_wheelDelta = 120;
1310 if (((GdkEventButton*)gdk_event)->button == 4)
1311 event.m_wheelRotation = 120;
1312 else if (((GdkEventButton*)gdk_event)->button == 5)
1313 event.m_wheelRotation = -120;
1314 }
1315
1316 wxPoint pt = win->GetClientAreaOrigin();
1317 event.m_x = (wxCoord)gdk_event->x - pt.x;
1318 event.m_y = (wxCoord)gdk_event->y - pt.y;
1319
1320 event.SetEventObject( win );
1321 event.SetId( win->GetId() );
1322 event.SetTimestamp( gdk_event->time );
1323 }
1324
1325 static void AdjustEventButtonState(wxMouseEvent& event)
1326 {
1327 // GDK reports the old state of the button for a button press event, but
1328 // for compatibility with MSW and common sense we want m_leftDown be TRUE
1329 // for a LEFT_DOWN event, not FALSE, so we will invert
1330 // left/right/middleDown for the corresponding click events
1331
1332 if ((event.GetEventType() == wxEVT_LEFT_DOWN) ||
1333 (event.GetEventType() == wxEVT_LEFT_DCLICK) ||
1334 (event.GetEventType() == wxEVT_LEFT_UP))
1335 {
1336 event.m_leftDown = !event.m_leftDown;
1337 return;
1338 }
1339
1340 if ((event.GetEventType() == wxEVT_MIDDLE_DOWN) ||
1341 (event.GetEventType() == wxEVT_MIDDLE_DCLICK) ||
1342 (event.GetEventType() == wxEVT_MIDDLE_UP))
1343 {
1344 event.m_middleDown = !event.m_middleDown;
1345 return;
1346 }
1347
1348 if ((event.GetEventType() == wxEVT_RIGHT_DOWN) ||
1349 (event.GetEventType() == wxEVT_RIGHT_DCLICK) ||
1350 (event.GetEventType() == wxEVT_RIGHT_UP))
1351 {
1352 event.m_rightDown = !event.m_rightDown;
1353 return;
1354 }
1355 }
1356
1357 // find the window to send the mouse event too
1358 static
1359 wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
1360 {
1361 wxCoord xx = x;
1362 wxCoord yy = y;
1363
1364 if (win->m_wxwindow)
1365 {
1366 GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
1367 xx += pizza->xoffset;
1368 yy += pizza->yoffset;
1369 }
1370
1371 wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
1372 while (node)
1373 {
1374 wxWindowGTK *child = node->GetData();
1375
1376 node = node->GetNext();
1377 if (!child->IsShown())
1378 continue;
1379
1380 if (child->IsTransparentForMouse())
1381 {
1382 // wxStaticBox is transparent in the box itself
1383 int xx1 = child->m_x;
1384 int yy1 = child->m_y;
1385 int xx2 = child->m_x + child->m_width;
1386 int yy2 = child->m_y + child->m_height;
1387
1388 // left
1389 if (((xx >= xx1) && (xx <= xx1+10) && (yy >= yy1) && (yy <= yy2)) ||
1390 // right
1391 ((xx >= xx2-10) && (xx <= xx2) && (yy >= yy1) && (yy <= yy2)) ||
1392 // top
1393 ((xx >= xx1) && (xx <= xx2) && (yy >= yy1) && (yy <= yy1+10)) ||
1394 // bottom
1395 ((xx >= xx1) && (xx <= xx2) && (yy >= yy2-1) && (yy <= yy2)))
1396 {
1397 win = child;
1398 x -= child->m_x;
1399 y -= child->m_y;
1400 break;
1401 }
1402
1403 }
1404 else
1405 {
1406 if ((child->m_wxwindow == (GtkWidget*) NULL) &&
1407 (child->m_x <= xx) &&
1408 (child->m_y <= yy) &&
1409 (child->m_x+child->m_width >= xx) &&
1410 (child->m_y+child->m_height >= yy))
1411 {
1412 win = child;
1413 x -= child->m_x;
1414 y -= child->m_y;
1415 break;
1416 }
1417 }
1418 }
1419
1420 return win;
1421 }
1422
1423 //-----------------------------------------------------------------------------
1424 // "button_press_event"
1425 //-----------------------------------------------------------------------------
1426
1427 extern "C" {
1428 static gboolean
1429 gtk_window_button_press_callback( GtkWidget *widget,
1430 GdkEventButton *gdk_event,
1431 wxWindowGTK *win )
1432 {
1433 DEBUG_MAIN_THREAD
1434
1435 if (g_isIdle)
1436 wxapp_install_idle_handler();
1437
1438 /*
1439 wxPrintf( wxT("1) OnButtonPress from ") );
1440 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1441 wxPrintf( win->GetClassInfo()->GetClassName() );
1442 wxPrintf( wxT(".\n") );
1443 */
1444 if (!win->m_hasVMT) return FALSE;
1445 if (g_blockEventsOnDrag) return TRUE;
1446 if (g_blockEventsOnScroll) return TRUE;
1447
1448 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1449
1450 if (win->m_wxwindow && (g_focusWindow != win) && win->AcceptsFocus())
1451 {
1452 gtk_widget_grab_focus( win->m_wxwindow );
1453 /*
1454 wxPrintf( wxT("GrabFocus from ") );
1455 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1456 wxPrintf( win->GetClassInfo()->GetClassName() );
1457 wxPrintf( wxT(".\n") );
1458 */
1459 }
1460
1461 // GDK sends surplus button down events
1462 // before a double click event. We
1463 // need to filter these out.
1464 if (gdk_event->type == GDK_BUTTON_PRESS)
1465 {
1466 GdkEvent *peek_event = gdk_event_peek();
1467 if (peek_event)
1468 {
1469 if ((peek_event->type == GDK_2BUTTON_PRESS) ||
1470 (peek_event->type == GDK_3BUTTON_PRESS))
1471 {
1472 gdk_event_free( peek_event );
1473 return TRUE;
1474 }
1475 else
1476 {
1477 gdk_event_free( peek_event );
1478 }
1479 }
1480 }
1481
1482 wxEventType event_type = wxEVT_NULL;
1483
1484 // GdkDisplay is a GTK+ 2.2.0 thing
1485 #if defined(__WXGTK20__) && GTK_CHECK_VERSION(2, 2, 0)
1486 if ( gdk_event->type == GDK_2BUTTON_PRESS &&
1487 !gtk_check_version(2,2,0) &&
1488 gdk_event->button >= 1 && gdk_event->button <= 3 )
1489 {
1490 // Reset GDK internal timestamp variables in order to disable GDK
1491 // triple click events. GDK will then next time believe no button has
1492 // been clicked just before, and send a normal button click event.
1493 GdkDisplay* display = gtk_widget_get_display (widget);
1494 display->button_click_time[1] = 0;
1495 display->button_click_time[0] = 0;
1496 }
1497 #endif // GTK 2+
1498
1499 if (gdk_event->button == 1)
1500 {
1501 // note that GDK generates triple click events which are not supported
1502 // by wxWidgets but still have to be passed to the app as otherwise
1503 // clicks would simply go missing
1504 switch (gdk_event->type)
1505 {
1506 // we shouldn't get triple clicks at all for GTK2 because we
1507 // suppress them artificially using the code above but we still
1508 // should map them to something for GTK1 and not just ignore them
1509 // as this would lose clicks
1510 case GDK_3BUTTON_PRESS: // we could also map this to DCLICK...
1511 case GDK_BUTTON_PRESS:
1512 event_type = wxEVT_LEFT_DOWN;
1513 break;
1514
1515 case GDK_2BUTTON_PRESS:
1516 event_type = wxEVT_LEFT_DCLICK;
1517 break;
1518
1519 default:
1520 // just to silence gcc warnings
1521 ;
1522 }
1523 }
1524 else if (gdk_event->button == 2)
1525 {
1526 switch (gdk_event->type)
1527 {
1528 case GDK_3BUTTON_PRESS:
1529 case GDK_BUTTON_PRESS:
1530 event_type = wxEVT_MIDDLE_DOWN;
1531 break;
1532
1533 case GDK_2BUTTON_PRESS:
1534 event_type = wxEVT_MIDDLE_DCLICK;
1535 break;
1536
1537 default:
1538 ;
1539 }
1540 }
1541 else if (gdk_event->button == 3)
1542 {
1543 switch (gdk_event->type)
1544 {
1545 case GDK_3BUTTON_PRESS:
1546 case GDK_BUTTON_PRESS:
1547 event_type = wxEVT_RIGHT_DOWN;
1548 break;
1549
1550 case GDK_2BUTTON_PRESS:
1551 event_type = wxEVT_RIGHT_DCLICK;
1552 break;
1553
1554 default:
1555 ;
1556 }
1557 }
1558 else if (gdk_event->button == 4 || gdk_event->button == 5)
1559 {
1560 if (gdk_event->type == GDK_BUTTON_PRESS )
1561 {
1562 event_type = wxEVT_MOUSEWHEEL;
1563 }
1564 }
1565
1566 if ( event_type == wxEVT_NULL )
1567 {
1568 // unknown mouse button or click type
1569 return FALSE;
1570 }
1571
1572 wxMouseEvent event( event_type );
1573 InitMouseEvent( win, event, gdk_event );
1574
1575 AdjustEventButtonState(event);
1576
1577 // wxListBox actually gets mouse events from the item, so we need to give it
1578 // a chance to correct this
1579 win->FixUpMouseEvent(widget, event.m_x, event.m_y);
1580
1581 // find the correct window to send the event to: it may be a different one
1582 // from the one which got it at GTK+ level because some controls don't have
1583 // their own X window and thus cannot get any events.
1584 if ( !g_captureWindow )
1585 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1586
1587 if (win->GetEventHandler()->ProcessEvent( event ))
1588 {
1589 g_signal_stop_emission_by_name (widget, "button_press_event");
1590 return TRUE;
1591 }
1592
1593 if (event_type == wxEVT_RIGHT_DOWN)
1594 {
1595 // generate a "context menu" event: this is similar to right mouse
1596 // click under many GUIs except that it is generated differently
1597 // (right up under MSW, ctrl-click under Mac, right down here) and
1598 //
1599 // (a) it's a command event and so is propagated to the parent
1600 // (b) under some ports it can be generated from kbd too
1601 // (c) it uses screen coords (because of (a))
1602 wxContextMenuEvent evtCtx(
1603 wxEVT_CONTEXT_MENU,
1604 win->GetId(),
1605 win->ClientToScreen(event.GetPosition()));
1606 evtCtx.SetEventObject(win);
1607 return win->GetEventHandler()->ProcessEvent(evtCtx);
1608 }
1609
1610 return FALSE;
1611 }
1612 }
1613
1614 //-----------------------------------------------------------------------------
1615 // "button_release_event"
1616 //-----------------------------------------------------------------------------
1617
1618 extern "C" {
1619 static gboolean
1620 gtk_window_button_release_callback( GtkWidget *widget,
1621 GdkEventButton *gdk_event,
1622 wxWindowGTK *win )
1623 {
1624 DEBUG_MAIN_THREAD
1625
1626 if (g_isIdle)
1627 wxapp_install_idle_handler();
1628
1629 if (!win->m_hasVMT) return FALSE;
1630 if (g_blockEventsOnDrag) return FALSE;
1631 if (g_blockEventsOnScroll) return FALSE;
1632
1633 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1634
1635 wxEventType event_type = wxEVT_NULL;
1636
1637 switch (gdk_event->button)
1638 {
1639 case 1:
1640 event_type = wxEVT_LEFT_UP;
1641 break;
1642
1643 case 2:
1644 event_type = wxEVT_MIDDLE_UP;
1645 break;
1646
1647 case 3:
1648 event_type = wxEVT_RIGHT_UP;
1649 break;
1650
1651 default:
1652 // unknwon button, don't process
1653 return FALSE;
1654 }
1655
1656 wxMouseEvent event( event_type );
1657 InitMouseEvent( win, event, gdk_event );
1658
1659 AdjustEventButtonState(event);
1660
1661 // same wxListBox hack as above
1662 win->FixUpMouseEvent(widget, event.m_x, event.m_y);
1663
1664 if ( !g_captureWindow )
1665 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1666
1667 if (win->GetEventHandler()->ProcessEvent( event ))
1668 {
1669 g_signal_stop_emission_by_name (widget, "button_release_event");
1670 return TRUE;
1671 }
1672
1673 return FALSE;
1674 }
1675 }
1676
1677 //-----------------------------------------------------------------------------
1678 // "motion_notify_event"
1679 //-----------------------------------------------------------------------------
1680
1681 extern "C" {
1682 static gboolean
1683 gtk_window_motion_notify_callback( GtkWidget *widget,
1684 GdkEventMotion *gdk_event,
1685 wxWindowGTK *win )
1686 {
1687 DEBUG_MAIN_THREAD
1688
1689 if (g_isIdle)
1690 wxapp_install_idle_handler();
1691
1692 if (!win->m_hasVMT) return FALSE;
1693 if (g_blockEventsOnDrag) return FALSE;
1694 if (g_blockEventsOnScroll) return FALSE;
1695
1696 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1697
1698 if (gdk_event->is_hint)
1699 {
1700 int x = 0;
1701 int y = 0;
1702 GdkModifierType state;
1703 gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
1704 gdk_event->x = x;
1705 gdk_event->y = y;
1706 }
1707
1708 /*
1709 printf( "OnMotion from " );
1710 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1711 printf( win->GetClassInfo()->GetClassName() );
1712 printf( ".\n" );
1713 */
1714
1715 wxMouseEvent event( wxEVT_MOTION );
1716 InitMouseEvent(win, event, gdk_event);
1717
1718 if ( g_captureWindow )
1719 {
1720 // synthetize a mouse enter or leave event if needed
1721 GdkWindow *winUnderMouse = gdk_window_at_pointer(NULL, NULL);
1722 // This seems to be necessary and actually been added to
1723 // GDK itself in version 2.0.X
1724 gdk_flush();
1725
1726 bool hasMouse = winUnderMouse == gdk_event->window;
1727 if ( hasMouse != g_captureWindowHasMouse )
1728 {
1729 // the mouse changed window
1730 g_captureWindowHasMouse = hasMouse;
1731
1732 wxMouseEvent eventM(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW
1733 : wxEVT_LEAVE_WINDOW);
1734 InitMouseEvent(win, eventM, gdk_event);
1735 eventM.SetEventObject(win);
1736 win->GetEventHandler()->ProcessEvent(eventM);
1737 }
1738 }
1739 else // no capture
1740 {
1741 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1742 }
1743
1744 if ( !g_captureWindow )
1745 {
1746 wxSetCursorEvent cevent( event.m_x, event.m_y );
1747 if (win->GetEventHandler()->ProcessEvent( cevent ))
1748 {
1749 // Rewrite cursor handling here (away from idle).
1750 }
1751 }
1752
1753 if (win->GetEventHandler()->ProcessEvent( event ))
1754 {
1755 g_signal_stop_emission_by_name (widget, "motion_notify_event");
1756 return TRUE;
1757 }
1758
1759 return FALSE;
1760 }
1761 }
1762
1763 //-----------------------------------------------------------------------------
1764 // "mouse_wheel_event"
1765 //-----------------------------------------------------------------------------
1766
1767 extern "C" {
1768 static gboolean
1769 gtk_window_wheel_callback (GtkWidget * widget,
1770 GdkEventScroll * gdk_event,
1771 wxWindowGTK * win)
1772 {
1773 DEBUG_MAIN_THREAD
1774
1775 if (g_isIdle)
1776 wxapp_install_idle_handler();
1777
1778 wxEventType event_type = wxEVT_NULL;
1779 if (gdk_event->direction == GDK_SCROLL_UP)
1780 event_type = wxEVT_MOUSEWHEEL;
1781 else if (gdk_event->direction == GDK_SCROLL_DOWN)
1782 event_type = wxEVT_MOUSEWHEEL;
1783 else
1784 return FALSE;
1785
1786 wxMouseEvent event( event_type );
1787 // Can't use InitMouse macro because scroll events don't have button
1788 event.SetTimestamp( gdk_event->time );
1789 event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK);
1790 event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK);
1791 event.m_altDown = (gdk_event->state & GDK_MOD1_MASK);
1792 event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK);
1793 event.m_leftDown = (gdk_event->state & GDK_BUTTON1_MASK);
1794 event.m_middleDown = (gdk_event->state & GDK_BUTTON2_MASK);
1795 event.m_rightDown = (gdk_event->state & GDK_BUTTON3_MASK);
1796 event.m_linesPerAction = 3;
1797 event.m_wheelDelta = 120;
1798 if (gdk_event->direction == GDK_SCROLL_UP)
1799 event.m_wheelRotation = 120;
1800 else
1801 event.m_wheelRotation = -120;
1802
1803 wxPoint pt = win->GetClientAreaOrigin();
1804 event.m_x = (wxCoord)gdk_event->x - pt.x;
1805 event.m_y = (wxCoord)gdk_event->y - pt.y;
1806
1807 event.SetEventObject( win );
1808 event.SetId( win->GetId() );
1809 event.SetTimestamp( gdk_event->time );
1810
1811 if (win->GetEventHandler()->ProcessEvent( event ))
1812 {
1813 g_signal_stop_emission_by_name (widget, "scroll_event");
1814 return TRUE;
1815 }
1816
1817 return FALSE;
1818 }
1819 }
1820
1821 //-----------------------------------------------------------------------------
1822 // "popup-menu"
1823 //-----------------------------------------------------------------------------
1824 extern "C" {
1825 static gboolean wxgtk_window_popup_menu_callback(GtkWidget*, wxWindowGTK* win)
1826 {
1827 wxContextMenuEvent event(
1828 wxEVT_CONTEXT_MENU,
1829 win->GetId(),
1830 wxPoint(-1, -1));
1831 event.SetEventObject(win);
1832 return win->GetEventHandler()->ProcessEvent(event);
1833 }
1834 }
1835
1836 //-----------------------------------------------------------------------------
1837 // "focus_in_event"
1838 //-----------------------------------------------------------------------------
1839
1840 // send the wxChildFocusEvent and wxFocusEvent, common code of
1841 // gtk_window_focus_in_callback() and SetFocus()
1842 static bool DoSendFocusEvents(wxWindow *win)
1843 {
1844 // Notify the parent keeping track of focus for the kbd navigation
1845 // purposes that we got it.
1846 wxChildFocusEvent eventChildFocus(win);
1847 (void)win->GetEventHandler()->ProcessEvent(eventChildFocus);
1848
1849 wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
1850 eventFocus.SetEventObject(win);
1851
1852 return win->GetEventHandler()->ProcessEvent(eventFocus);
1853 }
1854
1855 extern "C" {
1856 static gboolean
1857 gtk_window_focus_in_callback( GtkWidget *widget,
1858 GdkEventFocus *WXUNUSED(event),
1859 wxWindow *win )
1860 {
1861 DEBUG_MAIN_THREAD
1862
1863 if (g_isIdle)
1864 wxapp_install_idle_handler();
1865
1866 if (win->m_imData)
1867 gtk_im_context_focus_in(win->m_imData->context);
1868
1869 g_focusWindowLast =
1870 g_focusWindow = win;
1871
1872 wxLogTrace(TRACE_FOCUS,
1873 _T("%s: focus in"), win->GetName().c_str());
1874
1875 #ifdef HAVE_XIM
1876 if (win->m_ic)
1877 gdk_im_begin(win->m_ic, win->m_wxwindow->window);
1878 #endif
1879
1880 #if wxUSE_CARET
1881 // caret needs to be informed about focus change
1882 wxCaret *caret = win->GetCaret();
1883 if ( caret )
1884 {
1885 caret->OnSetFocus();
1886 }
1887 #endif // wxUSE_CARET
1888
1889 gboolean ret = FALSE;
1890
1891 // does the window itself think that it has the focus?
1892 if ( !win->m_hasFocus )
1893 {
1894 // not yet, notify it
1895 win->m_hasFocus = true;
1896
1897 (void)DoSendFocusEvents(win);
1898
1899 ret = TRUE;
1900 }
1901
1902 // Disable default focus handling for custom windows
1903 // since the default GTK+ handler issues a repaint
1904 if (win->m_wxwindow)
1905 return ret;
1906
1907 return FALSE;
1908 }
1909 }
1910
1911 //-----------------------------------------------------------------------------
1912 // "focus_out_event"
1913 //-----------------------------------------------------------------------------
1914
1915 extern "C" {
1916 static gboolean
1917 gtk_window_focus_out_callback( GtkWidget *widget,
1918 GdkEventFocus *gdk_event,
1919 wxWindowGTK *win )
1920 {
1921 DEBUG_MAIN_THREAD
1922
1923 if (g_isIdle)
1924 wxapp_install_idle_handler();
1925
1926 if (win->m_imData)
1927 gtk_im_context_focus_out(win->m_imData->context);
1928
1929 wxLogTrace( TRACE_FOCUS,
1930 _T("%s: focus out"), win->GetName().c_str() );
1931
1932
1933 wxWindowGTK *winFocus = wxFindFocusedChild(win);
1934 if ( winFocus )
1935 win = winFocus;
1936
1937 g_focusWindow = (wxWindowGTK *)NULL;
1938
1939 #ifdef HAVE_XIM
1940 if (win->m_ic)
1941 gdk_im_end();
1942 #endif
1943
1944 #if wxUSE_CARET
1945 // caret needs to be informed about focus change
1946 wxCaret *caret = win->GetCaret();
1947 if ( caret )
1948 {
1949 caret->OnKillFocus();
1950 }
1951 #endif // wxUSE_CARET
1952
1953 gboolean ret = FALSE;
1954
1955 // don't send the window a kill focus event if it thinks that it doesn't
1956 // have focus already
1957 if ( win->m_hasFocus )
1958 {
1959 win->m_hasFocus = false;
1960
1961 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
1962 event.SetEventObject( win );
1963
1964 (void)win->GetEventHandler()->ProcessEvent( event );
1965
1966 ret = TRUE;
1967 }
1968
1969 // Disable default focus handling for custom windows
1970 // since the default GTK+ handler issues a repaint
1971 if (win->m_wxwindow)
1972 return ret;
1973
1974 return FALSE;
1975 }
1976 }
1977
1978 //-----------------------------------------------------------------------------
1979 // "enter_notify_event"
1980 //-----------------------------------------------------------------------------
1981
1982 extern "C" {
1983 static gboolean
1984 gtk_window_enter_callback( GtkWidget *widget,
1985 GdkEventCrossing *gdk_event,
1986 wxWindowGTK *win )
1987 {
1988 DEBUG_MAIN_THREAD
1989
1990 if (g_isIdle)
1991 wxapp_install_idle_handler();
1992
1993 if (!win->m_hasVMT) return FALSE;
1994 if (g_blockEventsOnDrag) return FALSE;
1995
1996 // Event was emitted after a grab
1997 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
1998
1999 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
2000
2001 int x = 0;
2002 int y = 0;
2003 GdkModifierType state = (GdkModifierType)0;
2004
2005 gdk_window_get_pointer( widget->window, &x, &y, &state );
2006
2007 wxMouseEvent event( wxEVT_ENTER_WINDOW );
2008 InitMouseEvent(win, event, gdk_event);
2009 wxPoint pt = win->GetClientAreaOrigin();
2010 event.m_x = x + pt.x;
2011 event.m_y = y + pt.y;
2012
2013 if ( !g_captureWindow )
2014 {
2015 wxSetCursorEvent cevent( event.m_x, event.m_y );
2016 if (win->GetEventHandler()->ProcessEvent( cevent ))
2017 {
2018 // Rewrite cursor handling here (away from idle).
2019 }
2020 }
2021
2022 if (win->GetEventHandler()->ProcessEvent( event ))
2023 {
2024 g_signal_stop_emission_by_name (widget, "enter_notify_event");
2025 return TRUE;
2026 }
2027
2028 return FALSE;
2029 }
2030 }
2031
2032 //-----------------------------------------------------------------------------
2033 // "leave_notify_event"
2034 //-----------------------------------------------------------------------------
2035
2036 extern "C" {
2037 static gboolean
2038 gtk_window_leave_callback( GtkWidget *widget,
2039 GdkEventCrossing *gdk_event,
2040 wxWindowGTK *win )
2041 {
2042 DEBUG_MAIN_THREAD
2043
2044 if (g_isIdle)
2045 wxapp_install_idle_handler();
2046
2047 if (!win->m_hasVMT) return FALSE;
2048 if (g_blockEventsOnDrag) return FALSE;
2049
2050 // Event was emitted after an ungrab
2051 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
2052
2053 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
2054
2055 wxMouseEvent event( wxEVT_LEAVE_WINDOW );
2056 event.SetTimestamp( gdk_event->time );
2057 event.SetEventObject( win );
2058
2059 int x = 0;
2060 int y = 0;
2061 GdkModifierType state = (GdkModifierType)0;
2062
2063 gdk_window_get_pointer( widget->window, &x, &y, &state );
2064
2065 event.m_shiftDown = (state & GDK_SHIFT_MASK) != 0;
2066 event.m_controlDown = (state & GDK_CONTROL_MASK) != 0;
2067 event.m_altDown = (state & GDK_MOD1_MASK) != 0;
2068 event.m_metaDown = (state & GDK_MOD2_MASK) != 0;
2069 event.m_leftDown = (state & GDK_BUTTON1_MASK) != 0;
2070 event.m_middleDown = (state & GDK_BUTTON2_MASK) != 0;
2071 event.m_rightDown = (state & GDK_BUTTON3_MASK) != 0;
2072
2073 wxPoint pt = win->GetClientAreaOrigin();
2074 event.m_x = x + pt.x;
2075 event.m_y = y + pt.y;
2076
2077 if (win->GetEventHandler()->ProcessEvent( event ))
2078 {
2079 g_signal_stop_emission_by_name (widget, "leave_notify_event");
2080 return TRUE;
2081 }
2082
2083 return FALSE;
2084 }
2085 }
2086
2087 //-----------------------------------------------------------------------------
2088 // "value_changed" from m_vAdjust
2089 //-----------------------------------------------------------------------------
2090
2091 extern "C" {
2092 static void gtk_window_vscroll_callback( GtkAdjustment *adjust,
2093 wxWindowGTK *win )
2094 {
2095 DEBUG_MAIN_THREAD
2096
2097 if (g_isIdle)
2098 wxapp_install_idle_handler();
2099
2100 if (g_blockEventsOnDrag) return;
2101
2102 if (!win->m_hasVMT) return;
2103
2104 float diff = adjust->value - win->m_oldVerticalPos;
2105 if (fabs(diff) < 0.2) return;
2106
2107 win->m_oldVerticalPos = adjust->value;
2108
2109 wxEventType command = GtkScrollWinTypeToWx(GTK_SCROLL_JUMP);
2110
2111 int value = (int)(adjust->value+0.5);
2112
2113 wxScrollWinEvent event( command, value, wxVERTICAL );
2114 event.SetEventObject( win );
2115 win->GetEventHandler()->ProcessEvent( event );
2116 }
2117 }
2118
2119 //-----------------------------------------------------------------------------
2120 // "value_changed" from m_hAdjust
2121 //-----------------------------------------------------------------------------
2122
2123 extern "C" {
2124 static void gtk_window_hscroll_callback( GtkAdjustment *adjust,
2125 wxWindowGTK *win )
2126 {
2127 DEBUG_MAIN_THREAD
2128
2129 if (g_isIdle)
2130 wxapp_install_idle_handler();
2131
2132 if (g_blockEventsOnDrag) return;
2133 if (!win->m_hasVMT) return;
2134
2135 float diff = adjust->value - win->m_oldHorizontalPos;
2136 if (fabs(diff) < 0.2) return;
2137
2138 wxEventType command = GtkScrollWinTypeToWx(GTK_SCROLL_JUMP);
2139
2140 win->m_oldHorizontalPos = adjust->value;
2141
2142 int value = (int)(adjust->value+0.5);
2143
2144 wxScrollWinEvent event( command, value, wxHORIZONTAL );
2145 event.SetEventObject( win );
2146 win->GetEventHandler()->ProcessEvent( event );
2147 }
2148 }
2149
2150 //-----------------------------------------------------------------------------
2151 // "button_press_event" from scrollbar
2152 //-----------------------------------------------------------------------------
2153
2154 extern "C" {
2155 static gboolean
2156 gtk_scrollbar_button_press_callback( GtkWidget *widget,
2157 GdkEventButton *gdk_event,
2158 wxWindowGTK *win)
2159 {
2160 DEBUG_MAIN_THREAD
2161
2162 if (g_isIdle)
2163 wxapp_install_idle_handler();
2164
2165
2166 g_blockEventsOnScroll = true;
2167
2168 // FIXME: there is no 'slider' field in GTK+ 2.0 any more
2169 #if 0
2170 win->m_isScrolling = (gdk_event->window == widget->slider);
2171 #endif
2172
2173 return FALSE;
2174 }
2175 }
2176
2177 //-----------------------------------------------------------------------------
2178 // "button_release_event" from scrollbar
2179 //-----------------------------------------------------------------------------
2180
2181 extern "C" {
2182 static gboolean
2183 gtk_scrollbar_button_release_callback( GtkRange *widget,
2184 GdkEventButton *WXUNUSED(gdk_event),
2185 wxWindowGTK *win)
2186 {
2187 DEBUG_MAIN_THREAD
2188
2189 // don't test here as we can release the mouse while being over
2190 // a different window than the slider
2191 //
2192 // if (gdk_event->window != widget->slider) return FALSE;
2193
2194 g_blockEventsOnScroll = false;
2195
2196 if (win->m_isScrolling)
2197 {
2198 wxEventType command = wxEVT_SCROLLWIN_THUMBRELEASE;
2199 int value = -1;
2200 int dir = -1;
2201
2202 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(win->m_widget);
2203 if (widget == GTK_RANGE(scrolledWindow->hscrollbar))
2204 {
2205 value = (int)(win->m_hAdjust->value+0.5);
2206 dir = wxHORIZONTAL;
2207 }
2208 if (widget == GTK_RANGE(scrolledWindow->vscrollbar))
2209 {
2210 value = (int)(win->m_vAdjust->value+0.5);
2211 dir = wxVERTICAL;
2212 }
2213
2214 wxScrollWinEvent event( command, value, dir );
2215 event.SetEventObject( win );
2216 win->GetEventHandler()->ProcessEvent( event );
2217 }
2218
2219 win->m_isScrolling = false;
2220
2221 return FALSE;
2222 }
2223 }
2224
2225 // ----------------------------------------------------------------------------
2226 // this wxWindowBase function is implemented here (in platform-specific file)
2227 // because it is static and so couldn't be made virtual
2228 // ----------------------------------------------------------------------------
2229
2230 wxWindow *wxWindowBase::DoFindFocus()
2231 {
2232 // the cast is necessary when we compile in wxUniversal mode
2233 return (wxWindow *)g_focusWindow;
2234 }
2235
2236 //-----------------------------------------------------------------------------
2237 // "realize" from m_widget
2238 //-----------------------------------------------------------------------------
2239
2240 /* We cannot set colours and fonts before the widget has
2241 been realized, so we do this directly after realization. */
2242
2243 extern "C" {
2244 static void
2245 gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win )
2246 {
2247 DEBUG_MAIN_THREAD
2248
2249 if (g_isIdle)
2250 wxapp_install_idle_handler();
2251
2252 if (win->m_imData)
2253 {
2254 GtkPizza *pizza = GTK_PIZZA( m_widget );
2255 gtk_im_context_set_client_window( win->m_imData->context,
2256 pizza->bin_window );
2257 }
2258
2259 wxWindowCreateEvent event( win );
2260 event.SetEventObject( win );
2261 win->GetEventHandler()->ProcessEvent( event );
2262 }
2263 }
2264
2265 //-----------------------------------------------------------------------------
2266 // "size_allocate"
2267 //-----------------------------------------------------------------------------
2268
2269 extern "C" {
2270 static
2271 void gtk_window_size_callback( GtkWidget *WXUNUSED(widget),
2272 GtkAllocation *WXUNUSED(alloc),
2273 wxWindow *win )
2274 {
2275 if (g_isIdle)
2276 wxapp_install_idle_handler();
2277
2278 if (!win->m_hasScrolling) return;
2279
2280 int client_width = 0;
2281 int client_height = 0;
2282 win->GetClientSize( &client_width, &client_height );
2283 if ((client_width == win->m_oldClientWidth) && (client_height == win->m_oldClientHeight))
2284 return;
2285
2286 win->m_oldClientWidth = client_width;
2287 win->m_oldClientHeight = client_height;
2288
2289 if (!win->m_nativeSizeEvent)
2290 {
2291 wxSizeEvent event( win->GetSize(), win->GetId() );
2292 event.SetEventObject( win );
2293 win->GetEventHandler()->ProcessEvent( event );
2294 }
2295 }
2296 }
2297
2298
2299 #ifdef HAVE_XIM
2300 #define WXUNUSED_UNLESS_XIM(param) param
2301 #else
2302 #define WXUNUSED_UNLESS_XIM(param) WXUNUSED(param)
2303 #endif
2304
2305 /* Resize XIM window */
2306
2307 extern "C" {
2308 static
2309 void gtk_wxwindow_size_callback( GtkWidget* WXUNUSED_UNLESS_XIM(widget),
2310 GtkAllocation* WXUNUSED_UNLESS_XIM(alloc),
2311 wxWindowGTK* WXUNUSED_UNLESS_XIM(win) )
2312 {
2313 if (g_isIdle)
2314 wxapp_install_idle_handler();
2315
2316 #ifdef HAVE_XIM
2317 if (!win->m_ic)
2318 return;
2319
2320 if (gdk_ic_get_style (win->m_ic) & GDK_IM_PREEDIT_POSITION)
2321 {
2322 gint width, height;
2323
2324 gdk_drawable_get_size (widget->window, &width, &height);
2325 win->m_icattr->preedit_area.width = width;
2326 win->m_icattr->preedit_area.height = height;
2327 gdk_ic_set_attr (win->m_ic, win->m_icattr, GDK_IC_PREEDIT_AREA);
2328 }
2329 #endif // HAVE_XIM
2330 }
2331 }
2332
2333 //-----------------------------------------------------------------------------
2334 // "realize" from m_wxwindow
2335 //-----------------------------------------------------------------------------
2336
2337 /* Initialize XIM support */
2338
2339 extern "C" {
2340 static void
2341 gtk_wxwindow_realized_callback( GtkWidget * WXUNUSED_UNLESS_XIM(widget),
2342 wxWindowGTK * WXUNUSED_UNLESS_XIM(win) )
2343 {
2344 if (g_isIdle)
2345 wxapp_install_idle_handler();
2346
2347 #ifdef HAVE_XIM
2348 if (win->m_ic) return;
2349 if (!widget) return;
2350 if (!gdk_im_ready()) return;
2351
2352 win->m_icattr = gdk_ic_attr_new();
2353 if (!win->m_icattr) return;
2354
2355 gint width, height;
2356 GdkEventMask mask;
2357 GdkColormap *colormap;
2358 GdkICAttr *attr = win->m_icattr;
2359 unsigned attrmask = GDK_IC_ALL_REQ;
2360 GdkIMStyle style;
2361 GdkIMStyle supported_style = (GdkIMStyle)
2362 (GDK_IM_PREEDIT_NONE |
2363 GDK_IM_PREEDIT_NOTHING |
2364 GDK_IM_PREEDIT_POSITION |
2365 GDK_IM_STATUS_NONE |
2366 GDK_IM_STATUS_NOTHING);
2367
2368 if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
2369 supported_style = (GdkIMStyle)(supported_style & ~GDK_IM_PREEDIT_POSITION);
2370
2371 attr->style = style = gdk_im_decide_style (supported_style);
2372 attr->client_window = widget->window;
2373
2374 if ((colormap = gtk_widget_get_colormap (widget)) !=
2375 gtk_widget_get_default_colormap ())
2376 {
2377 attrmask |= GDK_IC_PREEDIT_COLORMAP;
2378 attr->preedit_colormap = colormap;
2379 }
2380
2381 attrmask |= GDK_IC_PREEDIT_FOREGROUND;
2382 attrmask |= GDK_IC_PREEDIT_BACKGROUND;
2383 attr->preedit_foreground = widget->style->fg[GTK_STATE_NORMAL];
2384 attr->preedit_background = widget->style->base[GTK_STATE_NORMAL];
2385
2386 switch (style & GDK_IM_PREEDIT_MASK)
2387 {
2388 case GDK_IM_PREEDIT_POSITION:
2389 if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
2390 {
2391 g_warning ("over-the-spot style requires fontset");
2392 break;
2393 }
2394
2395 gdk_drawable_get_size (widget->window, &width, &height);
2396
2397 attrmask |= GDK_IC_PREEDIT_POSITION_REQ;
2398 attr->spot_location.x = 0;
2399 attr->spot_location.y = height;
2400 attr->preedit_area.x = 0;
2401 attr->preedit_area.y = 0;
2402 attr->preedit_area.width = width;
2403 attr->preedit_area.height = height;
2404 attr->preedit_fontset = widget->style->font;
2405
2406 break;
2407 }
2408
2409 win->m_ic = gdk_ic_new (attr, (GdkICAttributesType)attrmask);
2410
2411 if (win->m_ic == NULL)
2412 g_warning ("Can't create input context.");
2413 else
2414 {
2415 mask = gdk_window_get_events (widget->window);
2416 mask = (GdkEventMask)(mask | gdk_ic_get_events (win->m_ic));
2417 gdk_window_set_events (widget->window, mask);
2418
2419 if (GTK_WIDGET_HAS_FOCUS(widget))
2420 gdk_im_begin (win->m_ic, widget->window);
2421 }
2422 #endif // HAVE_XIM
2423 }
2424 }
2425
2426 //-----------------------------------------------------------------------------
2427 // InsertChild for wxWindowGTK.
2428 //-----------------------------------------------------------------------------
2429
2430 /* Callback for wxWindowGTK. This very strange beast has to be used because
2431 * C++ has no virtual methods in a constructor. We have to emulate a
2432 * virtual function here as wxNotebook requires a different way to insert
2433 * a child in it. I had opted for creating a wxNotebookPage window class
2434 * which would have made this superfluous (such in the MDI window system),
2435 * but no-one was listening to me... */
2436
2437 static void wxInsertChildInWindow( wxWindowGTK* parent, wxWindowGTK* child )
2438 {
2439 /* the window might have been scrolled already, do we
2440 have to adapt the position */
2441 GtkPizza *pizza = GTK_PIZZA(parent->m_wxwindow);
2442 child->m_x += pizza->xoffset;
2443 child->m_y += pizza->yoffset;
2444
2445 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
2446 GTK_WIDGET(child->m_widget),
2447 child->m_x,
2448 child->m_y,
2449 child->m_width,
2450 child->m_height );
2451 }
2452
2453 //-----------------------------------------------------------------------------
2454 // global functions
2455 //-----------------------------------------------------------------------------
2456
2457 wxWindow *wxGetActiveWindow()
2458 {
2459 return wxWindow::FindFocus();
2460 }
2461
2462
2463 wxMouseState wxGetMouseState()
2464 {
2465 wxMouseState ms;
2466
2467 gint x;
2468 gint y;
2469 GdkModifierType mask;
2470
2471 gdk_window_get_pointer(NULL, &x, &y, &mask);
2472
2473 ms.SetX(x);
2474 ms.SetY(y);
2475 ms.SetLeftDown(mask & GDK_BUTTON1_MASK);
2476 ms.SetMiddleDown(mask & GDK_BUTTON2_MASK);
2477 ms.SetRightDown(mask & GDK_BUTTON3_MASK);
2478
2479 ms.SetControlDown(mask & GDK_CONTROL_MASK);
2480 ms.SetShiftDown(mask & GDK_SHIFT_MASK);
2481 ms.SetAltDown(mask & GDK_MOD1_MASK);
2482 ms.SetMetaDown(mask & GDK_MOD2_MASK);
2483
2484 return ms;
2485 }
2486
2487 //-----------------------------------------------------------------------------
2488 // wxWindowGTK
2489 //-----------------------------------------------------------------------------
2490
2491 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2492 // method
2493 #ifdef __WXUNIVERSAL__
2494 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK, wxWindowBase)
2495 #else // __WXGTK__
2496 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
2497 #endif // __WXUNIVERSAL__/__WXGTK__
2498
2499 void wxWindowGTK::Init()
2500 {
2501 // GTK specific
2502 m_widget = (GtkWidget *) NULL;
2503 m_wxwindow = (GtkWidget *) NULL;
2504 m_focusWidget = (GtkWidget *) NULL;
2505
2506 // position/size
2507 m_x = 0;
2508 m_y = 0;
2509 m_width = 0;
2510 m_height = 0;
2511
2512 m_sizeSet = false;
2513 m_hasVMT = false;
2514 m_needParent = true;
2515 m_isBeingDeleted = false;
2516
2517 m_noExpose = false;
2518 m_nativeSizeEvent = false;
2519
2520 m_hasScrolling = false;
2521 m_isScrolling = false;
2522
2523 m_hAdjust = (GtkAdjustment*) NULL;
2524 m_vAdjust = (GtkAdjustment*) NULL;
2525 m_oldHorizontalPos =
2526 m_oldVerticalPos = 0.0;
2527 m_oldClientWidth =
2528 m_oldClientHeight = 0;
2529
2530 m_resizing = false;
2531
2532 m_insertCallback = (wxInsertChildFunction) NULL;
2533
2534 m_acceptsFocus = false;
2535 m_hasFocus = false;
2536
2537 m_clipPaintRegion = false;
2538
2539 m_needsStyleChange = false;
2540
2541 m_cursor = *wxSTANDARD_CURSOR;
2542
2543 m_imData = NULL;
2544 m_dirtyTabOrder = false;
2545 }
2546
2547 wxWindowGTK::wxWindowGTK()
2548 {
2549 Init();
2550 }
2551
2552 wxWindowGTK::wxWindowGTK( wxWindow *parent,
2553 wxWindowID id,
2554 const wxPoint &pos,
2555 const wxSize &size,
2556 long style,
2557 const wxString &name )
2558 {
2559 Init();
2560
2561 Create( parent, id, pos, size, style, name );
2562 }
2563
2564 bool wxWindowGTK::Create( wxWindow *parent,
2565 wxWindowID id,
2566 const wxPoint &pos,
2567 const wxSize &size,
2568 long style,
2569 const wxString &name )
2570 {
2571 if (!PreCreation( parent, pos, size ) ||
2572 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
2573 {
2574 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2575 return false;
2576 }
2577
2578 m_insertCallback = wxInsertChildInWindow;
2579
2580 m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
2581 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
2582
2583 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
2584
2585 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
2586 scroll_class->scrollbar_spacing = 0;
2587
2588 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
2589
2590 m_hAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->hscrollbar) );
2591 m_vAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->vscrollbar) );
2592
2593 m_wxwindow = gtk_pizza_new();
2594
2595 #ifndef __WXUNIVERSAL__
2596 GtkPizza *pizza = GTK_PIZZA(m_wxwindow);
2597
2598 if (HasFlag(wxRAISED_BORDER))
2599 {
2600 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_OUT );
2601 }
2602 else if (HasFlag(wxSUNKEN_BORDER))
2603 {
2604 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_IN );
2605 }
2606 else if (HasFlag(wxSIMPLE_BORDER))
2607 {
2608 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_THIN );
2609 }
2610 else
2611 {
2612 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_NONE );
2613 }
2614 #endif // __WXUNIVERSAL__
2615
2616 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
2617
2618 GTK_WIDGET_SET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
2619 m_acceptsFocus = true;
2620
2621 // I _really_ don't want scrollbars in the beginning
2622 m_vAdjust->lower = 0.0;
2623 m_vAdjust->upper = 1.0;
2624 m_vAdjust->value = 0.0;
2625 m_vAdjust->step_increment = 1.0;
2626 m_vAdjust->page_increment = 1.0;
2627 m_vAdjust->page_size = 5.0;
2628 g_signal_emit_by_name (m_vAdjust, "changed");
2629 m_hAdjust->lower = 0.0;
2630 m_hAdjust->upper = 1.0;
2631 m_hAdjust->value = 0.0;
2632 m_hAdjust->step_increment = 1.0;
2633 m_hAdjust->page_increment = 1.0;
2634 m_hAdjust->page_size = 5.0;
2635 g_signal_emit_by_name (m_hAdjust, "changed");
2636
2637 // these handlers block mouse events to any window during scrolling such as
2638 // motion events and prevent GTK and wxWidgets from fighting over where the
2639 // slider should be
2640 g_signal_connect (scrolledWindow->vscrollbar, "button_press_event",
2641 G_CALLBACK (gtk_scrollbar_button_press_callback), this);
2642 g_signal_connect (scrolledWindow->hscrollbar, "button_press_event",
2643 G_CALLBACK (gtk_scrollbar_button_press_callback), this);
2644 g_signal_connect (scrolledWindow->vscrollbar, "button_release_event",
2645 G_CALLBACK (gtk_scrollbar_button_release_callback), this);
2646 g_signal_connect (scrolledWindow->hscrollbar, "button_release_event",
2647 G_CALLBACK (gtk_scrollbar_button_release_callback), this);
2648
2649 // these handlers get notified when screen updates are required either when
2650 // scrolling or when the window size (and therefore scrollbar configuration)
2651 // has changed
2652
2653 g_signal_connect (m_hAdjust, "value_changed",
2654 G_CALLBACK (gtk_window_hscroll_callback), this);
2655 g_signal_connect (m_vAdjust, "value_changed",
2656 G_CALLBACK (gtk_window_vscroll_callback), this);
2657
2658 gtk_widget_show( m_wxwindow );
2659
2660 if (m_parent)
2661 m_parent->DoAddChild( this );
2662
2663 m_focusWidget = m_wxwindow;
2664
2665 PostCreation();
2666
2667 return true;
2668 }
2669
2670 wxWindowGTK::~wxWindowGTK()
2671 {
2672 SendDestroyEvent();
2673
2674 if (g_focusWindow == this)
2675 g_focusWindow = NULL;
2676
2677 if ( g_delayedFocus == this )
2678 g_delayedFocus = NULL;
2679
2680 m_isBeingDeleted = true;
2681 m_hasVMT = false;
2682
2683 // destroy children before destroying this window itself
2684 DestroyChildren();
2685
2686 // unhook focus handlers to prevent stray events being
2687 // propagated to this (soon to be) dead object
2688 if (m_focusWidget != NULL)
2689 {
2690 g_signal_handlers_disconnect_by_func (m_focusWidget,
2691 (gpointer) gtk_window_focus_in_callback,
2692 this);
2693 g_signal_handlers_disconnect_by_func (m_focusWidget,
2694 (gpointer) gtk_window_focus_out_callback,
2695 this);
2696 }
2697
2698 if (m_widget)
2699 Show( false );
2700
2701 #ifdef HAVE_XIM
2702 if (m_ic)
2703 gdk_ic_destroy (m_ic);
2704 if (m_icattr)
2705 gdk_ic_attr_destroy (m_icattr);
2706 #endif
2707
2708 // delete before the widgets to avoid a crash on solaris
2709 delete m_imData;
2710
2711 if (m_wxwindow)
2712 {
2713 gtk_widget_destroy( m_wxwindow );
2714 m_wxwindow = (GtkWidget*) NULL;
2715 }
2716
2717 if (m_widget)
2718 {
2719 gtk_widget_destroy( m_widget );
2720 m_widget = (GtkWidget*) NULL;
2721 }
2722 }
2723
2724 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
2725 {
2726 wxCHECK_MSG( !m_needParent || parent, false, wxT("Need complete parent.") );
2727
2728 // Use either the given size, or the default if -1 is given.
2729 // See wxWindowBase for these functions.
2730 m_width = WidthDefault(size.x) ;
2731 m_height = HeightDefault(size.y);
2732
2733 m_x = (int)pos.x;
2734 m_y = (int)pos.y;
2735
2736 return true;
2737 }
2738
2739 void wxWindowGTK::PostCreation()
2740 {
2741 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2742
2743 if (m_wxwindow)
2744 {
2745 if (!m_noExpose)
2746 {
2747 // these get reported to wxWidgets -> wxPaintEvent
2748
2749 gtk_pizza_set_external( GTK_PIZZA(m_wxwindow), TRUE );
2750
2751 g_signal_connect (m_wxwindow, "expose_event",
2752 G_CALLBACK (gtk_window_expose_callback), this);
2753
2754 gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), HasFlag( wxFULL_REPAINT_ON_RESIZE ) );
2755 }
2756
2757 // Create input method handler
2758 m_imData = new wxGtkIMData;
2759
2760 // Cannot handle drawing preedited text yet
2761 gtk_im_context_set_use_preedit( m_imData->context, FALSE );
2762
2763 g_signal_connect (m_imData->context, "commit",
2764 G_CALLBACK (gtk_wxwindow_commit_cb), this);
2765
2766 // these are called when the "sunken" or "raised" borders are drawn
2767 g_signal_connect (m_widget, "expose_event",
2768 G_CALLBACK (gtk_window_own_expose_callback), this);
2769 }
2770
2771 // focus handling
2772
2773 if (!GTK_IS_WINDOW(m_widget))
2774 {
2775 if (m_focusWidget == NULL)
2776 m_focusWidget = m_widget;
2777
2778 if (m_wxwindow)
2779 {
2780 g_signal_connect (m_focusWidget, "focus_in_event",
2781 G_CALLBACK (gtk_window_focus_in_callback), this);
2782 g_signal_connect (m_focusWidget, "focus_out_event",
2783 G_CALLBACK (gtk_window_focus_out_callback), this);
2784 }
2785 else
2786 {
2787 g_signal_connect_after (m_focusWidget, "focus_in_event",
2788 G_CALLBACK (gtk_window_focus_in_callback), this);
2789 g_signal_connect_after (m_focusWidget, "focus_out_event",
2790 G_CALLBACK (gtk_window_focus_out_callback), this);
2791 }
2792 }
2793
2794 // connect to the various key and mouse handlers
2795
2796 GtkWidget *connect_widget = GetConnectWidget();
2797
2798 ConnectWidget( connect_widget );
2799
2800 /* We cannot set colours, fonts and cursors before the widget has
2801 been realized, so we do this directly after realization */
2802 g_signal_connect (connect_widget, "realize",
2803 G_CALLBACK (gtk_window_realized_callback), this);
2804
2805 if (m_wxwindow)
2806 {
2807 // Catch native resize events
2808 g_signal_connect (m_wxwindow, "size_allocate",
2809 G_CALLBACK (gtk_window_size_callback), this);
2810
2811 // Initialize XIM support
2812 g_signal_connect (m_wxwindow, "realize",
2813 G_CALLBACK (gtk_wxwindow_realized_callback), this);
2814
2815 // And resize XIM window
2816 g_signal_connect (m_wxwindow, "size_allocate",
2817 G_CALLBACK (gtk_wxwindow_size_callback), this);
2818 }
2819
2820 if (GTK_IS_COMBO(m_widget))
2821 {
2822 GtkCombo *gcombo = GTK_COMBO(m_widget);
2823
2824 g_signal_connect (gcombo->entry, "size_request",
2825 G_CALLBACK (wxgtk_combo_size_request_callback),
2826 this);
2827 }
2828 else
2829 {
2830 // This is needed if we want to add our windows into native
2831 // GTK controls, such as the toolbar. With this callback, the
2832 // toolbar gets to know the correct size (the one set by the
2833 // programmer). Sadly, it misbehaves for wxComboBox.
2834 g_signal_connect (m_widget, "size_request",
2835 G_CALLBACK (wxgtk_window_size_request_callback),
2836 this);
2837 }
2838
2839 InheritAttributes();
2840
2841 m_hasVMT = true;
2842
2843 // unless the window was created initially hidden (i.e. Hide() had been
2844 // called before Create()), we should show it at GTK+ level as well
2845 if ( IsShown() )
2846 gtk_widget_show( m_widget );
2847 }
2848
2849 void wxWindowGTK::ConnectWidget( GtkWidget *widget )
2850 {
2851 g_signal_connect (widget, "key_press_event",
2852 G_CALLBACK (gtk_window_key_press_callback), this);
2853 g_signal_connect (widget, "key_release_event",
2854 G_CALLBACK (gtk_window_key_release_callback), this);
2855 g_signal_connect (widget, "button_press_event",
2856 G_CALLBACK (gtk_window_button_press_callback), this);
2857 g_signal_connect (widget, "button_release_event",
2858 G_CALLBACK (gtk_window_button_release_callback), this);
2859 g_signal_connect (widget, "motion_notify_event",
2860 G_CALLBACK (gtk_window_motion_notify_callback), this);
2861 g_signal_connect (widget, "scroll_event",
2862 G_CALLBACK (gtk_window_wheel_callback), this);
2863 g_signal_connect (widget, "popup_menu",
2864 G_CALLBACK (wxgtk_window_popup_menu_callback), this);
2865 g_signal_connect (widget, "enter_notify_event",
2866 G_CALLBACK (gtk_window_enter_callback), this);
2867 g_signal_connect (widget, "leave_notify_event",
2868 G_CALLBACK (gtk_window_leave_callback), this);
2869 }
2870
2871 bool wxWindowGTK::Destroy()
2872 {
2873 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2874
2875 m_hasVMT = false;
2876
2877 return wxWindowBase::Destroy();
2878 }
2879
2880 void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
2881 {
2882 gtk_pizza_set_size( GTK_PIZZA(m_parent->m_wxwindow), m_widget, x, y, width, height );
2883 }
2884
2885 void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
2886 {
2887 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2888 wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") );
2889
2890 /*
2891 printf( "DoSetSize: name %s, x,y,w,h: %d,%d,%d,%d \n", GetName().c_str(), x,y,width,height );
2892 */
2893
2894 if (m_resizing) return; /* I don't like recursions */
2895 m_resizing = true;
2896
2897 int currentX, currentY;
2898 GetPosition(&currentX, &currentY);
2899 if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2900 x = currentX;
2901 if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2902 y = currentY;
2903 AdjustForParentClientOrigin(x, y, sizeFlags);
2904
2905 // calculate the best size if we should auto size the window
2906 if ( ((sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1) ||
2907 ((sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1) )
2908 {
2909 const wxSize sizeBest = GetBestSize();
2910 if ( (sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1 )
2911 width = sizeBest.x;
2912 if ( (sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1 )
2913 height = sizeBest.y;
2914 }
2915
2916 if (width != -1)
2917 m_width = width;
2918 if (height != -1)
2919 m_height = height;
2920
2921 int minWidth = GetMinWidth(),
2922 minHeight = GetMinHeight(),
2923 maxWidth = GetMaxWidth(),
2924 maxHeight = GetMaxHeight();
2925
2926 if ((minWidth != -1) && (m_width < minWidth )) m_width = minWidth;
2927 if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight;
2928 if ((maxWidth != -1) && (m_width > maxWidth )) m_width = maxWidth;
2929 if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
2930
2931 #if wxUSE_TOOLBAR_NATIVE
2932 if (wxDynamicCast(GetParent(), wxToolBar))
2933 {
2934 // don't take the x,y values, they're wrong because toolbar sets them
2935 GtkWidget *widget = GTK_WIDGET(m_widget);
2936 gtk_widget_set_size_request (widget, m_width, m_height);
2937 if (GTK_WIDGET_VISIBLE (widget))
2938 gtk_widget_queue_resize (widget);
2939 }
2940 else
2941 #endif
2942 if (m_parent->m_wxwindow == NULL) // i.e. wxNotebook
2943 {
2944 // don't set the size for children of wxNotebook, just take the values.
2945 m_x = x;
2946 m_y = y;
2947 m_width = width;
2948 m_height = height;
2949 }
2950 else
2951 {
2952 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
2953 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
2954 {
2955 if (x != -1) m_x = x + pizza->xoffset;
2956 if (y != -1) m_y = y + pizza->yoffset;
2957 }
2958 else
2959 {
2960 m_x = x + pizza->xoffset;
2961 m_y = y + pizza->yoffset;
2962 }
2963
2964 int left_border = 0;
2965 int right_border = 0;
2966 int top_border = 0;
2967 int bottom_border = 0;
2968
2969 /* the default button has a border around it */
2970 if (GTK_WIDGET_CAN_DEFAULT(m_widget))
2971 {
2972 GtkBorder *default_border = NULL;
2973 gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
2974 if (default_border)
2975 {
2976 left_border += default_border->left;
2977 right_border += default_border->right;
2978 top_border += default_border->top;
2979 bottom_border += default_border->bottom;
2980 g_free( default_border );
2981 }
2982 }
2983
2984 DoMoveWindow( m_x-top_border,
2985 m_y-left_border,
2986 m_width+left_border+right_border,
2987 m_height+top_border+bottom_border );
2988 }
2989
2990 if (m_hasScrolling)
2991 {
2992 /* Sometimes the client area changes size without the
2993 whole windows's size changing, but if the whole
2994 windows's size doesn't change, no wxSizeEvent will
2995 normally be sent. Here we add an extra test if
2996 the client test has been changed and this will
2997 be used then. */
2998 GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
2999 }
3000
3001 /*
3002 wxPrintf( "OnSize sent from " );
3003 if (GetClassInfo() && GetClassInfo()->GetClassName())
3004 wxPrintf( GetClassInfo()->GetClassName() );
3005 wxPrintf( " %d %d %d %d\n", (int)m_x, (int)m_y, (int)m_width, (int)m_height );
3006 */
3007
3008 if (!m_nativeSizeEvent)
3009 {
3010 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
3011 event.SetEventObject( this );
3012 GetEventHandler()->ProcessEvent( event );
3013 }
3014
3015 m_resizing = false;
3016 }
3017
3018 void wxWindowGTK::OnInternalIdle()
3019 {
3020 if ( m_dirtyTabOrder )
3021 {
3022 m_dirtyTabOrder = false;
3023 RealizeTabOrder();
3024 }
3025
3026 // Update style if the window was not yet realized
3027 // and SetBackgroundStyle(wxBG_STYLE_CUSTOM) was called
3028 if (m_needsStyleChange)
3029 {
3030 SetBackgroundStyle(GetBackgroundStyle());
3031 m_needsStyleChange = false;
3032 }
3033
3034 // Update invalidated regions.
3035 GtkUpdate();
3036
3037 wxCursor cursor = m_cursor;
3038 if (g_globalCursor.Ok()) cursor = g_globalCursor;
3039
3040 if (cursor.Ok())
3041 {
3042 /* I now set the cursor anew in every OnInternalIdle call
3043 as setting the cursor in a parent window also effects the
3044 windows above so that checking for the current cursor is
3045 not possible. */
3046
3047 if (m_wxwindow)
3048 {
3049 GdkWindow *window = GTK_PIZZA(m_wxwindow)->bin_window;
3050 if (window)
3051 gdk_window_set_cursor( window, cursor.GetCursor() );
3052
3053 if (!g_globalCursor.Ok())
3054 cursor = *wxSTANDARD_CURSOR;
3055
3056 window = m_widget->window;
3057 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
3058 gdk_window_set_cursor( window, cursor.GetCursor() );
3059
3060 }
3061 else
3062 {
3063
3064 GdkWindow *window = m_widget->window;
3065 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
3066 gdk_window_set_cursor( window, cursor.GetCursor() );
3067
3068 }
3069 }
3070
3071 if (wxUpdateUIEvent::CanUpdate(this))
3072 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
3073 }
3074
3075 void wxWindowGTK::DoGetSize( int *width, int *height ) const
3076 {
3077 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3078
3079 if (width) (*width) = m_width;
3080 if (height) (*height) = m_height;
3081 }
3082
3083 void wxWindowGTK::DoSetClientSize( int width, int height )
3084 {
3085 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3086
3087 if (!m_wxwindow)
3088 {
3089 SetSize( width, height );
3090 }
3091 else
3092 {
3093 int dw = 0;
3094 int dh = 0;
3095
3096 #ifndef __WXUNIVERSAL__
3097 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3098 {
3099 /* when using GTK 1.2 we set the shadow border size to 2 */
3100 dw += 2 * 2;
3101 dh += 2 * 2;
3102 }
3103 if (HasFlag(wxSIMPLE_BORDER))
3104 {
3105 /* when using GTK 1.2 we set the simple border size to 1 */
3106 dw += 1 * 2;
3107 dh += 1 * 2;
3108 }
3109 #endif // __WXUNIVERSAL__
3110
3111 if (m_hasScrolling)
3112 {
3113 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3114
3115 GtkRequisition vscroll_req;
3116 vscroll_req.width = 2;
3117 vscroll_req.height = 2;
3118 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3119 (scroll_window->vscrollbar, &vscroll_req );
3120
3121 GtkRequisition hscroll_req;
3122 hscroll_req.width = 2;
3123 hscroll_req.height = 2;
3124 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3125 (scroll_window->hscrollbar, &hscroll_req );
3126
3127 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3128
3129 if (scroll_window->vscrollbar_visible)
3130 {
3131 dw += vscroll_req.width;
3132 dw += scroll_class->scrollbar_spacing;
3133 }
3134
3135 if (scroll_window->hscrollbar_visible)
3136 {
3137 dh += hscroll_req.height;
3138 dh += scroll_class->scrollbar_spacing;
3139 }
3140 }
3141
3142 SetSize( width+dw, height+dh );
3143 }
3144 }
3145
3146 void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
3147 {
3148 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3149
3150 if (!m_wxwindow)
3151 {
3152 if (width) (*width) = m_width;
3153 if (height) (*height) = m_height;
3154 }
3155 else
3156 {
3157 int dw = 0;
3158 int dh = 0;
3159
3160 #ifndef __WXUNIVERSAL__
3161 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3162 {
3163 /* when using GTK 1.2 we set the shadow border size to 2 */
3164 dw += 2 * 2;
3165 dh += 2 * 2;
3166 }
3167 if (HasFlag(wxSIMPLE_BORDER))
3168 {
3169 /* when using GTK 1.2 we set the simple border size to 1 */
3170 dw += 1 * 2;
3171 dh += 1 * 2;
3172 }
3173 #endif // __WXUNIVERSAL__
3174
3175 if (m_hasScrolling)
3176 {
3177 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3178
3179 GtkRequisition vscroll_req;
3180 vscroll_req.width = 2;
3181 vscroll_req.height = 2;
3182 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3183 (scroll_window->vscrollbar, &vscroll_req );
3184
3185 GtkRequisition hscroll_req;
3186 hscroll_req.width = 2;
3187 hscroll_req.height = 2;
3188 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3189 (scroll_window->hscrollbar, &hscroll_req );
3190
3191 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3192
3193 if (scroll_window->vscrollbar_visible)
3194 {
3195 dw += vscroll_req.width;
3196 dw += scroll_class->scrollbar_spacing;
3197 }
3198
3199 if (scroll_window->hscrollbar_visible)
3200 {
3201 dh += hscroll_req.height;
3202 dh += scroll_class->scrollbar_spacing;
3203 }
3204 }
3205
3206 if (width) (*width) = m_width - dw;
3207 if (height) (*height) = m_height - dh;
3208 }
3209
3210 /*
3211 printf( "GetClientSize, name %s ", GetName().c_str() );
3212 if (width) printf( " width = %d", (*width) );
3213 if (height) printf( " height = %d", (*height) );
3214 printf( "\n" );
3215 */
3216 }
3217
3218 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
3219 {
3220 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3221
3222 int dx = 0;
3223 int dy = 0;
3224 if (m_parent && m_parent->m_wxwindow)
3225 {
3226 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
3227 dx = pizza->xoffset;
3228 dy = pizza->yoffset;
3229 }
3230
3231 if (x) (*x) = m_x - dx;
3232 if (y) (*y) = m_y - dy;
3233 }
3234
3235 void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
3236 {
3237 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3238
3239 if (!m_widget->window) return;
3240
3241 GdkWindow *source = (GdkWindow *) NULL;
3242 if (m_wxwindow)
3243 source = GTK_PIZZA(m_wxwindow)->bin_window;
3244 else
3245 source = m_widget->window;
3246
3247 int org_x = 0;
3248 int org_y = 0;
3249 gdk_window_get_origin( source, &org_x, &org_y );
3250
3251 if (!m_wxwindow)
3252 {
3253 if (GTK_WIDGET_NO_WINDOW (m_widget))
3254 {
3255 org_x += m_widget->allocation.x;
3256 org_y += m_widget->allocation.y;
3257 }
3258 }
3259
3260 if (x) *x += org_x;
3261 if (y) *y += org_y;
3262 }
3263
3264 void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
3265 {
3266 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3267
3268 if (!m_widget->window) return;
3269
3270 GdkWindow *source = (GdkWindow *) NULL;
3271 if (m_wxwindow)
3272 source = GTK_PIZZA(m_wxwindow)->bin_window;
3273 else
3274 source = m_widget->window;
3275
3276 int org_x = 0;
3277 int org_y = 0;
3278 gdk_window_get_origin( source, &org_x, &org_y );
3279
3280 if (!m_wxwindow)
3281 {
3282 if (GTK_WIDGET_NO_WINDOW (m_widget))
3283 {
3284 org_x += m_widget->allocation.x;
3285 org_y += m_widget->allocation.y;
3286 }
3287 }
3288
3289 if (x) *x -= org_x;
3290 if (y) *y -= org_y;
3291 }
3292
3293 bool wxWindowGTK::Show( bool show )
3294 {
3295 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3296
3297 if (!wxWindowBase::Show(show))
3298 {
3299 // nothing to do
3300 return false;
3301 }
3302
3303 if (show)
3304 gtk_widget_show( m_widget );
3305 else
3306 gtk_widget_hide( m_widget );
3307
3308 wxShowEvent eventShow(GetId(), show);
3309 eventShow.SetEventObject(this);
3310
3311 GetEventHandler()->ProcessEvent(eventShow);
3312
3313 return true;
3314 }
3315
3316 static void wxWindowNotifyEnable(wxWindowGTK* win, bool enable)
3317 {
3318 win->OnParentEnable(enable);
3319
3320 // Recurse, so that children have the opportunity to Do The Right Thing
3321 // and reset colours that have been messed up by a parent's (really ancestor's)
3322 // Enable call
3323 for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
3324 node;
3325 node = node->GetNext() )
3326 {
3327 wxWindow *child = node->GetData();
3328 if (!child->IsKindOf(CLASSINFO(wxDialog)) && !child->IsKindOf(CLASSINFO(wxFrame)))
3329 wxWindowNotifyEnable(child, enable);
3330 }
3331 }
3332
3333 bool wxWindowGTK::Enable( bool enable )
3334 {
3335 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3336
3337 if (!wxWindowBase::Enable(enable))
3338 {
3339 // nothing to do
3340 return false;
3341 }
3342
3343 gtk_widget_set_sensitive( m_widget, enable );
3344 if ( m_wxwindow )
3345 gtk_widget_set_sensitive( m_wxwindow, enable );
3346
3347 wxWindowNotifyEnable(this, enable);
3348
3349 return true;
3350 }
3351
3352 int wxWindowGTK::GetCharHeight() const
3353 {
3354 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
3355
3356 wxFont font = GetFont();
3357 wxCHECK_MSG( font.Ok(), 12, wxT("invalid font") );
3358
3359 PangoContext *context = NULL;
3360 if (m_widget)
3361 context = gtk_widget_get_pango_context( m_widget );
3362
3363 if (!context)
3364 return 0;
3365
3366 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3367 PangoLayout *layout = pango_layout_new(context);
3368 pango_layout_set_font_description(layout, desc);
3369 pango_layout_set_text(layout, "H", 1);
3370 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3371
3372 PangoRectangle rect;
3373 pango_layout_line_get_extents(line, NULL, &rect);
3374
3375 g_object_unref( G_OBJECT( layout ) );
3376
3377 return (int) PANGO_PIXELS(rect.height);
3378 }
3379
3380 int wxWindowGTK::GetCharWidth() const
3381 {
3382 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
3383
3384 wxFont font = GetFont();
3385 wxCHECK_MSG( font.Ok(), 8, wxT("invalid font") );
3386
3387 PangoContext *context = NULL;
3388 if (m_widget)
3389 context = gtk_widget_get_pango_context( m_widget );
3390
3391 if (!context)
3392 return 0;
3393
3394 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3395 PangoLayout *layout = pango_layout_new(context);
3396 pango_layout_set_font_description(layout, desc);
3397 pango_layout_set_text(layout, "g", 1);
3398 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3399
3400 PangoRectangle rect;
3401 pango_layout_line_get_extents(line, NULL, &rect);
3402
3403 g_object_unref( G_OBJECT( layout ) );
3404
3405 return (int) PANGO_PIXELS(rect.width);
3406 }
3407
3408 void wxWindowGTK::GetTextExtent( const wxString& string,
3409 int *x,
3410 int *y,
3411 int *descent,
3412 int *externalLeading,
3413 const wxFont *theFont ) const
3414 {
3415 wxFont fontToUse = theFont ? *theFont : GetFont();
3416
3417 wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") );
3418
3419 if (string.empty())
3420 {
3421 if (x) (*x) = 0;
3422 if (y) (*y) = 0;
3423 return;
3424 }
3425
3426 PangoContext *context = NULL;
3427 if (m_widget)
3428 context = gtk_widget_get_pango_context( m_widget );
3429
3430 if (!context)
3431 {
3432 if (x) (*x) = 0;
3433 if (y) (*y) = 0;
3434 return;
3435 }
3436
3437 PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
3438 PangoLayout *layout = pango_layout_new(context);
3439 pango_layout_set_font_description(layout, desc);
3440 {
3441 const wxCharBuffer data = wxGTK_CONV( string );
3442 if ( data )
3443 pango_layout_set_text(layout, data, strlen(data));
3444 }
3445
3446 PangoRectangle rect;
3447 pango_layout_get_extents(layout, NULL, &rect);
3448
3449 if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width);
3450 if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height);
3451 if (descent)
3452 {
3453 PangoLayoutIter *iter = pango_layout_get_iter(layout);
3454 int baseline = pango_layout_iter_get_baseline(iter);
3455 pango_layout_iter_free(iter);
3456 *descent = *y - PANGO_PIXELS(baseline);
3457 }
3458 if (externalLeading) (*externalLeading) = 0; // ??
3459
3460 g_object_unref( G_OBJECT( layout ) );
3461 }
3462
3463 void wxWindowGTK::SetFocus()
3464 {
3465 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3466 if ( m_hasFocus )
3467 {
3468 // don't do anything if we already have focus
3469 return;
3470 }
3471
3472 if (m_wxwindow)
3473 {
3474 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
3475 {
3476 gtk_widget_grab_focus (m_wxwindow);
3477 }
3478 }
3479 else if (m_widget)
3480 {
3481 if (GTK_IS_CONTAINER(m_widget))
3482 {
3483 gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
3484 }
3485 else
3486 if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
3487 {
3488
3489 if (!GTK_WIDGET_REALIZED(m_widget))
3490 {
3491 // we can't set the focus to the widget now so we remember that
3492 // it should be focused and will do it later, during the idle
3493 // time, as soon as we can
3494 wxLogTrace(TRACE_FOCUS,
3495 _T("Delaying setting focus to %s(%s)"),
3496 GetClassInfo()->GetClassName(), GetLabel().c_str());
3497
3498 g_delayedFocus = this;
3499 }
3500 else
3501 {
3502 wxLogTrace(TRACE_FOCUS,
3503 _T("Setting focus to %s(%s)"),
3504 GetClassInfo()->GetClassName(), GetLabel().c_str());
3505
3506 gtk_widget_grab_focus (m_widget);
3507 }
3508 }
3509 else
3510 {
3511 wxLogTrace(TRACE_FOCUS,
3512 _T("Can't set focus to %s(%s)"),
3513 GetClassInfo()->GetClassName(), GetLabel().c_str());
3514 }
3515 }
3516 }
3517
3518 bool wxWindowGTK::AcceptsFocus() const
3519 {
3520 return m_acceptsFocus && wxWindowBase::AcceptsFocus();
3521 }
3522
3523 bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
3524 {
3525 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3526
3527 wxWindowGTK *oldParent = m_parent,
3528 *newParent = (wxWindowGTK *)newParentBase;
3529
3530 wxASSERT( GTK_IS_WIDGET(m_widget) );
3531
3532 if ( !wxWindowBase::Reparent(newParent) )
3533 return false;
3534
3535 wxASSERT( GTK_IS_WIDGET(m_widget) );
3536
3537 /* prevent GTK from deleting the widget arbitrarily */
3538 gtk_widget_ref( m_widget );
3539
3540 if (oldParent)
3541 {
3542 gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
3543 }
3544
3545 wxASSERT( GTK_IS_WIDGET(m_widget) );
3546
3547 if (newParent)
3548 {
3549 /* insert GTK representation */
3550 (*(newParent->m_insertCallback))(newParent, this);
3551 }
3552
3553 /* reverse: prevent GTK from deleting the widget arbitrarily */
3554 gtk_widget_unref( m_widget );
3555
3556 return true;
3557 }
3558
3559 void wxWindowGTK::DoAddChild(wxWindowGTK *child)
3560 {
3561 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
3562
3563 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
3564
3565 wxASSERT_MSG( (m_insertCallback != NULL), wxT("invalid child insertion function") );
3566
3567 /* add to list */
3568 AddChild( child );
3569
3570 /* insert GTK representation */
3571 (*m_insertCallback)(this, child);
3572 }
3573
3574 void wxWindowGTK::AddChild(wxWindowBase *child)
3575 {
3576 wxWindowBase::AddChild(child);
3577 m_dirtyTabOrder = true;
3578 if (g_isIdle)
3579 wxapp_install_idle_handler();
3580 }
3581
3582 void wxWindowGTK::RemoveChild(wxWindowBase *child)
3583 {
3584 wxWindowBase::RemoveChild(child);
3585 m_dirtyTabOrder = true;
3586 if (g_isIdle)
3587 wxapp_install_idle_handler();
3588 }
3589
3590 void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, MoveKind move)
3591 {
3592 wxWindowBase::DoMoveInTabOrder(win, move);
3593 m_dirtyTabOrder = true;
3594 if (g_isIdle)
3595 wxapp_install_idle_handler();
3596 }
3597
3598 bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3599 {
3600 // none needed by default
3601 return false;
3602 }
3603
3604 void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget* WXUNUSED(w))
3605 {
3606 // nothing to do by default since none is needed
3607 }
3608
3609 void wxWindowGTK::RealizeTabOrder()
3610 {
3611 if (m_wxwindow)
3612 {
3613 if ( !m_children.empty() )
3614 {
3615 // we don't only construct the correct focus chain but also use
3616 // this opportunity to update the mnemonic widgets for the widgets
3617 // that need them
3618
3619 GList *chain = NULL;
3620 wxWindowGTK* mnemonicWindow = NULL;
3621
3622 for ( wxWindowList::const_iterator i = m_children.begin();
3623 i != m_children.end();
3624 ++i )
3625 {
3626 wxWindowGTK *win = *i;
3627
3628 if ( mnemonicWindow )
3629 {
3630 if ( win->AcceptsFocusFromKeyboard() )
3631 {
3632 // wxComboBox et al. needs to focus on on a different
3633 // widget than m_widget, so if the main widget isn't
3634 // focusable try the connect widget
3635 GtkWidget* w = win->m_widget;
3636 if ( !GTK_WIDGET_CAN_FOCUS(w) )
3637 {
3638 w = win->GetConnectWidget();
3639 if ( !GTK_WIDGET_CAN_FOCUS(w) )
3640 w = NULL;
3641 }
3642
3643 if ( w )
3644 {
3645 mnemonicWindow->GTKWidgetDoSetMnemonic(w);
3646 mnemonicWindow = NULL;
3647 }
3648 }
3649 }
3650 else if ( win->GTKWidgetNeedsMnemonic() )
3651 {
3652 mnemonicWindow = win;
3653 }
3654
3655 chain = g_list_prepend(chain, win->m_widget);
3656 }
3657
3658 chain = g_list_reverse(chain);
3659
3660 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain);
3661 g_list_free(chain);
3662 }
3663 else // no children
3664 {
3665 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow));
3666 }
3667 }
3668 }
3669
3670 void wxWindowGTK::Raise()
3671 {
3672 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3673
3674 if (m_wxwindow && m_wxwindow->window)
3675 {
3676 gdk_window_raise( m_wxwindow->window );
3677 }
3678 else if (m_widget->window)
3679 {
3680 gdk_window_raise( m_widget->window );
3681 }
3682 }
3683
3684 void wxWindowGTK::Lower()
3685 {
3686 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3687
3688 if (m_wxwindow && m_wxwindow->window)
3689 {
3690 gdk_window_lower( m_wxwindow->window );
3691 }
3692 else if (m_widget->window)
3693 {
3694 gdk_window_lower( m_widget->window );
3695 }
3696 }
3697
3698 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
3699 {
3700 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3701
3702 if (cursor == m_cursor)
3703 return false;
3704
3705 if (g_isIdle)
3706 wxapp_install_idle_handler();
3707
3708 if (cursor == wxNullCursor)
3709 return wxWindowBase::SetCursor( *wxSTANDARD_CURSOR );
3710 else
3711 return wxWindowBase::SetCursor( cursor );
3712 }
3713
3714 void wxWindowGTK::WarpPointer( int x, int y )
3715 {
3716 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3717
3718 // We provide this function ourselves as it is
3719 // missing in GDK (top of this file).
3720
3721 GdkWindow *window = (GdkWindow*) NULL;
3722 if (m_wxwindow)
3723 window = GTK_PIZZA(m_wxwindow)->bin_window;
3724 else
3725 window = GetConnectWidget()->window;
3726
3727 if (window)
3728 gdk_window_warp_pointer( window, x, y );
3729 }
3730
3731 static bool wxScrollAdjust(GtkAdjustment* adj, double change)
3732 {
3733 double value_start = adj->value;
3734 double value = value_start + change;
3735 double upper = adj->upper - adj->page_size;
3736 if (value > upper)
3737 {
3738 value = upper;
3739 }
3740 // Lower bound will be checked by gtk_adjustment_set_value
3741 gtk_adjustment_set_value(adj, value);
3742 return adj->value != value_start;
3743 }
3744
3745 bool wxWindowGTK::ScrollLines(int lines)
3746 {
3747 return
3748 m_vAdjust != NULL &&
3749 wxScrollAdjust(m_vAdjust, lines * m_vAdjust->step_increment);
3750 }
3751
3752 bool wxWindowGTK::ScrollPages(int pages)
3753 {
3754 return
3755 m_vAdjust != NULL &&
3756 wxScrollAdjust(m_vAdjust, pages * m_vAdjust->page_increment);
3757 }
3758
3759 void wxWindowGTK::SetVScrollAdjustment(GtkAdjustment* adj)
3760 {
3761 wxASSERT(m_vAdjust == NULL);
3762 m_vAdjust = adj;
3763 }
3764
3765 void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
3766 {
3767 if (!m_widget)
3768 return;
3769 if (!m_widget->window)
3770 return;
3771
3772 if (m_wxwindow)
3773 {
3774 GdkRectangle gdk_rect,
3775 *p;
3776 if (rect)
3777 {
3778 gdk_rect.x = rect->x;
3779 gdk_rect.y = rect->y;
3780 gdk_rect.width = rect->width;
3781 gdk_rect.height = rect->height;
3782 p = &gdk_rect;
3783 }
3784 else // invalidate everything
3785 {
3786 p = NULL;
3787 }
3788
3789 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, p, TRUE );
3790 }
3791 }
3792
3793 void wxWindowGTK::Update()
3794 {
3795 GtkUpdate();
3796
3797 // when we call Update() we really want to update the window immediately on
3798 // screen, even if it means flushing the entire queue and hence slowing down
3799 // everything -- but it should still be done, it's just that Update() should
3800 // be called very rarely
3801 gdk_flush();
3802 }
3803
3804 void wxWindowGTK::GtkUpdate()
3805 {
3806 if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window)
3807 gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE );
3808
3809 // for consistency with other platforms (and also because it's convenient
3810 // to be able to update an entire TLW by calling Update() only once), we
3811 // should also update all our children here
3812 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
3813 node;
3814 node = node->GetNext() )
3815 {
3816 node->GetData()->GtkUpdate();
3817 }
3818 }
3819
3820 void wxWindowGTK::GtkSendPaintEvents()
3821 {
3822 if (!m_wxwindow)
3823 {
3824 m_updateRegion.Clear();
3825 return;
3826 }
3827
3828 // Clip to paint region in wxClientDC
3829 m_clipPaintRegion = true;
3830
3831 // widget to draw on
3832 GtkPizza *pizza = GTK_PIZZA (m_wxwindow);
3833
3834 if (GetThemeEnabled() && (GetBackgroundStyle() == wxBG_STYLE_SYSTEM))
3835 {
3836 // find ancestor from which to steal background
3837 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
3838 if (!parent)
3839 parent = (wxWindow*)this;
3840
3841 if (GTK_WIDGET_MAPPED(parent->m_widget))
3842 {
3843 wxRegionIterator upd( m_updateRegion );
3844 while (upd)
3845 {
3846 GdkRectangle rect;
3847 rect.x = upd.GetX();
3848 rect.y = upd.GetY();
3849 rect.width = upd.GetWidth();
3850 rect.height = upd.GetHeight();
3851
3852 gtk_paint_flat_box( parent->m_widget->style,
3853 pizza->bin_window,
3854 (GtkStateType)GTK_WIDGET_STATE(m_wxwindow),
3855 GTK_SHADOW_NONE,
3856 &rect,
3857 parent->m_widget,
3858 (char *)"base",
3859 0, 0, -1, -1 );
3860
3861 ++upd;
3862 }
3863 }
3864 }
3865 else
3866
3867 {
3868 wxWindowDC dc( (wxWindow*)this );
3869 dc.SetClippingRegion( m_updateRegion );
3870
3871 wxEraseEvent erase_event( GetId(), &dc );
3872 erase_event.SetEventObject( this );
3873
3874 GetEventHandler()->ProcessEvent(erase_event);
3875 }
3876
3877 wxNcPaintEvent nc_paint_event( GetId() );
3878 nc_paint_event.SetEventObject( this );
3879 GetEventHandler()->ProcessEvent( nc_paint_event );
3880
3881 wxPaintEvent paint_event( GetId() );
3882 paint_event.SetEventObject( this );
3883 GetEventHandler()->ProcessEvent( paint_event );
3884
3885 m_clipPaintRegion = false;
3886
3887 m_updateRegion.Clear();
3888 }
3889
3890 void wxWindowGTK::ClearBackground()
3891 {
3892 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3893 }
3894
3895 #if wxUSE_TOOLTIPS
3896 void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
3897 {
3898 wxWindowBase::DoSetToolTip(tip);
3899
3900 if (m_tooltip)
3901 m_tooltip->Apply( (wxWindow *)this );
3902 }
3903
3904 void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
3905 {
3906 wxString tmp( tip );
3907 gtk_tooltips_set_tip( tips, GetConnectWidget(), wxGTK_CONV(tmp), (gchar*) NULL );
3908 }
3909 #endif // wxUSE_TOOLTIPS
3910
3911 bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
3912 {
3913 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3914
3915 if (!wxWindowBase::SetBackgroundColour(colour))
3916 return false;
3917
3918 if (colour.Ok())
3919 {
3920 // We need the pixel value e.g. for background clearing.
3921 m_backgroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
3922 }
3923
3924 // apply style change (forceStyle=true so that new style is applied
3925 // even if the bg colour changed from valid to wxNullColour)
3926 if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM)
3927 ApplyWidgetStyle(true);
3928
3929 return true;
3930 }
3931
3932 bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
3933 {
3934 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3935
3936 if (!wxWindowBase::SetForegroundColour(colour))
3937 {
3938 return false;
3939 }
3940
3941 if (colour.Ok())
3942 {
3943 // We need the pixel value e.g. for background clearing.
3944 m_foregroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
3945 }
3946
3947 // apply style change (forceStyle=true so that new style is applied
3948 // even if the bg colour changed from valid to wxNullColour):
3949 ApplyWidgetStyle(true);
3950
3951 return true;
3952 }
3953
3954 PangoContext *wxWindowGTK::GtkGetPangoDefaultContext()
3955 {
3956 return gtk_widget_get_pango_context( m_widget );
3957 }
3958
3959 GtkRcStyle *wxWindowGTK::CreateWidgetStyle(bool forceStyle)
3960 {
3961 // do we need to apply any changes at all?
3962 if ( !forceStyle &&
3963 !m_font.Ok() &&
3964 !m_foregroundColour.Ok() && !m_backgroundColour.Ok() )
3965 {
3966 return NULL;
3967 }
3968
3969 GtkRcStyle *style = gtk_rc_style_new();
3970
3971 if ( m_font.Ok() )
3972 {
3973 style->font_desc =
3974 pango_font_description_copy( m_font.GetNativeFontInfo()->description );
3975 }
3976
3977 if ( m_foregroundColour.Ok() )
3978 {
3979 GdkColor *fg = m_foregroundColour.GetColor();
3980
3981 style->fg[GTK_STATE_NORMAL] = *fg;
3982 style->color_flags[GTK_STATE_NORMAL] = GTK_RC_FG;
3983
3984 style->fg[GTK_STATE_PRELIGHT] = *fg;
3985 style->color_flags[GTK_STATE_PRELIGHT] = GTK_RC_FG;
3986
3987 style->fg[GTK_STATE_ACTIVE] = *fg;
3988 style->color_flags[GTK_STATE_ACTIVE] = GTK_RC_FG;
3989 }
3990
3991 if ( m_backgroundColour.Ok() )
3992 {
3993 GdkColor *bg = m_backgroundColour.GetColor();
3994
3995 style->bg[GTK_STATE_NORMAL] = *bg;
3996 style->base[GTK_STATE_NORMAL] = *bg;
3997 style->color_flags[GTK_STATE_NORMAL] = (GtkRcFlags)
3998 (style->color_flags[GTK_STATE_NORMAL] | GTK_RC_BG | GTK_RC_BASE);
3999
4000 style->bg[GTK_STATE_PRELIGHT] = *bg;
4001 style->base[GTK_STATE_PRELIGHT] = *bg;
4002 style->color_flags[GTK_STATE_PRELIGHT] = (GtkRcFlags)
4003 (style->color_flags[GTK_STATE_PRELIGHT] | GTK_RC_BG | GTK_RC_BASE);
4004
4005 style->bg[GTK_STATE_ACTIVE] = *bg;
4006 style->base[GTK_STATE_ACTIVE] = *bg;
4007 style->color_flags[GTK_STATE_ACTIVE] = (GtkRcFlags)
4008 (style->color_flags[GTK_STATE_ACTIVE] | GTK_RC_BG | GTK_RC_BASE);
4009
4010 style->bg[GTK_STATE_INSENSITIVE] = *bg;
4011 style->base[GTK_STATE_INSENSITIVE] = *bg;
4012 style->color_flags[GTK_STATE_INSENSITIVE] = (GtkRcFlags)
4013 (style->color_flags[GTK_STATE_INSENSITIVE] | GTK_RC_BG | GTK_RC_BASE);
4014 }
4015
4016 return style;
4017 }
4018
4019 void wxWindowGTK::ApplyWidgetStyle(bool forceStyle)
4020 {
4021 GtkRcStyle *style = CreateWidgetStyle(forceStyle);
4022 if ( style )
4023 {
4024 DoApplyWidgetStyle(style);
4025 gtk_rc_style_unref(style);
4026 }
4027
4028 // Style change may affect GTK+'s size calculation:
4029 InvalidateBestSize();
4030 }
4031
4032 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
4033 {
4034 if (m_wxwindow)
4035 gtk_widget_modify_style(m_wxwindow, style);
4036 else
4037 gtk_widget_modify_style(m_widget, style);
4038 }
4039
4040 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
4041 {
4042 wxWindowBase::SetBackgroundStyle(style);
4043
4044 if (style == wxBG_STYLE_CUSTOM)
4045 {
4046 GdkWindow *window = (GdkWindow*) NULL;
4047 if (m_wxwindow)
4048 window = GTK_PIZZA(m_wxwindow)->bin_window;
4049 else
4050 window = GetConnectWidget()->window;
4051
4052 if (window)
4053 {
4054 // Make sure GDK/X11 doesn't refresh the window
4055 // automatically.
4056 gdk_window_set_back_pixmap( window, None, False );
4057 #ifdef __X__
4058 Display* display = GDK_WINDOW_DISPLAY(window);
4059 XFlush(display);
4060 #endif
4061 m_needsStyleChange = false;
4062 }
4063 else
4064 // Do in OnIdle, because the window is not yet available
4065 m_needsStyleChange = true;
4066
4067 // Don't apply widget style, or we get a grey background
4068 }
4069 else
4070 {
4071 // apply style change (forceStyle=true so that new style is applied
4072 // even if the bg colour changed from valid to wxNullColour):
4073 ApplyWidgetStyle(true);
4074 }
4075 return true;
4076 }
4077
4078 #if wxUSE_DRAG_AND_DROP
4079
4080 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
4081 {
4082 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4083
4084 GtkWidget *dnd_widget = GetConnectWidget();
4085
4086 if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
4087
4088 if (m_dropTarget) delete m_dropTarget;
4089 m_dropTarget = dropTarget;
4090
4091 if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
4092 }
4093
4094 #endif // wxUSE_DRAG_AND_DROP
4095
4096 GtkWidget* wxWindowGTK::GetConnectWidget()
4097 {
4098 GtkWidget *connect_widget = m_widget;
4099 if (m_wxwindow) connect_widget = m_wxwindow;
4100
4101 return connect_widget;
4102 }
4103
4104 bool wxWindowGTK::IsOwnGtkWindow( GdkWindow *window )
4105 {
4106 if (m_wxwindow)
4107 return (window == GTK_PIZZA(m_wxwindow)->bin_window);
4108
4109 return (window == m_widget->window);
4110 }
4111
4112 bool wxWindowGTK::SetFont( const wxFont &font )
4113 {
4114 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
4115
4116 if (!wxWindowBase::SetFont(font))
4117 return false;
4118
4119 // apply style change (forceStyle=true so that new style is applied
4120 // even if the font changed from valid to wxNullFont):
4121 ApplyWidgetStyle(true);
4122
4123 return true;
4124 }
4125
4126 void wxWindowGTK::DoCaptureMouse()
4127 {
4128 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4129
4130 GdkWindow *window = (GdkWindow*) NULL;
4131 if (m_wxwindow)
4132 window = GTK_PIZZA(m_wxwindow)->bin_window;
4133 else
4134 window = GetConnectWidget()->window;
4135
4136 wxCHECK_RET( window, _T("CaptureMouse() failed") );
4137
4138 wxCursor* cursor = & m_cursor;
4139 if (!cursor->Ok())
4140 cursor = wxSTANDARD_CURSOR;
4141
4142 gdk_pointer_grab( window, FALSE,
4143 (GdkEventMask)
4144 (GDK_BUTTON_PRESS_MASK |
4145 GDK_BUTTON_RELEASE_MASK |
4146 GDK_POINTER_MOTION_HINT_MASK |
4147 GDK_POINTER_MOTION_MASK),
4148 (GdkWindow *) NULL,
4149 cursor->GetCursor(),
4150 (guint32)GDK_CURRENT_TIME );
4151 g_captureWindow = this;
4152 g_captureWindowHasMouse = true;
4153 }
4154
4155 void wxWindowGTK::DoReleaseMouse()
4156 {
4157 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4158
4159 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
4160
4161 g_captureWindow = (wxWindowGTK*) NULL;
4162
4163 GdkWindow *window = (GdkWindow*) NULL;
4164 if (m_wxwindow)
4165 window = GTK_PIZZA(m_wxwindow)->bin_window;
4166 else
4167 window = GetConnectWidget()->window;
4168
4169 if (!window)
4170 return;
4171
4172 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
4173 }
4174
4175 /* static */
4176 wxWindow *wxWindowBase::GetCapture()
4177 {
4178 return (wxWindow *)g_captureWindow;
4179 }
4180
4181 bool wxWindowGTK::IsRetained() const
4182 {
4183 return false;
4184 }
4185
4186 void wxWindowGTK::SetScrollbar( int orient, int pos, int thumbVisible,
4187 int range, bool refresh )
4188 {
4189 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4190
4191 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4192
4193 m_hasScrolling = true;
4194
4195 if (orient == wxHORIZONTAL)
4196 {
4197 float fpos = (float)pos;
4198 float frange = (float)range;
4199 float fthumb = (float)thumbVisible;
4200 if (fpos > frange-fthumb) fpos = frange-fthumb;
4201 if (fpos < 0.0) fpos = 0.0;
4202
4203 if ((fabs(frange-m_hAdjust->upper) < 0.2) &&
4204 (fabs(fthumb-m_hAdjust->page_size) < 0.2))
4205 {
4206 SetScrollPos( orient, pos, refresh );
4207 return;
4208 }
4209
4210 m_oldHorizontalPos = fpos;
4211
4212 m_hAdjust->lower = 0.0;
4213 m_hAdjust->upper = frange;
4214 m_hAdjust->value = fpos;
4215 m_hAdjust->step_increment = 1.0;
4216 m_hAdjust->page_increment = (float)(wxMax(fthumb,0));
4217 m_hAdjust->page_size = fthumb;
4218 }
4219 else
4220 {
4221 float fpos = (float)pos;
4222 float frange = (float)range;
4223 float fthumb = (float)thumbVisible;
4224 if (fpos > frange-fthumb) fpos = frange-fthumb;
4225 if (fpos < 0.0) fpos = 0.0;
4226
4227 if ((fabs(frange-m_vAdjust->upper) < 0.2) &&
4228 (fabs(fthumb-m_vAdjust->page_size) < 0.2))
4229 {
4230 SetScrollPos( orient, pos, refresh );
4231 return;
4232 }
4233
4234 m_oldVerticalPos = fpos;
4235
4236 m_vAdjust->lower = 0.0;
4237 m_vAdjust->upper = frange;
4238 m_vAdjust->value = fpos;
4239 m_vAdjust->step_increment = 1.0;
4240 m_vAdjust->page_increment = (float)(wxMax(fthumb,0));
4241 m_vAdjust->page_size = fthumb;
4242 }
4243
4244 if (orient == wxHORIZONTAL)
4245 g_signal_emit_by_name (m_hAdjust, "changed");
4246 else
4247 g_signal_emit_by_name (m_vAdjust, "changed");
4248 }
4249
4250 void wxWindowGTK::GtkUpdateScrollbar(int orient)
4251 {
4252 GtkAdjustment *adj = orient == wxHORIZONTAL ? m_hAdjust : m_vAdjust;
4253 gpointer fn = orient == wxHORIZONTAL
4254 ? (gpointer) gtk_window_hscroll_callback
4255 : (gpointer) gtk_window_vscroll_callback;
4256
4257 g_signal_handlers_disconnect_by_func (adj, fn, this);
4258 g_signal_emit_by_name (adj, "value_changed");
4259 g_signal_connect (adj, "value_changed", G_CALLBACK (fn), this);
4260 }
4261
4262 void wxWindowGTK::SetScrollPos( int orient, int pos, bool WXUNUSED(refresh) )
4263 {
4264 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4265 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4266
4267 GtkAdjustment *adj = orient == wxHORIZONTAL ? m_hAdjust : m_vAdjust;
4268
4269 float fpos = (float)pos;
4270 if (fpos > adj->upper - adj->page_size)
4271 fpos = adj->upper - adj->page_size;
4272 if (fpos < 0.0)
4273 fpos = 0.0;
4274 *(orient == wxHORIZONTAL ? &m_oldHorizontalPos : &m_oldVerticalPos) = fpos;
4275
4276 if (fabs(fpos-adj->value) < 0.2)
4277 return;
4278 adj->value = fpos;
4279
4280 if ( m_wxwindow->window )
4281 {
4282 }
4283 }
4284
4285 int wxWindowGTK::GetScrollThumb( int orient ) const
4286 {
4287 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4288
4289 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4290
4291 if (orient == wxHORIZONTAL)
4292 return (int)(m_hAdjust->page_size+0.5);
4293 else
4294 return (int)(m_vAdjust->page_size+0.5);
4295 }
4296
4297 int wxWindowGTK::GetScrollPos( int orient ) const
4298 {
4299 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4300
4301 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4302
4303 if (orient == wxHORIZONTAL)
4304 return (int)(m_hAdjust->value+0.5);
4305 else
4306 return (int)(m_vAdjust->value+0.5);
4307 }
4308
4309 int wxWindowGTK::GetScrollRange( int orient ) const
4310 {
4311 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4312
4313 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4314
4315 if (orient == wxHORIZONTAL)
4316 return (int)(m_hAdjust->upper+0.5);
4317 else
4318 return (int)(m_vAdjust->upper+0.5);
4319 }
4320
4321 void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
4322 {
4323 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4324
4325 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4326
4327 // No scrolling requested.
4328 if ((dx == 0) && (dy == 0)) return;
4329
4330 m_clipPaintRegion = true;
4331
4332 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy );
4333
4334 m_clipPaintRegion = false;
4335 }
4336
4337 void wxWindowGTK::SetWindowStyleFlag( long style )
4338 {
4339 // Updates the internal variable. NB: Now m_windowStyle bits carry the _new_ style values already
4340 wxWindowBase::SetWindowStyleFlag(style);
4341 }
4342
4343 // Find the wxWindow at the current mouse position, also returning the mouse
4344 // position.
4345 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4346 {
4347 pt = wxGetMousePosition();
4348 wxWindow* found = wxFindWindowAtPoint(pt);
4349 return found;
4350 }
4351
4352 // Get the current mouse position.
4353 wxPoint wxGetMousePosition()
4354 {
4355 /* This crashes when used within wxHelpContext,
4356 so we have to use the X-specific implementation below.
4357 gint x, y;
4358 GdkModifierType *mask;
4359 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4360
4361 return wxPoint(x, y);
4362 */
4363
4364 int x, y;
4365 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
4366
4367 Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY();
4368 Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
4369 Window rootReturn, childReturn;
4370 int rootX, rootY, winX, winY;
4371 unsigned int maskReturn;
4372
4373 XQueryPointer (display,
4374 rootWindow,
4375 &rootReturn,
4376 &childReturn,
4377 &rootX, &rootY, &winX, &winY, &maskReturn);
4378 return wxPoint(rootX, rootY);
4379
4380 }
4381
4382 // Needed for implementing e.g. combobox on wxGTK within a modal dialog.
4383 void wxAddGrab(wxWindow* window)
4384 {
4385 gtk_grab_add( (GtkWidget*) window->GetHandle() );
4386 }
4387
4388 void wxRemoveGrab(wxWindow* window)
4389 {
4390 gtk_grab_remove( (GtkWidget*) window->GetHandle() );
4391 }
4392
4393 // ----------------------------------------------------------------------------
4394 // wxWinModule
4395 // ----------------------------------------------------------------------------
4396
4397 class wxWinModule : public wxModule
4398 {
4399 public:
4400 bool OnInit();
4401 void OnExit();
4402
4403 private:
4404 DECLARE_DYNAMIC_CLASS(wxWinModule)
4405 };
4406
4407 IMPLEMENT_DYNAMIC_CLASS(wxWinModule, wxModule)
4408
4409 bool wxWinModule::OnInit()
4410 {
4411 // g_eraseGC = gdk_gc_new( gdk_get_default_root_window() );
4412 // gdk_gc_set_fill( g_eraseGC, GDK_SOLID );
4413
4414 return true;
4415 }
4416
4417 void wxWinModule::OnExit()
4418 {
4419 if (g_eraseGC)
4420 g_object_unref (G_OBJECT (g_eraseGC));
4421 }