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