]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/window.cpp
Alter focus handling to disable GTK+ standard
[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 gint 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 bool 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 g_signal_stop_emission_by_name (widget, "focus_in_event");
1887
1888 return ret;
1889 }
1890 }
1891
1892 //-----------------------------------------------------------------------------
1893 // "focus_out_event"
1894 //-----------------------------------------------------------------------------
1895
1896 extern "C" {
1897 static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk_event, wxWindowGTK *win )
1898 {
1899 DEBUG_MAIN_THREAD
1900
1901 if (g_isIdle)
1902 wxapp_install_idle_handler();
1903
1904 if (win->m_imData)
1905 gtk_im_context_focus_out(win->m_imData->context);
1906
1907 wxLogTrace( TRACE_FOCUS,
1908 _T("%s: focus out"), win->GetName().c_str() );
1909
1910
1911 wxWindowGTK *winFocus = wxFindFocusedChild(win);
1912 if ( winFocus )
1913 win = winFocus;
1914
1915 g_focusWindow = (wxWindowGTK *)NULL;
1916
1917 #ifdef HAVE_XIM
1918 if (win->m_ic)
1919 gdk_im_end();
1920 #endif
1921
1922 #if wxUSE_CARET
1923 // caret needs to be informed about focus change
1924 wxCaret *caret = win->GetCaret();
1925 if ( caret )
1926 {
1927 caret->OnKillFocus();
1928 }
1929 #endif // wxUSE_CARET
1930
1931 bool ret = FALSE;
1932
1933 // don't send the window a kill focus event if it thinks that it doesn't
1934 // have focus already
1935 if ( win->m_hasFocus )
1936 {
1937 win->m_hasFocus = false;
1938
1939 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
1940 event.SetEventObject( win );
1941
1942 (void)win->GetEventHandler()->ProcessEvent( event );
1943
1944 ret = TRUE;
1945 }
1946
1947 // Disable default focus handling for custom windows
1948 // since the default GTK+ handler issues a repaint
1949 if (win->m_wxwindow)
1950 g_signal_stop_emission_by_name (widget, "focus_out_event");
1951
1952 return ret;
1953 }
1954 }
1955
1956 //-----------------------------------------------------------------------------
1957 // "enter_notify_event"
1958 //-----------------------------------------------------------------------------
1959
1960 extern "C" {
1961 static
1962 gint gtk_window_enter_callback( GtkWidget *widget,
1963 GdkEventCrossing *gdk_event,
1964 wxWindowGTK *win )
1965 {
1966 DEBUG_MAIN_THREAD
1967
1968 if (g_isIdle)
1969 wxapp_install_idle_handler();
1970
1971 if (!win->m_hasVMT) return FALSE;
1972 if (g_blockEventsOnDrag) return FALSE;
1973
1974 // Event was emitted after a grab
1975 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
1976
1977 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1978
1979 int x = 0;
1980 int y = 0;
1981 GdkModifierType state = (GdkModifierType)0;
1982
1983 gdk_window_get_pointer( widget->window, &x, &y, &state );
1984
1985 wxMouseEvent event( wxEVT_ENTER_WINDOW );
1986 InitMouseEvent(win, event, gdk_event);
1987 wxPoint pt = win->GetClientAreaOrigin();
1988 event.m_x = x + pt.x;
1989 event.m_y = y + pt.y;
1990
1991 if (win->GetEventHandler()->ProcessEvent( event ))
1992 {
1993 g_signal_stop_emission_by_name (widget, "enter_notify_event");
1994 return TRUE;
1995 }
1996
1997 return FALSE;
1998 }
1999 }
2000
2001 //-----------------------------------------------------------------------------
2002 // "leave_notify_event"
2003 //-----------------------------------------------------------------------------
2004
2005 extern "C" {
2006 static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindowGTK *win )
2007 {
2008 DEBUG_MAIN_THREAD
2009
2010 if (g_isIdle)
2011 wxapp_install_idle_handler();
2012
2013 if (!win->m_hasVMT) return FALSE;
2014 if (g_blockEventsOnDrag) return FALSE;
2015
2016 // Event was emitted after an ungrab
2017 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
2018
2019 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
2020
2021 wxMouseEvent event( wxEVT_LEAVE_WINDOW );
2022 event.SetTimestamp( gdk_event->time );
2023 event.SetEventObject( win );
2024
2025 int x = 0;
2026 int y = 0;
2027 GdkModifierType state = (GdkModifierType)0;
2028
2029 gdk_window_get_pointer( widget->window, &x, &y, &state );
2030
2031 event.m_shiftDown = (state & GDK_SHIFT_MASK) != 0;
2032 event.m_controlDown = (state & GDK_CONTROL_MASK) != 0;
2033 event.m_altDown = (state & GDK_MOD1_MASK) != 0;
2034 event.m_metaDown = (state & GDK_MOD2_MASK) != 0;
2035 event.m_leftDown = (state & GDK_BUTTON1_MASK) != 0;
2036 event.m_middleDown = (state & GDK_BUTTON2_MASK) != 0;
2037 event.m_rightDown = (state & GDK_BUTTON3_MASK) != 0;
2038
2039 wxPoint pt = win->GetClientAreaOrigin();
2040 event.m_x = x + pt.x;
2041 event.m_y = y + pt.y;
2042
2043 if (win->GetEventHandler()->ProcessEvent( event ))
2044 {
2045 g_signal_stop_emission_by_name (widget, "leave_notify_event");
2046 return TRUE;
2047 }
2048
2049 return FALSE;
2050 }
2051 }
2052
2053 //-----------------------------------------------------------------------------
2054 // "value_changed" from m_vAdjust
2055 //-----------------------------------------------------------------------------
2056
2057 extern "C" {
2058 static void gtk_window_vscroll_callback( GtkAdjustment *adjust,
2059 wxWindowGTK *win )
2060 {
2061 DEBUG_MAIN_THREAD
2062
2063 if (g_isIdle)
2064 wxapp_install_idle_handler();
2065
2066 if (g_blockEventsOnDrag) return;
2067
2068 if (!win->m_hasVMT) return;
2069
2070 float diff = adjust->value - win->m_oldVerticalPos;
2071 if (fabs(diff) < 0.2) return;
2072
2073 win->m_oldVerticalPos = adjust->value;
2074
2075 wxEventType command = GtkScrollWinTypeToWx(GTK_SCROLL_JUMP);
2076
2077 int value = (int)(adjust->value+0.5);
2078
2079 wxScrollWinEvent event( command, value, wxVERTICAL );
2080 event.SetEventObject( win );
2081 win->GetEventHandler()->ProcessEvent( event );
2082 }
2083 }
2084
2085 //-----------------------------------------------------------------------------
2086 // "value_changed" from m_hAdjust
2087 //-----------------------------------------------------------------------------
2088
2089 extern "C" {
2090 static void gtk_window_hscroll_callback( GtkAdjustment *adjust,
2091 wxWindowGTK *win )
2092 {
2093 DEBUG_MAIN_THREAD
2094
2095 if (g_isIdle)
2096 wxapp_install_idle_handler();
2097
2098 if (g_blockEventsOnDrag) return;
2099 if (!win->m_hasVMT) return;
2100
2101 float diff = adjust->value - win->m_oldHorizontalPos;
2102 if (fabs(diff) < 0.2) return;
2103
2104 wxEventType command = GtkScrollWinTypeToWx(GTK_SCROLL_JUMP);
2105
2106 win->m_oldHorizontalPos = adjust->value;
2107
2108 int value = (int)(adjust->value+0.5);
2109
2110 wxScrollWinEvent event( command, value, wxHORIZONTAL );
2111 event.SetEventObject( win );
2112 win->GetEventHandler()->ProcessEvent( event );
2113 }
2114 }
2115
2116 //-----------------------------------------------------------------------------
2117 // "button_press_event" from scrollbar
2118 //-----------------------------------------------------------------------------
2119
2120 extern "C" {
2121 static gint gtk_scrollbar_button_press_callback( GtkRange *widget,
2122 GdkEventButton *gdk_event,
2123 wxWindowGTK *win)
2124 {
2125 DEBUG_MAIN_THREAD
2126
2127 if (g_isIdle)
2128 wxapp_install_idle_handler();
2129
2130
2131 g_blockEventsOnScroll = true;
2132
2133 // FIXME: there is no 'slider' field in GTK+ 2.0 any more
2134 #if 0
2135 win->m_isScrolling = (gdk_event->window == widget->slider);
2136 #endif
2137
2138 return FALSE;
2139 }
2140 }
2141
2142 //-----------------------------------------------------------------------------
2143 // "button_release_event" from scrollbar
2144 //-----------------------------------------------------------------------------
2145
2146 extern "C" {
2147 static gint gtk_scrollbar_button_release_callback( GtkRange *widget,
2148 GdkEventButton *WXUNUSED(gdk_event),
2149 wxWindowGTK *win)
2150 {
2151 DEBUG_MAIN_THREAD
2152
2153 // don't test here as we can release the mouse while being over
2154 // a different window than the slider
2155 //
2156 // if (gdk_event->window != widget->slider) return FALSE;
2157
2158 g_blockEventsOnScroll = false;
2159
2160 if (win->m_isScrolling)
2161 {
2162 wxEventType command = wxEVT_SCROLLWIN_THUMBRELEASE;
2163 int value = -1;
2164 int dir = -1;
2165
2166 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(win->m_widget);
2167 if (widget == GTK_RANGE(scrolledWindow->hscrollbar))
2168 {
2169 value = (int)(win->m_hAdjust->value+0.5);
2170 dir = wxHORIZONTAL;
2171 }
2172 if (widget == GTK_RANGE(scrolledWindow->vscrollbar))
2173 {
2174 value = (int)(win->m_vAdjust->value+0.5);
2175 dir = wxVERTICAL;
2176 }
2177
2178 wxScrollWinEvent event( command, value, dir );
2179 event.SetEventObject( win );
2180 win->GetEventHandler()->ProcessEvent( event );
2181 }
2182
2183 win->m_isScrolling = false;
2184
2185 return FALSE;
2186 }
2187 }
2188
2189 // ----------------------------------------------------------------------------
2190 // this wxWindowBase function is implemented here (in platform-specific file)
2191 // because it is static and so couldn't be made virtual
2192 // ----------------------------------------------------------------------------
2193
2194 wxWindow *wxWindowBase::DoFindFocus()
2195 {
2196 // the cast is necessary when we compile in wxUniversal mode
2197 return (wxWindow *)g_focusWindow;
2198 }
2199
2200 //-----------------------------------------------------------------------------
2201 // "realize" from m_widget
2202 //-----------------------------------------------------------------------------
2203
2204 /* We cannot set colours and fonts before the widget has
2205 been realized, so we do this directly after realization. */
2206
2207 extern "C" {
2208 static gint
2209 gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win )
2210 {
2211 DEBUG_MAIN_THREAD
2212
2213 if (g_isIdle)
2214 wxapp_install_idle_handler();
2215
2216 if (win->m_imData)
2217 {
2218 GtkPizza *pizza = GTK_PIZZA( m_widget );
2219 gtk_im_context_set_client_window( win->m_imData->context,
2220 pizza->bin_window );
2221 }
2222
2223 wxWindowCreateEvent event( win );
2224 event.SetEventObject( win );
2225 win->GetEventHandler()->ProcessEvent( event );
2226
2227 return FALSE;
2228 }
2229 }
2230
2231 //-----------------------------------------------------------------------------
2232 // "size_allocate"
2233 //-----------------------------------------------------------------------------
2234
2235 extern "C" {
2236 static
2237 void gtk_window_size_callback( GtkWidget *WXUNUSED(widget),
2238 GtkAllocation *WXUNUSED(alloc),
2239 wxWindow *win )
2240 {
2241 if (g_isIdle)
2242 wxapp_install_idle_handler();
2243
2244 if (!win->m_hasScrolling) return;
2245
2246 int client_width = 0;
2247 int client_height = 0;
2248 win->GetClientSize( &client_width, &client_height );
2249 if ((client_width == win->m_oldClientWidth) && (client_height == win->m_oldClientHeight))
2250 return;
2251
2252 win->m_oldClientWidth = client_width;
2253 win->m_oldClientHeight = client_height;
2254
2255 if (!win->m_nativeSizeEvent)
2256 {
2257 wxSizeEvent event( win->GetSize(), win->GetId() );
2258 event.SetEventObject( win );
2259 win->GetEventHandler()->ProcessEvent( event );
2260 }
2261 }
2262 }
2263
2264
2265 #ifdef HAVE_XIM
2266 #define WXUNUSED_UNLESS_XIM(param) param
2267 #else
2268 #define WXUNUSED_UNLESS_XIM(param) WXUNUSED(param)
2269 #endif
2270
2271 /* Resize XIM window */
2272
2273 extern "C" {
2274 static
2275 void gtk_wxwindow_size_callback( GtkWidget* WXUNUSED_UNLESS_XIM(widget),
2276 GtkAllocation* WXUNUSED_UNLESS_XIM(alloc),
2277 wxWindowGTK* WXUNUSED_UNLESS_XIM(win) )
2278 {
2279 if (g_isIdle)
2280 wxapp_install_idle_handler();
2281
2282 #ifdef HAVE_XIM
2283 if (!win->m_ic)
2284 return;
2285
2286 if (gdk_ic_get_style (win->m_ic) & GDK_IM_PREEDIT_POSITION)
2287 {
2288 gint width, height;
2289
2290 gdk_window_get_size (widget->window, &width, &height);
2291 win->m_icattr->preedit_area.width = width;
2292 win->m_icattr->preedit_area.height = height;
2293 gdk_ic_set_attr (win->m_ic, win->m_icattr, GDK_IC_PREEDIT_AREA);
2294 }
2295 #endif // HAVE_XIM
2296 }
2297 }
2298
2299 //-----------------------------------------------------------------------------
2300 // "realize" from m_wxwindow
2301 //-----------------------------------------------------------------------------
2302
2303 /* Initialize XIM support */
2304
2305 extern "C" {
2306 static gint
2307 gtk_wxwindow_realized_callback( GtkWidget * WXUNUSED_UNLESS_XIM(widget),
2308 wxWindowGTK * WXUNUSED_UNLESS_XIM(win) )
2309 {
2310 if (g_isIdle)
2311 wxapp_install_idle_handler();
2312
2313 #ifdef HAVE_XIM
2314 if (win->m_ic) return FALSE;
2315 if (!widget) return FALSE;
2316 if (!gdk_im_ready()) return FALSE;
2317
2318 win->m_icattr = gdk_ic_attr_new();
2319 if (!win->m_icattr) return FALSE;
2320
2321 gint width, height;
2322 GdkEventMask mask;
2323 GdkColormap *colormap;
2324 GdkICAttr *attr = win->m_icattr;
2325 unsigned attrmask = GDK_IC_ALL_REQ;
2326 GdkIMStyle style;
2327 GdkIMStyle supported_style = (GdkIMStyle)
2328 (GDK_IM_PREEDIT_NONE |
2329 GDK_IM_PREEDIT_NOTHING |
2330 GDK_IM_PREEDIT_POSITION |
2331 GDK_IM_STATUS_NONE |
2332 GDK_IM_STATUS_NOTHING);
2333
2334 if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
2335 supported_style = (GdkIMStyle)(supported_style & ~GDK_IM_PREEDIT_POSITION);
2336
2337 attr->style = style = gdk_im_decide_style (supported_style);
2338 attr->client_window = widget->window;
2339
2340 if ((colormap = gtk_widget_get_colormap (widget)) !=
2341 gtk_widget_get_default_colormap ())
2342 {
2343 attrmask |= GDK_IC_PREEDIT_COLORMAP;
2344 attr->preedit_colormap = colormap;
2345 }
2346
2347 attrmask |= GDK_IC_PREEDIT_FOREGROUND;
2348 attrmask |= GDK_IC_PREEDIT_BACKGROUND;
2349 attr->preedit_foreground = widget->style->fg[GTK_STATE_NORMAL];
2350 attr->preedit_background = widget->style->base[GTK_STATE_NORMAL];
2351
2352 switch (style & GDK_IM_PREEDIT_MASK)
2353 {
2354 case GDK_IM_PREEDIT_POSITION:
2355 if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
2356 {
2357 g_warning ("over-the-spot style requires fontset");
2358 break;
2359 }
2360
2361 gdk_window_get_size (widget->window, &width, &height);
2362
2363 attrmask |= GDK_IC_PREEDIT_POSITION_REQ;
2364 attr->spot_location.x = 0;
2365 attr->spot_location.y = height;
2366 attr->preedit_area.x = 0;
2367 attr->preedit_area.y = 0;
2368 attr->preedit_area.width = width;
2369 attr->preedit_area.height = height;
2370 attr->preedit_fontset = widget->style->font;
2371
2372 break;
2373 }
2374
2375 win->m_ic = gdk_ic_new (attr, (GdkICAttributesType)attrmask);
2376
2377 if (win->m_ic == NULL)
2378 g_warning ("Can't create input context.");
2379 else
2380 {
2381 mask = gdk_window_get_events (widget->window);
2382 mask = (GdkEventMask)(mask | gdk_ic_get_events (win->m_ic));
2383 gdk_window_set_events (widget->window, mask);
2384
2385 if (GTK_WIDGET_HAS_FOCUS(widget))
2386 gdk_im_begin (win->m_ic, widget->window);
2387 }
2388 #endif // HAVE_XIM
2389
2390 return FALSE;
2391 }
2392 }
2393
2394 //-----------------------------------------------------------------------------
2395 // InsertChild for wxWindowGTK.
2396 //-----------------------------------------------------------------------------
2397
2398 /* Callback for wxWindowGTK. This very strange beast has to be used because
2399 * C++ has no virtual methods in a constructor. We have to emulate a
2400 * virtual function here as wxNotebook requires a different way to insert
2401 * a child in it. I had opted for creating a wxNotebookPage window class
2402 * which would have made this superfluous (such in the MDI window system),
2403 * but no-one was listening to me... */
2404
2405 static void wxInsertChildInWindow( wxWindowGTK* parent, wxWindowGTK* child )
2406 {
2407 /* the window might have been scrolled already, do we
2408 have to adapt the position */
2409 GtkPizza *pizza = GTK_PIZZA(parent->m_wxwindow);
2410 child->m_x += pizza->xoffset;
2411 child->m_y += pizza->yoffset;
2412
2413 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
2414 GTK_WIDGET(child->m_widget),
2415 child->m_x,
2416 child->m_y,
2417 child->m_width,
2418 child->m_height );
2419 }
2420
2421 //-----------------------------------------------------------------------------
2422 // global functions
2423 //-----------------------------------------------------------------------------
2424
2425 wxWindow *wxGetActiveWindow()
2426 {
2427 return wxWindow::FindFocus();
2428 }
2429
2430
2431 wxMouseState wxGetMouseState()
2432 {
2433 wxMouseState ms;
2434
2435 gint x;
2436 gint y;
2437 GdkModifierType mask;
2438
2439 gdk_window_get_pointer(NULL, &x, &y, &mask);
2440
2441 ms.SetX(x);
2442 ms.SetY(y);
2443 ms.SetLeftDown(mask & GDK_BUTTON1_MASK);
2444 ms.SetMiddleDown(mask & GDK_BUTTON2_MASK);
2445 ms.SetRightDown(mask & GDK_BUTTON3_MASK);
2446
2447 ms.SetControlDown(mask & GDK_CONTROL_MASK);
2448 ms.SetShiftDown(mask & GDK_SHIFT_MASK);
2449 ms.SetAltDown(mask & GDK_MOD1_MASK);
2450 ms.SetMetaDown(mask & GDK_MOD2_MASK);
2451
2452 return ms;
2453 }
2454
2455 //-----------------------------------------------------------------------------
2456 // wxWindowGTK
2457 //-----------------------------------------------------------------------------
2458
2459 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2460 // method
2461 #ifdef __WXUNIVERSAL__
2462 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK, wxWindowBase)
2463 #else // __WXGTK__
2464 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
2465 #endif // __WXUNIVERSAL__/__WXGTK__
2466
2467 void wxWindowGTK::Init()
2468 {
2469 // GTK specific
2470 m_widget = (GtkWidget *) NULL;
2471 m_wxwindow = (GtkWidget *) NULL;
2472 m_focusWidget = (GtkWidget *) NULL;
2473
2474 // position/size
2475 m_x = 0;
2476 m_y = 0;
2477 m_width = 0;
2478 m_height = 0;
2479
2480 m_sizeSet = false;
2481 m_hasVMT = false;
2482 m_needParent = true;
2483 m_isBeingDeleted = false;
2484
2485 m_noExpose = false;
2486 m_nativeSizeEvent = false;
2487
2488 m_hasScrolling = false;
2489 m_isScrolling = false;
2490
2491 m_hAdjust = (GtkAdjustment*) NULL;
2492 m_vAdjust = (GtkAdjustment*) NULL;
2493 m_oldHorizontalPos =
2494 m_oldVerticalPos = 0.0;
2495 m_oldClientWidth =
2496 m_oldClientHeight = 0;
2497
2498 m_resizing = false;
2499
2500 m_insertCallback = (wxInsertChildFunction) NULL;
2501
2502 m_acceptsFocus = false;
2503 m_hasFocus = false;
2504
2505 m_clipPaintRegion = false;
2506
2507 m_needsStyleChange = false;
2508
2509 m_cursor = *wxSTANDARD_CURSOR;
2510
2511 m_imData = NULL;
2512 m_dirtyTabOrder = false;
2513 }
2514
2515 wxWindowGTK::wxWindowGTK()
2516 {
2517 Init();
2518 }
2519
2520 wxWindowGTK::wxWindowGTK( wxWindow *parent,
2521 wxWindowID id,
2522 const wxPoint &pos,
2523 const wxSize &size,
2524 long style,
2525 const wxString &name )
2526 {
2527 Init();
2528
2529 Create( parent, id, pos, size, style, name );
2530 }
2531
2532 bool wxWindowGTK::Create( wxWindow *parent,
2533 wxWindowID id,
2534 const wxPoint &pos,
2535 const wxSize &size,
2536 long style,
2537 const wxString &name )
2538 {
2539 if (!PreCreation( parent, pos, size ) ||
2540 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
2541 {
2542 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2543 return false;
2544 }
2545
2546 m_insertCallback = wxInsertChildInWindow;
2547
2548 m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
2549 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
2550
2551 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
2552
2553 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
2554 scroll_class->scrollbar_spacing = 0;
2555
2556 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
2557
2558 m_hAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->hscrollbar) );
2559 m_vAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->vscrollbar) );
2560
2561 m_wxwindow = gtk_pizza_new();
2562
2563 #ifndef __WXUNIVERSAL__
2564 GtkPizza *pizza = GTK_PIZZA(m_wxwindow);
2565
2566 if (HasFlag(wxRAISED_BORDER))
2567 {
2568 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_OUT );
2569 }
2570 else if (HasFlag(wxSUNKEN_BORDER))
2571 {
2572 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_IN );
2573 }
2574 else if (HasFlag(wxSIMPLE_BORDER))
2575 {
2576 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_THIN );
2577 }
2578 else
2579 {
2580 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_NONE );
2581 }
2582 #endif // __WXUNIVERSAL__
2583
2584 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
2585
2586 GTK_WIDGET_SET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
2587 m_acceptsFocus = true;
2588
2589 // I _really_ don't want scrollbars in the beginning
2590 m_vAdjust->lower = 0.0;
2591 m_vAdjust->upper = 1.0;
2592 m_vAdjust->value = 0.0;
2593 m_vAdjust->step_increment = 1.0;
2594 m_vAdjust->page_increment = 1.0;
2595 m_vAdjust->page_size = 5.0;
2596 g_signal_emit_by_name (m_vAdjust, "changed");
2597 m_hAdjust->lower = 0.0;
2598 m_hAdjust->upper = 1.0;
2599 m_hAdjust->value = 0.0;
2600 m_hAdjust->step_increment = 1.0;
2601 m_hAdjust->page_increment = 1.0;
2602 m_hAdjust->page_size = 5.0;
2603 g_signal_emit_by_name (m_hAdjust, "changed");
2604
2605 // these handlers block mouse events to any window during scrolling such as
2606 // motion events and prevent GTK and wxWidgets from fighting over where the
2607 // slider should be
2608 g_signal_connect (scrolledWindow->vscrollbar, "button_press_event",
2609 G_CALLBACK (gtk_scrollbar_button_press_callback), this);
2610 g_signal_connect (scrolledWindow->hscrollbar, "button_press_event",
2611 G_CALLBACK (gtk_scrollbar_button_press_callback), this);
2612 g_signal_connect (scrolledWindow->vscrollbar, "button_release_event",
2613 G_CALLBACK (gtk_scrollbar_button_release_callback), this);
2614 g_signal_connect (scrolledWindow->hscrollbar, "button_release_event",
2615 G_CALLBACK (gtk_scrollbar_button_release_callback), this);
2616
2617 // these handlers get notified when screen updates are required either when
2618 // scrolling or when the window size (and therefore scrollbar configuration)
2619 // has changed
2620
2621 g_signal_connect (m_hAdjust, "value_changed",
2622 G_CALLBACK (gtk_window_hscroll_callback), this);
2623 g_signal_connect (m_vAdjust, "value_changed",
2624 G_CALLBACK (gtk_window_vscroll_callback), this);
2625
2626 gtk_widget_show( m_wxwindow );
2627
2628 if (m_parent)
2629 m_parent->DoAddChild( this );
2630
2631 m_focusWidget = m_wxwindow;
2632
2633 PostCreation();
2634
2635 return true;
2636 }
2637
2638 wxWindowGTK::~wxWindowGTK()
2639 {
2640 SendDestroyEvent();
2641
2642 if (g_focusWindow == this)
2643 g_focusWindow = NULL;
2644
2645 if ( g_delayedFocus == this )
2646 g_delayedFocus = NULL;
2647
2648 m_isBeingDeleted = true;
2649 m_hasVMT = false;
2650
2651 // destroy children before destroying this window itself
2652 DestroyChildren();
2653
2654 // unhook focus handlers to prevent stray events being
2655 // propagated to this (soon to be) dead object
2656 if (m_focusWidget != NULL)
2657 {
2658 g_signal_handlers_disconnect_by_func (m_focusWidget,
2659 (gpointer) gtk_window_focus_in_callback,
2660 this);
2661 g_signal_handlers_disconnect_by_func (m_focusWidget,
2662 (gpointer) gtk_window_focus_out_callback,
2663 this);
2664 }
2665
2666 if (m_widget)
2667 Show( false );
2668
2669 #ifdef HAVE_XIM
2670 if (m_ic)
2671 gdk_ic_destroy (m_ic);
2672 if (m_icattr)
2673 gdk_ic_attr_destroy (m_icattr);
2674 #endif
2675
2676 // delete before the widgets to avoid a crash on solaris
2677 delete m_imData;
2678
2679 if (m_wxwindow)
2680 {
2681 gtk_widget_destroy( m_wxwindow );
2682 m_wxwindow = (GtkWidget*) NULL;
2683 }
2684
2685 if (m_widget)
2686 {
2687 gtk_widget_destroy( m_widget );
2688 m_widget = (GtkWidget*) NULL;
2689 }
2690 }
2691
2692 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
2693 {
2694 wxCHECK_MSG( !m_needParent || parent, false, wxT("Need complete parent.") );
2695
2696 // Use either the given size, or the default if -1 is given.
2697 // See wxWindowBase for these functions.
2698 m_width = WidthDefault(size.x) ;
2699 m_height = HeightDefault(size.y);
2700
2701 m_x = (int)pos.x;
2702 m_y = (int)pos.y;
2703
2704 return true;
2705 }
2706
2707 void wxWindowGTK::PostCreation()
2708 {
2709 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2710
2711 if (m_wxwindow)
2712 {
2713 if (!m_noExpose)
2714 {
2715 // these get reported to wxWidgets -> wxPaintEvent
2716
2717 gtk_pizza_set_external( GTK_PIZZA(m_wxwindow), TRUE );
2718
2719 g_signal_connect (m_wxwindow, "expose_event",
2720 G_CALLBACK (gtk_window_expose_callback), this);
2721
2722 gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), HasFlag( wxFULL_REPAINT_ON_RESIZE ) );
2723 }
2724
2725 // Create input method handler
2726 m_imData = new wxGtkIMData;
2727
2728 // Cannot handle drawing preedited text yet
2729 gtk_im_context_set_use_preedit( m_imData->context, FALSE );
2730
2731 g_signal_connect (m_imData->context, "commit",
2732 G_CALLBACK (gtk_wxwindow_commit_cb), this);
2733
2734 // these are called when the "sunken" or "raised" borders are drawn
2735 g_signal_connect (m_widget, "expose_event",
2736 G_CALLBACK (gtk_window_own_expose_callback), this);
2737 }
2738
2739 // focus handling
2740
2741 if (!GTK_IS_WINDOW(m_widget))
2742 {
2743 if (m_focusWidget == NULL)
2744 m_focusWidget = m_widget;
2745
2746 g_signal_connect (m_focusWidget, "focus_in_event",
2747 G_CALLBACK (gtk_window_focus_in_callback), this);
2748 g_signal_connect_after (m_focusWidget, "focus_out_event",
2749 G_CALLBACK (gtk_window_focus_out_callback), this);
2750 }
2751
2752 // connect to the various key and mouse handlers
2753
2754 GtkWidget *connect_widget = GetConnectWidget();
2755
2756 ConnectWidget( connect_widget );
2757
2758 /* We cannot set colours, fonts and cursors before the widget has
2759 been realized, so we do this directly after realization */
2760 g_signal_connect (connect_widget, "realize",
2761 G_CALLBACK (gtk_window_realized_callback), this);
2762
2763 if (m_wxwindow)
2764 {
2765 // Catch native resize events
2766 g_signal_connect (m_wxwindow, "size_allocate",
2767 G_CALLBACK (gtk_window_size_callback), this);
2768
2769 // Initialize XIM support
2770 g_signal_connect (m_wxwindow, "realize",
2771 G_CALLBACK (gtk_wxwindow_realized_callback), this);
2772
2773 // And resize XIM window
2774 g_signal_connect (m_wxwindow, "size_allocate",
2775 G_CALLBACK (gtk_wxwindow_size_callback), this);
2776 }
2777
2778 if (GTK_IS_COMBO(m_widget))
2779 {
2780 GtkCombo *gcombo = GTK_COMBO(m_widget);
2781
2782 g_signal_connect (gcombo->entry, "size_request",
2783 G_CALLBACK (wxgtk_combo_size_request_callback),
2784 this);
2785 }
2786 else
2787 {
2788 // This is needed if we want to add our windows into native
2789 // GTK controls, such as the toolbar. With this callback, the
2790 // toolbar gets to know the correct size (the one set by the
2791 // programmer). Sadly, it misbehaves for wxComboBox.
2792 g_signal_connect (m_widget, "size_request",
2793 G_CALLBACK (wxgtk_window_size_request_callback),
2794 this);
2795 }
2796
2797 InheritAttributes();
2798
2799 m_hasVMT = true;
2800
2801 // unless the window was created initially hidden (i.e. Hide() had been
2802 // called before Create()), we should show it at GTK+ level as well
2803 if ( IsShown() )
2804 gtk_widget_show( m_widget );
2805 }
2806
2807 void wxWindowGTK::ConnectWidget( GtkWidget *widget )
2808 {
2809 g_signal_connect (widget, "key_press_event",
2810 G_CALLBACK (gtk_window_key_press_callback), this);
2811 g_signal_connect (widget, "key_release_event",
2812 G_CALLBACK (gtk_window_key_release_callback), this);
2813 g_signal_connect (widget, "button_press_event",
2814 G_CALLBACK (gtk_window_button_press_callback), this);
2815 g_signal_connect (widget, "button_release_event",
2816 G_CALLBACK (gtk_window_button_release_callback), this);
2817 g_signal_connect (widget, "motion_notify_event",
2818 G_CALLBACK (gtk_window_motion_notify_callback), this);
2819 g_signal_connect (widget, "scroll_event",
2820 G_CALLBACK (gtk_window_wheel_callback), this);
2821 g_signal_connect (widget, "popup_menu",
2822 G_CALLBACK (wxgtk_window_popup_menu_callback), this);
2823 g_signal_connect (widget, "enter_notify_event",
2824 G_CALLBACK (gtk_window_enter_callback), this);
2825 g_signal_connect (widget, "leave_notify_event",
2826 G_CALLBACK (gtk_window_leave_callback), this);
2827 }
2828
2829 bool wxWindowGTK::Destroy()
2830 {
2831 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2832
2833 m_hasVMT = false;
2834
2835 return wxWindowBase::Destroy();
2836 }
2837
2838 void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
2839 {
2840 gtk_pizza_set_size( GTK_PIZZA(m_parent->m_wxwindow), m_widget, x, y, width, height );
2841 }
2842
2843 void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
2844 {
2845 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2846 wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") );
2847
2848 /*
2849 printf( "DoSetSize: name %s, x,y,w,h: %d,%d,%d,%d \n", GetName().c_str(), x,y,width,height );
2850 */
2851
2852 if (m_resizing) return; /* I don't like recursions */
2853 m_resizing = true;
2854
2855 int currentX, currentY;
2856 GetPosition(&currentX, &currentY);
2857 if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2858 x = currentX;
2859 if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2860 y = currentY;
2861 AdjustForParentClientOrigin(x, y, sizeFlags);
2862
2863 if (m_parent->m_wxwindow == NULL) /* i.e. wxNotebook */
2864 {
2865 /* don't set the size for children of wxNotebook, just take the values. */
2866 m_x = x;
2867 m_y = y;
2868 m_width = width;
2869 m_height = height;
2870 }
2871 else
2872 {
2873 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
2874 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
2875 {
2876 if (x != -1) m_x = x + pizza->xoffset;
2877 if (y != -1) m_y = y + pizza->yoffset;
2878 }
2879 else
2880 {
2881 m_x = x + pizza->xoffset;
2882 m_y = y + pizza->yoffset;
2883 }
2884
2885 // calculate the best size if we should auto size the window
2886 if ( ((sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1) ||
2887 ((sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1) )
2888 {
2889 const wxSize sizeBest = GetBestSize();
2890 if ( (sizeFlags & wxSIZE_AUTO_WIDTH) && width == -1 )
2891 width = sizeBest.x;
2892 if ( (sizeFlags & wxSIZE_AUTO_HEIGHT) && height == -1 )
2893 height = sizeBest.y;
2894 }
2895
2896 if (width != -1)
2897 m_width = width;
2898 if (height != -1)
2899 m_height = height;
2900
2901 int minWidth = GetMinWidth(),
2902 minHeight = GetMinHeight(),
2903 maxWidth = GetMaxWidth(),
2904 maxHeight = GetMaxHeight();
2905
2906 if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth;
2907 if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight;
2908 if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth;
2909 if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
2910
2911 int left_border = 0;
2912 int right_border = 0;
2913 int top_border = 0;
2914 int bottom_border = 0;
2915
2916 /* the default button has a border around it */
2917 if (GTK_WIDGET_CAN_DEFAULT(m_widget))
2918 {
2919 GtkBorder *default_border = NULL;
2920 gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
2921 if (default_border)
2922 {
2923 left_border += default_border->left;
2924 right_border += default_border->right;
2925 top_border += default_border->top;
2926 bottom_border += default_border->bottom;
2927 g_free( default_border );
2928 }
2929 }
2930
2931 DoMoveWindow( m_x-top_border,
2932 m_y-left_border,
2933 m_width+left_border+right_border,
2934 m_height+top_border+bottom_border );
2935 }
2936
2937 if (m_hasScrolling)
2938 {
2939 /* Sometimes the client area changes size without the
2940 whole windows's size changing, but if the whole
2941 windows's size doesn't change, no wxSizeEvent will
2942 normally be sent. Here we add an extra test if
2943 the client test has been changed and this will
2944 be used then. */
2945 GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
2946 }
2947
2948 /*
2949 wxPrintf( "OnSize sent from " );
2950 if (GetClassInfo() && GetClassInfo()->GetClassName())
2951 wxPrintf( GetClassInfo()->GetClassName() );
2952 wxPrintf( " %d %d %d %d\n", (int)m_x, (int)m_y, (int)m_width, (int)m_height );
2953 */
2954
2955 if (!m_nativeSizeEvent)
2956 {
2957 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
2958 event.SetEventObject( this );
2959 GetEventHandler()->ProcessEvent( event );
2960 }
2961
2962 m_resizing = false;
2963 }
2964
2965 void wxWindowGTK::OnInternalIdle()
2966 {
2967 if ( m_dirtyTabOrder )
2968 {
2969 m_dirtyTabOrder = false;
2970 RealizeTabOrder();
2971 }
2972
2973 // Update style if the window was not yet realized
2974 // and SetBackgroundStyle(wxBG_STYLE_CUSTOM) was called
2975 if (m_needsStyleChange)
2976 {
2977 SetBackgroundStyle(GetBackgroundStyle());
2978 m_needsStyleChange = false;
2979 }
2980
2981 // Update invalidated regions.
2982 GtkUpdate();
2983
2984 wxCursor cursor = m_cursor;
2985 if (g_globalCursor.Ok()) cursor = g_globalCursor;
2986
2987 if (cursor.Ok())
2988 {
2989 /* I now set the cursor anew in every OnInternalIdle call
2990 as setting the cursor in a parent window also effects the
2991 windows above so that checking for the current cursor is
2992 not possible. */
2993
2994 if (m_wxwindow)
2995 {
2996 GdkWindow *window = GTK_PIZZA(m_wxwindow)->bin_window;
2997 if (window)
2998 gdk_window_set_cursor( window, cursor.GetCursor() );
2999
3000 if (!g_globalCursor.Ok())
3001 cursor = *wxSTANDARD_CURSOR;
3002
3003 window = m_widget->window;
3004 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
3005 gdk_window_set_cursor( window, cursor.GetCursor() );
3006
3007 }
3008 else
3009 {
3010
3011 GdkWindow *window = m_widget->window;
3012 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
3013 gdk_window_set_cursor( window, cursor.GetCursor() );
3014
3015 }
3016 }
3017
3018 if (wxUpdateUIEvent::CanUpdate(this))
3019 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
3020 }
3021
3022 void wxWindowGTK::DoGetSize( int *width, int *height ) const
3023 {
3024 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3025
3026 if (width) (*width) = m_width;
3027 if (height) (*height) = m_height;
3028 }
3029
3030 void wxWindowGTK::DoSetClientSize( int width, int height )
3031 {
3032 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3033
3034 if (!m_wxwindow)
3035 {
3036 SetSize( width, height );
3037 }
3038 else
3039 {
3040 int dw = 0;
3041 int dh = 0;
3042
3043 #ifndef __WXUNIVERSAL__
3044 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3045 {
3046 /* when using GTK 1.2 we set the shadow border size to 2 */
3047 dw += 2 * 2;
3048 dh += 2 * 2;
3049 }
3050 if (HasFlag(wxSIMPLE_BORDER))
3051 {
3052 /* when using GTK 1.2 we set the simple border size to 1 */
3053 dw += 1 * 2;
3054 dh += 1 * 2;
3055 }
3056 #endif // __WXUNIVERSAL__
3057
3058 if (m_hasScrolling)
3059 {
3060 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3061
3062 GtkRequisition vscroll_req;
3063 vscroll_req.width = 2;
3064 vscroll_req.height = 2;
3065 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3066 (scroll_window->vscrollbar, &vscroll_req );
3067
3068 GtkRequisition hscroll_req;
3069 hscroll_req.width = 2;
3070 hscroll_req.height = 2;
3071 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3072 (scroll_window->hscrollbar, &hscroll_req );
3073
3074 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3075
3076 if (scroll_window->vscrollbar_visible)
3077 {
3078 dw += vscroll_req.width;
3079 dw += scroll_class->scrollbar_spacing;
3080 }
3081
3082 if (scroll_window->hscrollbar_visible)
3083 {
3084 dh += hscroll_req.height;
3085 dh += scroll_class->scrollbar_spacing;
3086 }
3087 }
3088
3089 SetSize( width+dw, height+dh );
3090 }
3091 }
3092
3093 void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
3094 {
3095 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3096
3097 if (!m_wxwindow)
3098 {
3099 if (width) (*width) = m_width;
3100 if (height) (*height) = m_height;
3101 }
3102 else
3103 {
3104 int dw = 0;
3105 int dh = 0;
3106
3107 #ifndef __WXUNIVERSAL__
3108 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3109 {
3110 /* when using GTK 1.2 we set the shadow border size to 2 */
3111 dw += 2 * 2;
3112 dh += 2 * 2;
3113 }
3114 if (HasFlag(wxSIMPLE_BORDER))
3115 {
3116 /* when using GTK 1.2 we set the simple border size to 1 */
3117 dw += 1 * 2;
3118 dh += 1 * 2;
3119 }
3120 #endif // __WXUNIVERSAL__
3121
3122 if (m_hasScrolling)
3123 {
3124 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3125
3126 GtkRequisition vscroll_req;
3127 vscroll_req.width = 2;
3128 vscroll_req.height = 2;
3129 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3130 (scroll_window->vscrollbar, &vscroll_req );
3131
3132 GtkRequisition hscroll_req;
3133 hscroll_req.width = 2;
3134 hscroll_req.height = 2;
3135 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3136 (scroll_window->hscrollbar, &hscroll_req );
3137
3138 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3139
3140 if (scroll_window->vscrollbar_visible)
3141 {
3142 dw += vscroll_req.width;
3143 dw += scroll_class->scrollbar_spacing;
3144 }
3145
3146 if (scroll_window->hscrollbar_visible)
3147 {
3148 dh += hscroll_req.height;
3149 dh += scroll_class->scrollbar_spacing;
3150 }
3151 }
3152
3153 if (width) (*width) = m_width - dw;
3154 if (height) (*height) = m_height - dh;
3155 }
3156
3157 /*
3158 printf( "GetClientSize, name %s ", GetName().c_str() );
3159 if (width) printf( " width = %d", (*width) );
3160 if (height) printf( " height = %d", (*height) );
3161 printf( "\n" );
3162 */
3163 }
3164
3165 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
3166 {
3167 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3168
3169 int dx = 0;
3170 int dy = 0;
3171 if (m_parent && m_parent->m_wxwindow)
3172 {
3173 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
3174 dx = pizza->xoffset;
3175 dy = pizza->yoffset;
3176 }
3177
3178 if (x) (*x) = m_x - dx;
3179 if (y) (*y) = m_y - dy;
3180 }
3181
3182 void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
3183 {
3184 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3185
3186 if (!m_widget->window) return;
3187
3188 GdkWindow *source = (GdkWindow *) NULL;
3189 if (m_wxwindow)
3190 source = GTK_PIZZA(m_wxwindow)->bin_window;
3191 else
3192 source = m_widget->window;
3193
3194 int org_x = 0;
3195 int org_y = 0;
3196 gdk_window_get_origin( source, &org_x, &org_y );
3197
3198 if (!m_wxwindow)
3199 {
3200 if (GTK_WIDGET_NO_WINDOW (m_widget))
3201 {
3202 org_x += m_widget->allocation.x;
3203 org_y += m_widget->allocation.y;
3204 }
3205 }
3206
3207 if (x) *x += org_x;
3208 if (y) *y += org_y;
3209 }
3210
3211 void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
3212 {
3213 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3214
3215 if (!m_widget->window) return;
3216
3217 GdkWindow *source = (GdkWindow *) NULL;
3218 if (m_wxwindow)
3219 source = GTK_PIZZA(m_wxwindow)->bin_window;
3220 else
3221 source = m_widget->window;
3222
3223 int org_x = 0;
3224 int org_y = 0;
3225 gdk_window_get_origin( source, &org_x, &org_y );
3226
3227 if (!m_wxwindow)
3228 {
3229 if (GTK_WIDGET_NO_WINDOW (m_widget))
3230 {
3231 org_x += m_widget->allocation.x;
3232 org_y += m_widget->allocation.y;
3233 }
3234 }
3235
3236 if (x) *x -= org_x;
3237 if (y) *y -= org_y;
3238 }
3239
3240 bool wxWindowGTK::Show( bool show )
3241 {
3242 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3243
3244 if (!wxWindowBase::Show(show))
3245 {
3246 // nothing to do
3247 return false;
3248 }
3249
3250 if (show)
3251 gtk_widget_show( m_widget );
3252 else
3253 gtk_widget_hide( m_widget );
3254
3255 wxShowEvent eventShow(GetId(), show);
3256 eventShow.SetEventObject(this);
3257
3258 GetEventHandler()->ProcessEvent(eventShow);
3259
3260 return true;
3261 }
3262
3263 static void wxWindowNotifyEnable(wxWindowGTK* win, bool enable)
3264 {
3265 win->OnParentEnable(enable);
3266
3267 // Recurse, so that children have the opportunity to Do The Right Thing
3268 // and reset colours that have been messed up by a parent's (really ancestor's)
3269 // Enable call
3270 for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
3271 node;
3272 node = node->GetNext() )
3273 {
3274 wxWindow *child = node->GetData();
3275 if (!child->IsKindOf(CLASSINFO(wxDialog)) && !child->IsKindOf(CLASSINFO(wxFrame)))
3276 wxWindowNotifyEnable(child, enable);
3277 }
3278 }
3279
3280 bool wxWindowGTK::Enable( bool enable )
3281 {
3282 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3283
3284 if (!wxWindowBase::Enable(enable))
3285 {
3286 // nothing to do
3287 return false;
3288 }
3289
3290 gtk_widget_set_sensitive( m_widget, enable );
3291 if ( m_wxwindow )
3292 gtk_widget_set_sensitive( m_wxwindow, enable );
3293
3294 wxWindowNotifyEnable(this, enable);
3295
3296 return true;
3297 }
3298
3299 int wxWindowGTK::GetCharHeight() const
3300 {
3301 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
3302
3303 wxFont font = GetFont();
3304 wxCHECK_MSG( font.Ok(), 12, wxT("invalid font") );
3305
3306 PangoContext *context = NULL;
3307 if (m_widget)
3308 context = gtk_widget_get_pango_context( m_widget );
3309
3310 if (!context)
3311 return 0;
3312
3313 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3314 PangoLayout *layout = pango_layout_new(context);
3315 pango_layout_set_font_description(layout, desc);
3316 pango_layout_set_text(layout, "H", 1);
3317 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3318
3319 PangoRectangle rect;
3320 pango_layout_line_get_extents(line, NULL, &rect);
3321
3322 g_object_unref( G_OBJECT( layout ) );
3323
3324 return (int) PANGO_PIXELS(rect.height);
3325 }
3326
3327 int wxWindowGTK::GetCharWidth() const
3328 {
3329 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
3330
3331 wxFont font = GetFont();
3332 wxCHECK_MSG( font.Ok(), 8, wxT("invalid font") );
3333
3334 PangoContext *context = NULL;
3335 if (m_widget)
3336 context = gtk_widget_get_pango_context( m_widget );
3337
3338 if (!context)
3339 return 0;
3340
3341 PangoFontDescription *desc = font.GetNativeFontInfo()->description;
3342 PangoLayout *layout = pango_layout_new(context);
3343 pango_layout_set_font_description(layout, desc);
3344 pango_layout_set_text(layout, "g", 1);
3345 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3346
3347 PangoRectangle rect;
3348 pango_layout_line_get_extents(line, NULL, &rect);
3349
3350 g_object_unref( G_OBJECT( layout ) );
3351
3352 return (int) PANGO_PIXELS(rect.width);
3353 }
3354
3355 void wxWindowGTK::GetTextExtent( const wxString& string,
3356 int *x,
3357 int *y,
3358 int *descent,
3359 int *externalLeading,
3360 const wxFont *theFont ) const
3361 {
3362 wxFont fontToUse = theFont ? *theFont : GetFont();
3363
3364 wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") );
3365
3366 if (string.empty())
3367 {
3368 if (x) (*x) = 0;
3369 if (y) (*y) = 0;
3370 return;
3371 }
3372
3373 PangoContext *context = NULL;
3374 if (m_widget)
3375 context = gtk_widget_get_pango_context( m_widget );
3376
3377 if (!context)
3378 {
3379 if (x) (*x) = 0;
3380 if (y) (*y) = 0;
3381 return;
3382 }
3383
3384 PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
3385 PangoLayout *layout = pango_layout_new(context);
3386 pango_layout_set_font_description(layout, desc);
3387 {
3388 #if wxUSE_UNICODE
3389 const wxCharBuffer data = wxConvUTF8.cWC2MB( string );
3390 pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data ));
3391 #else
3392 const wxWCharBuffer wdata = wxConvLocal.cMB2WC( string );
3393 const wxCharBuffer data = wxConvUTF8.cWC2MB( wdata );
3394 pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data ));
3395 #endif
3396 }
3397
3398 PangoRectangle rect;
3399 pango_layout_get_extents(layout, NULL, &rect);
3400
3401 if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width);
3402 if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height);
3403 if (descent)
3404 {
3405 PangoLayoutIter *iter = pango_layout_get_iter(layout);
3406 int baseline = pango_layout_iter_get_baseline(iter);
3407 pango_layout_iter_free(iter);
3408 *descent = *y - PANGO_PIXELS(baseline);
3409 }
3410 if (externalLeading) (*externalLeading) = 0; // ??
3411
3412 g_object_unref( G_OBJECT( layout ) );
3413 }
3414
3415 void wxWindowGTK::SetFocus()
3416 {
3417 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3418 if ( m_hasFocus )
3419 {
3420 // don't do anything if we already have focus
3421 return;
3422 }
3423
3424 if (m_wxwindow)
3425 {
3426 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
3427 {
3428 gtk_widget_grab_focus (m_wxwindow);
3429 }
3430 }
3431 else if (m_widget)
3432 {
3433 if (GTK_IS_CONTAINER(m_widget))
3434 {
3435 gtk_widget_child_focus( m_widget, GTK_DIR_TAB_FORWARD );
3436 }
3437 else
3438 if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
3439 {
3440
3441 if (!GTK_WIDGET_REALIZED(m_widget))
3442 {
3443 // we can't set the focus to the widget now so we remember that
3444 // it should be focused and will do it later, during the idle
3445 // time, as soon as we can
3446 wxLogTrace(TRACE_FOCUS,
3447 _T("Delaying setting focus to %s(%s)"),
3448 GetClassInfo()->GetClassName(), GetLabel().c_str());
3449
3450 g_delayedFocus = this;
3451 }
3452 else
3453 {
3454 wxLogTrace(TRACE_FOCUS,
3455 _T("Setting focus to %s(%s)"),
3456 GetClassInfo()->GetClassName(), GetLabel().c_str());
3457
3458 gtk_widget_grab_focus (m_widget);
3459 }
3460 }
3461 else
3462 {
3463 wxLogTrace(TRACE_FOCUS,
3464 _T("Can't set focus to %s(%s)"),
3465 GetClassInfo()->GetClassName(), GetLabel().c_str());
3466 }
3467 }
3468 }
3469
3470 bool wxWindowGTK::AcceptsFocus() const
3471 {
3472 return m_acceptsFocus && wxWindowBase::AcceptsFocus();
3473 }
3474
3475 bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
3476 {
3477 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3478
3479 wxWindowGTK *oldParent = m_parent,
3480 *newParent = (wxWindowGTK *)newParentBase;
3481
3482 wxASSERT( GTK_IS_WIDGET(m_widget) );
3483
3484 if ( !wxWindowBase::Reparent(newParent) )
3485 return false;
3486
3487 wxASSERT( GTK_IS_WIDGET(m_widget) );
3488
3489 /* prevent GTK from deleting the widget arbitrarily */
3490 gtk_widget_ref( m_widget );
3491
3492 if (oldParent)
3493 {
3494 gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
3495 }
3496
3497 wxASSERT( GTK_IS_WIDGET(m_widget) );
3498
3499 if (newParent)
3500 {
3501 /* insert GTK representation */
3502 (*(newParent->m_insertCallback))(newParent, this);
3503 }
3504
3505 /* reverse: prevent GTK from deleting the widget arbitrarily */
3506 gtk_widget_unref( m_widget );
3507
3508 return true;
3509 }
3510
3511 void wxWindowGTK::DoAddChild(wxWindowGTK *child)
3512 {
3513 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
3514
3515 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
3516
3517 wxASSERT_MSG( (m_insertCallback != NULL), wxT("invalid child insertion function") );
3518
3519 /* add to list */
3520 AddChild( child );
3521
3522 /* insert GTK representation */
3523 (*m_insertCallback)(this, child);
3524 }
3525
3526 void wxWindowGTK::AddChild(wxWindowBase *child)
3527 {
3528 wxWindowBase::AddChild(child);
3529 m_dirtyTabOrder = true;
3530 if (g_isIdle)
3531 wxapp_install_idle_handler();
3532 }
3533
3534 void wxWindowGTK::RemoveChild(wxWindowBase *child)
3535 {
3536 wxWindowBase::RemoveChild(child);
3537 m_dirtyTabOrder = true;
3538 if (g_isIdle)
3539 wxapp_install_idle_handler();
3540 }
3541
3542 void wxWindowGTK::DoMoveInTabOrder(wxWindow *win, MoveKind move)
3543 {
3544 wxWindowBase::DoMoveInTabOrder(win, move);
3545 m_dirtyTabOrder = true;
3546 if (g_isIdle)
3547 wxapp_install_idle_handler();
3548 }
3549
3550 void wxWindowGTK::RealizeTabOrder()
3551 {
3552 if (m_wxwindow)
3553 {
3554 if ( !m_children.empty() )
3555 {
3556 #if wxUSE_STATTEXT
3557 // we don't only construct the correct focus chain but also use
3558 // this opportunity to update the mnemonic widgets for all labels
3559 //
3560 // it would be nice to extract this code from here and put it in
3561 // stattext.cpp to reduce dependencies but there is no really easy
3562 // way to do it unfortunately
3563 wxStaticText *lastLabel = NULL;
3564 #endif // wxUSE_STATTEXT
3565
3566 GList *chain = NULL;
3567
3568 for ( wxWindowList::const_iterator i = m_children.begin();
3569 i != m_children.end();
3570 ++i )
3571 {
3572 wxWindowGTK *win = *i;
3573 #if wxUSE_STATTEXT
3574 if ( lastLabel )
3575 {
3576 if ( win->AcceptsFocusFromKeyboard() )
3577 {
3578 GtkLabel *l = GTK_LABEL(lastLabel->m_widget);
3579 gtk_label_set_mnemonic_widget(l, win->m_widget);
3580 lastLabel = NULL;
3581 }
3582 }
3583 else // check if this one is a label
3584 {
3585 lastLabel = wxDynamicCast(win, wxStaticText);
3586 }
3587 #endif // wxUSE_STATTEXT
3588
3589 chain = g_list_prepend(chain, win->m_widget);
3590 }
3591
3592 chain = g_list_reverse(chain);
3593
3594 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow), chain);
3595 g_list_free(chain);
3596 }
3597 else // no children
3598 {
3599 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow));
3600 }
3601 }
3602 }
3603
3604 void wxWindowGTK::Raise()
3605 {
3606 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3607
3608 if (m_wxwindow && m_wxwindow->window)
3609 {
3610 gdk_window_raise( m_wxwindow->window );
3611 }
3612 else if (m_widget->window)
3613 {
3614 gdk_window_raise( m_widget->window );
3615 }
3616 }
3617
3618 void wxWindowGTK::Lower()
3619 {
3620 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3621
3622 if (m_wxwindow && m_wxwindow->window)
3623 {
3624 gdk_window_lower( m_wxwindow->window );
3625 }
3626 else if (m_widget->window)
3627 {
3628 gdk_window_lower( m_widget->window );
3629 }
3630 }
3631
3632 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
3633 {
3634 wxCHECK_MSG( (m_widget != NULL), false, wxT("invalid window") );
3635
3636 if (cursor == m_cursor)
3637 return false;
3638
3639 if (g_isIdle)
3640 wxapp_install_idle_handler();
3641
3642 if (cursor == wxNullCursor)
3643 return wxWindowBase::SetCursor( *wxSTANDARD_CURSOR );
3644 else
3645 return wxWindowBase::SetCursor( cursor );
3646 }
3647
3648 void wxWindowGTK::WarpPointer( int x, int y )
3649 {
3650 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3651
3652 // We provide this function ourselves as it is
3653 // missing in GDK (top of this file).
3654
3655 GdkWindow *window = (GdkWindow*) NULL;
3656 if (m_wxwindow)
3657 window = GTK_PIZZA(m_wxwindow)->bin_window;
3658 else
3659 window = GetConnectWidget()->window;
3660
3661 if (window)
3662 gdk_window_warp_pointer( window, x, y );
3663 }
3664
3665 static bool wxScrollAdjust(GtkAdjustment* adj, double change)
3666 {
3667 double value_start = adj->value;
3668 double value = value_start + change;
3669 double upper = adj->upper - adj->page_size;
3670 if (value > upper)
3671 {
3672 value = upper;
3673 }
3674 // Lower bound will be checked by gtk_adjustment_set_value
3675 gtk_adjustment_set_value(adj, value);
3676 return adj->value != value_start;
3677 }
3678
3679 bool wxWindowGTK::ScrollLines(int lines)
3680 {
3681 return
3682 m_vAdjust != NULL &&
3683 wxScrollAdjust(m_vAdjust, lines * m_vAdjust->step_increment);
3684 }
3685
3686 bool wxWindowGTK::ScrollPages(int pages)
3687 {
3688 return
3689 m_vAdjust != NULL &&
3690 wxScrollAdjust(m_vAdjust, pages * m_vAdjust->page_increment);
3691 }
3692
3693 void wxWindowGTK::SetVScrollAdjustment(GtkAdjustment* adj)
3694 {
3695 wxASSERT(m_vAdjust == NULL);
3696 m_vAdjust = adj;
3697 }
3698
3699 void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
3700 {
3701 if (!m_widget)
3702 return;
3703 if (!m_widget->window)
3704 return;
3705
3706 if (m_wxwindow)
3707 {
3708 GdkRectangle gdk_rect,
3709 *p;
3710 if (rect)
3711 {
3712 gdk_rect.x = rect->x;
3713 gdk_rect.y = rect->y;
3714 gdk_rect.width = rect->width;
3715 gdk_rect.height = rect->height;
3716 p = &gdk_rect;
3717 }
3718 else // invalidate everything
3719 {
3720 p = NULL;
3721 }
3722
3723 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, p, TRUE );
3724 }
3725 }
3726
3727 void wxWindowGTK::Update()
3728 {
3729 GtkUpdate();
3730
3731 // when we call Update() we really want to update the window immediately on
3732 // screen, even if it means flushing the entire queue and hence slowing down
3733 // everything -- but it should still be done, it's just that Update() should
3734 // be called very rarely
3735 gdk_flush();
3736 }
3737
3738 void wxWindowGTK::GtkUpdate()
3739 {
3740 if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window)
3741 gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE );
3742
3743 // for consistency with other platforms (and also because it's convenient
3744 // to be able to update an entire TLW by calling Update() only once), we
3745 // should also update all our children here
3746 for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
3747 node;
3748 node = node->GetNext() )
3749 {
3750 node->GetData()->GtkUpdate();
3751 }
3752 }
3753
3754 void wxWindowGTK::GtkSendPaintEvents()
3755 {
3756 if (!m_wxwindow)
3757 {
3758 m_updateRegion.Clear();
3759 return;
3760 }
3761
3762 // Clip to paint region in wxClientDC
3763 m_clipPaintRegion = true;
3764
3765 // widget to draw on
3766 GtkPizza *pizza = GTK_PIZZA (m_wxwindow);
3767
3768 if (GetThemeEnabled() && (GetBackgroundStyle() == wxBG_STYLE_SYSTEM))
3769 {
3770 // find ancestor from which to steal background
3771 wxWindow *parent = wxGetTopLevelParent((wxWindow *)this);
3772 if (!parent)
3773 parent = (wxWindow*)this;
3774
3775 if (GTK_WIDGET_MAPPED(parent->m_widget))
3776 {
3777 wxRegionIterator upd( m_updateRegion );
3778 while (upd)
3779 {
3780 GdkRectangle rect;
3781 rect.x = upd.GetX();
3782 rect.y = upd.GetY();
3783 rect.width = upd.GetWidth();
3784 rect.height = upd.GetHeight();
3785
3786 gtk_paint_flat_box( parent->m_widget->style,
3787 pizza->bin_window,
3788 (GtkStateType)GTK_WIDGET_STATE(m_wxwindow),
3789 GTK_SHADOW_NONE,
3790 &rect,
3791 parent->m_widget,
3792 (char *)"base",
3793 0, 0, -1, -1 );
3794
3795 ++upd;
3796 }
3797 }
3798 }
3799 else
3800
3801 {
3802 wxWindowDC dc( (wxWindow*)this );
3803 dc.SetClippingRegion( m_updateRegion );
3804
3805 wxEraseEvent erase_event( GetId(), &dc );
3806 erase_event.SetEventObject( this );
3807
3808 GetEventHandler()->ProcessEvent(erase_event);
3809 }
3810
3811 wxNcPaintEvent nc_paint_event( GetId() );
3812 nc_paint_event.SetEventObject( this );
3813 GetEventHandler()->ProcessEvent( nc_paint_event );
3814
3815 wxPaintEvent paint_event( GetId() );
3816 paint_event.SetEventObject( this );
3817 GetEventHandler()->ProcessEvent( paint_event );
3818
3819 m_clipPaintRegion = false;
3820
3821 m_updateRegion.Clear();
3822 }
3823
3824 void wxWindowGTK::ClearBackground()
3825 {
3826 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3827 }
3828
3829 #if wxUSE_TOOLTIPS
3830 void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
3831 {
3832 wxWindowBase::DoSetToolTip(tip);
3833
3834 if (m_tooltip)
3835 m_tooltip->Apply( (wxWindow *)this );
3836 }
3837
3838 void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
3839 {
3840 wxString tmp( tip );
3841 gtk_tooltips_set_tip( tips, GetConnectWidget(), wxGTK_CONV(tmp), (gchar*) NULL );
3842 }
3843 #endif // wxUSE_TOOLTIPS
3844
3845 bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
3846 {
3847 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3848
3849 if (!wxWindowBase::SetBackgroundColour(colour))
3850 return false;
3851
3852 if (colour.Ok())
3853 {
3854 // We need the pixel value e.g. for background clearing.
3855 m_backgroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
3856 }
3857
3858 // apply style change (forceStyle=true so that new style is applied
3859 // even if the bg colour changed from valid to wxNullColour)
3860 if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM)
3861 ApplyWidgetStyle(true);
3862
3863 return true;
3864 }
3865
3866 bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
3867 {
3868 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
3869
3870 if (!wxWindowBase::SetForegroundColour(colour))
3871 {
3872 return false;
3873 }
3874
3875 if (colour.Ok())
3876 {
3877 // We need the pixel value e.g. for background clearing.
3878 m_foregroundColour.CalcPixel(gtk_widget_get_colormap(m_widget));
3879 }
3880
3881 // apply style change (forceStyle=true so that new style is applied
3882 // even if the bg colour changed from valid to wxNullColour):
3883 ApplyWidgetStyle(true);
3884
3885 return true;
3886 }
3887
3888 PangoContext *wxWindowGTK::GtkGetPangoDefaultContext()
3889 {
3890 return gtk_widget_get_pango_context( m_widget );
3891 }
3892
3893 GtkRcStyle *wxWindowGTK::CreateWidgetStyle(bool forceStyle)
3894 {
3895 // do we need to apply any changes at all?
3896 if ( !forceStyle &&
3897 !m_font.Ok() &&
3898 !m_foregroundColour.Ok() && !m_backgroundColour.Ok() )
3899 {
3900 return NULL;
3901 }
3902
3903 GtkRcStyle *style = gtk_rc_style_new();
3904
3905 if ( m_font.Ok() )
3906 {
3907 style->font_desc =
3908 pango_font_description_copy( m_font.GetNativeFontInfo()->description );
3909 }
3910
3911 if ( m_foregroundColour.Ok() )
3912 {
3913 GdkColor *fg = m_foregroundColour.GetColor();
3914
3915 style->fg[GTK_STATE_NORMAL] = *fg;
3916 style->color_flags[GTK_STATE_NORMAL] = GTK_RC_FG;
3917
3918 style->fg[GTK_STATE_PRELIGHT] = *fg;
3919 style->color_flags[GTK_STATE_PRELIGHT] = GTK_RC_FG;
3920
3921 style->fg[GTK_STATE_ACTIVE] = *fg;
3922 style->color_flags[GTK_STATE_ACTIVE] = GTK_RC_FG;
3923 }
3924
3925 if ( m_backgroundColour.Ok() )
3926 {
3927 GdkColor *bg = m_backgroundColour.GetColor();
3928
3929 style->bg[GTK_STATE_NORMAL] = *bg;
3930 style->base[GTK_STATE_NORMAL] = *bg;
3931 style->color_flags[GTK_STATE_NORMAL] = (GtkRcFlags)
3932 (style->color_flags[GTK_STATE_NORMAL] | GTK_RC_BG | GTK_RC_BASE);
3933
3934 style->bg[GTK_STATE_PRELIGHT] = *bg;
3935 style->base[GTK_STATE_PRELIGHT] = *bg;
3936 style->color_flags[GTK_STATE_PRELIGHT] = (GtkRcFlags)
3937 (style->color_flags[GTK_STATE_PRELIGHT] | GTK_RC_BG | GTK_RC_BASE);
3938
3939 style->bg[GTK_STATE_ACTIVE] = *bg;
3940 style->base[GTK_STATE_ACTIVE] = *bg;
3941 style->color_flags[GTK_STATE_ACTIVE] = (GtkRcFlags)
3942 (style->color_flags[GTK_STATE_ACTIVE] | GTK_RC_BG | GTK_RC_BASE);
3943
3944 style->bg[GTK_STATE_INSENSITIVE] = *bg;
3945 style->base[GTK_STATE_INSENSITIVE] = *bg;
3946 style->color_flags[GTK_STATE_INSENSITIVE] = (GtkRcFlags)
3947 (style->color_flags[GTK_STATE_INSENSITIVE] | GTK_RC_BG | GTK_RC_BASE);
3948 }
3949
3950 return style;
3951 }
3952
3953 void wxWindowGTK::ApplyWidgetStyle(bool forceStyle)
3954 {
3955 GtkRcStyle *style = CreateWidgetStyle(forceStyle);
3956 if ( style )
3957 {
3958 DoApplyWidgetStyle(style);
3959 gtk_rc_style_unref(style);
3960 }
3961
3962 // Style change may affect GTK+'s size calculation:
3963 InvalidateBestSize();
3964 }
3965
3966 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
3967 {
3968 if (m_wxwindow)
3969 gtk_widget_modify_style(m_wxwindow, style);
3970 else
3971 gtk_widget_modify_style(m_widget, style);
3972 }
3973
3974 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
3975 {
3976 wxWindowBase::SetBackgroundStyle(style);
3977
3978 if (style == wxBG_STYLE_CUSTOM)
3979 {
3980 GdkWindow *window = (GdkWindow*) NULL;
3981 if (m_wxwindow)
3982 window = GTK_PIZZA(m_wxwindow)->bin_window;
3983 else
3984 window = GetConnectWidget()->window;
3985
3986 if (window)
3987 {
3988 // Make sure GDK/X11 doesn't refresh the window
3989 // automatically.
3990 gdk_window_set_back_pixmap( window, None, False );
3991 #ifdef __X__
3992 Display* display = GDK_WINDOW_DISPLAY(window);
3993 XFlush(display);
3994 #endif
3995 m_needsStyleChange = false;
3996 }
3997 else
3998 // Do in OnIdle, because the window is not yet available
3999 m_needsStyleChange = true;
4000
4001 // Don't apply widget style, or we get a grey background
4002 }
4003 else
4004 {
4005 // apply style change (forceStyle=true so that new style is applied
4006 // even if the bg colour changed from valid to wxNullColour):
4007 ApplyWidgetStyle(true);
4008 }
4009 return true;
4010 }
4011
4012 #if wxUSE_DRAG_AND_DROP
4013
4014 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
4015 {
4016 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4017
4018 GtkWidget *dnd_widget = GetConnectWidget();
4019
4020 if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
4021
4022 if (m_dropTarget) delete m_dropTarget;
4023 m_dropTarget = dropTarget;
4024
4025 if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
4026 }
4027
4028 #endif // wxUSE_DRAG_AND_DROP
4029
4030 GtkWidget* wxWindowGTK::GetConnectWidget()
4031 {
4032 GtkWidget *connect_widget = m_widget;
4033 if (m_wxwindow) connect_widget = m_wxwindow;
4034
4035 return connect_widget;
4036 }
4037
4038 bool wxWindowGTK::IsOwnGtkWindow( GdkWindow *window )
4039 {
4040 if (m_wxwindow)
4041 return (window == GTK_PIZZA(m_wxwindow)->bin_window);
4042
4043 return (window == m_widget->window);
4044 }
4045
4046 bool wxWindowGTK::SetFont( const wxFont &font )
4047 {
4048 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") );
4049
4050 if (!wxWindowBase::SetFont(font))
4051 return false;
4052
4053 // apply style change (forceStyle=true so that new style is applied
4054 // even if the font changed from valid to wxNullFont):
4055 ApplyWidgetStyle(true);
4056
4057 return true;
4058 }
4059
4060 void wxWindowGTK::DoCaptureMouse()
4061 {
4062 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4063
4064 GdkWindow *window = (GdkWindow*) NULL;
4065 if (m_wxwindow)
4066 window = GTK_PIZZA(m_wxwindow)->bin_window;
4067 else
4068 window = GetConnectWidget()->window;
4069
4070 wxCHECK_RET( window, _T("CaptureMouse() failed") );
4071
4072 wxCursor* cursor = & m_cursor;
4073 if (!cursor->Ok())
4074 cursor = wxSTANDARD_CURSOR;
4075
4076 gdk_pointer_grab( window, FALSE,
4077 (GdkEventMask)
4078 (GDK_BUTTON_PRESS_MASK |
4079 GDK_BUTTON_RELEASE_MASK |
4080 GDK_POINTER_MOTION_HINT_MASK |
4081 GDK_POINTER_MOTION_MASK),
4082 (GdkWindow *) NULL,
4083 cursor->GetCursor(),
4084 (guint32)GDK_CURRENT_TIME );
4085 g_captureWindow = this;
4086 g_captureWindowHasMouse = true;
4087 }
4088
4089 void wxWindowGTK::DoReleaseMouse()
4090 {
4091 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4092
4093 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
4094
4095 g_captureWindow = (wxWindowGTK*) NULL;
4096
4097 GdkWindow *window = (GdkWindow*) NULL;
4098 if (m_wxwindow)
4099 window = GTK_PIZZA(m_wxwindow)->bin_window;
4100 else
4101 window = GetConnectWidget()->window;
4102
4103 if (!window)
4104 return;
4105
4106 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
4107 }
4108
4109 /* static */
4110 wxWindow *wxWindowBase::GetCapture()
4111 {
4112 return (wxWindow *)g_captureWindow;
4113 }
4114
4115 bool wxWindowGTK::IsRetained() const
4116 {
4117 return false;
4118 }
4119
4120 void wxWindowGTK::SetScrollbar( int orient, int pos, int thumbVisible,
4121 int range, bool refresh )
4122 {
4123 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4124
4125 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4126
4127 m_hasScrolling = true;
4128
4129 if (orient == wxHORIZONTAL)
4130 {
4131 float fpos = (float)pos;
4132 float frange = (float)range;
4133 float fthumb = (float)thumbVisible;
4134 if (fpos > frange-fthumb) fpos = frange-fthumb;
4135 if (fpos < 0.0) fpos = 0.0;
4136
4137 if ((fabs(frange-m_hAdjust->upper) < 0.2) &&
4138 (fabs(fthumb-m_hAdjust->page_size) < 0.2))
4139 {
4140 SetScrollPos( orient, pos, refresh );
4141 return;
4142 }
4143
4144 m_oldHorizontalPos = fpos;
4145
4146 m_hAdjust->lower = 0.0;
4147 m_hAdjust->upper = frange;
4148 m_hAdjust->value = fpos;
4149 m_hAdjust->step_increment = 1.0;
4150 m_hAdjust->page_increment = (float)(wxMax(fthumb,0));
4151 m_hAdjust->page_size = fthumb;
4152 }
4153 else
4154 {
4155 float fpos = (float)pos;
4156 float frange = (float)range;
4157 float fthumb = (float)thumbVisible;
4158 if (fpos > frange-fthumb) fpos = frange-fthumb;
4159 if (fpos < 0.0) fpos = 0.0;
4160
4161 if ((fabs(frange-m_vAdjust->upper) < 0.2) &&
4162 (fabs(fthumb-m_vAdjust->page_size) < 0.2))
4163 {
4164 SetScrollPos( orient, pos, refresh );
4165 return;
4166 }
4167
4168 m_oldVerticalPos = fpos;
4169
4170 m_vAdjust->lower = 0.0;
4171 m_vAdjust->upper = frange;
4172 m_vAdjust->value = fpos;
4173 m_vAdjust->step_increment = 1.0;
4174 m_vAdjust->page_increment = (float)(wxMax(fthumb,0));
4175 m_vAdjust->page_size = fthumb;
4176 }
4177
4178 if (orient == wxHORIZONTAL)
4179 g_signal_emit_by_name (m_hAdjust, "changed");
4180 else
4181 g_signal_emit_by_name (m_vAdjust, "changed");
4182 }
4183
4184 void wxWindowGTK::GtkUpdateScrollbar(int orient)
4185 {
4186 GtkAdjustment *adj = orient == wxHORIZONTAL ? m_hAdjust : m_vAdjust;
4187 gpointer fn = orient == wxHORIZONTAL
4188 ? (gpointer) gtk_window_hscroll_callback
4189 : (gpointer) gtk_window_vscroll_callback;
4190
4191 g_signal_handlers_disconnect_by_func (adj, fn, this);
4192 g_signal_emit_by_name (adj, "value_changed");
4193 g_signal_connect (adj, "value_changed", G_CALLBACK (fn), this);
4194 }
4195
4196 void wxWindowGTK::SetScrollPos( int orient, int pos, bool WXUNUSED(refresh) )
4197 {
4198 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4199 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4200
4201 GtkAdjustment *adj = orient == wxHORIZONTAL ? m_hAdjust : m_vAdjust;
4202
4203 float fpos = (float)pos;
4204 if (fpos > adj->upper - adj->page_size)
4205 fpos = adj->upper - adj->page_size;
4206 if (fpos < 0.0)
4207 fpos = 0.0;
4208 *(orient == wxHORIZONTAL ? &m_oldHorizontalPos : &m_oldVerticalPos) = fpos;
4209
4210 if (fabs(fpos-adj->value) < 0.2)
4211 return;
4212 adj->value = fpos;
4213
4214 if ( m_wxwindow->window )
4215 {
4216 }
4217 }
4218
4219 int wxWindowGTK::GetScrollThumb( int orient ) const
4220 {
4221 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4222
4223 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4224
4225 if (orient == wxHORIZONTAL)
4226 return (int)(m_hAdjust->page_size+0.5);
4227 else
4228 return (int)(m_vAdjust->page_size+0.5);
4229 }
4230
4231 int wxWindowGTK::GetScrollPos( int orient ) const
4232 {
4233 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4234
4235 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4236
4237 if (orient == wxHORIZONTAL)
4238 return (int)(m_hAdjust->value+0.5);
4239 else
4240 return (int)(m_vAdjust->value+0.5);
4241 }
4242
4243 int wxWindowGTK::GetScrollRange( int orient ) const
4244 {
4245 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4246
4247 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4248
4249 if (orient == wxHORIZONTAL)
4250 return (int)(m_hAdjust->upper+0.5);
4251 else
4252 return (int)(m_vAdjust->upper+0.5);
4253 }
4254
4255 void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
4256 {
4257 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4258
4259 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4260
4261 // No scrolling requested.
4262 if ((dx == 0) && (dy == 0)) return;
4263
4264 m_clipPaintRegion = true;
4265
4266 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy );
4267
4268 m_clipPaintRegion = false;
4269 }
4270
4271 void wxWindowGTK::SetWindowStyleFlag( long style )
4272 {
4273 // Updates the internal variable. NB: Now m_windowStyle bits carry the _new_ style values already
4274 wxWindowBase::SetWindowStyleFlag(style);
4275 }
4276
4277 // Find the wxWindow at the current mouse position, also returning the mouse
4278 // position.
4279 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4280 {
4281 pt = wxGetMousePosition();
4282 wxWindow* found = wxFindWindowAtPoint(pt);
4283 return found;
4284 }
4285
4286 // Get the current mouse position.
4287 wxPoint wxGetMousePosition()
4288 {
4289 /* This crashes when used within wxHelpContext,
4290 so we have to use the X-specific implementation below.
4291 gint x, y;
4292 GdkModifierType *mask;
4293 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4294
4295 return wxPoint(x, y);
4296 */
4297
4298 int x, y;
4299 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
4300
4301 Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY();
4302 Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
4303 Window rootReturn, childReturn;
4304 int rootX, rootY, winX, winY;
4305 unsigned int maskReturn;
4306
4307 XQueryPointer (display,
4308 rootWindow,
4309 &rootReturn,
4310 &childReturn,
4311 &rootX, &rootY, &winX, &winY, &maskReturn);
4312 return wxPoint(rootX, rootY);
4313
4314 }
4315
4316 // Needed for implementing e.g. combobox on wxGTK within a modal dialog.
4317 void wxAddGrab(wxWindow* window)
4318 {
4319 gtk_grab_add( (GtkWidget*) window->GetHandle() );
4320 }
4321
4322 void wxRemoveGrab(wxWindow* window)
4323 {
4324 gtk_grab_remove( (GtkWidget*) window->GetHandle() );
4325 }
4326
4327 // ----------------------------------------------------------------------------
4328 // wxWinModule
4329 // ----------------------------------------------------------------------------
4330
4331 class wxWinModule : public wxModule
4332 {
4333 public:
4334 bool OnInit();
4335 void OnExit();
4336
4337 private:
4338 DECLARE_DYNAMIC_CLASS(wxWinModule)
4339 };
4340
4341 IMPLEMENT_DYNAMIC_CLASS(wxWinModule, wxModule)
4342
4343 bool wxWinModule::OnInit()
4344 {
4345 // g_eraseGC = gdk_gc_new( GDK_ROOT_PARENT() );
4346 // gdk_gc_set_fill( g_eraseGC, GDK_SOLID );
4347
4348 return true;
4349 }
4350
4351 void wxWinModule::OnExit()
4352 {
4353 if (g_eraseGC)
4354 gdk_gc_unref( g_eraseGC );
4355 }
4356