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