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