]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/window.cpp
Scrolling pages works not under GTK2.
[wxWidgets.git] / src / gtk / window.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: gtk/window.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling, Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10
11 #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 wxWindows, 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 wxWindows 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 wxWindows 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 wxWindows 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 wxWindows 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 wxWindows, 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 wxWindows 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 gdk_event
1351 //
1352 // NB: this has to be a macro as gdk_event type is different for different
1353 // events we're used with
1354 #define InitMouseEvent(/* wxWindowGTK * */ win, \
1355 /* wxMouseEvent& */ event, \
1356 /* GdkEventXXX * */ gdk_event) \
1357 { \
1358 event.SetTimestamp( gdk_event->time ); \
1359 event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK); \
1360 event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK); \
1361 event.m_altDown = (gdk_event->state & GDK_MOD1_MASK); \
1362 event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK); \
1363 event.m_leftDown = (gdk_event->state & GDK_BUTTON1_MASK); \
1364 event.m_middleDown = (gdk_event->state & GDK_BUTTON2_MASK); \
1365 event.m_rightDown = (gdk_event->state & GDK_BUTTON3_MASK); \
1366 if (event.GetEventType()==wxEVT_MOUSEWHEEL) \
1367 { \
1368 if (((GdkEventButton*)gdk_event)->button == 4) \
1369 event.m_wheelRotation = 120; \
1370 else if (((GdkEventButton*)gdk_event)->button == 5) \
1371 event.m_wheelRotation = -120; \
1372 } \
1373 \
1374 wxPoint pt = win->GetClientAreaOrigin(); \
1375 event.m_x = (wxCoord)gdk_event->x - pt.x; \
1376 event.m_y = (wxCoord)gdk_event->y - pt.y; \
1377 \
1378 event.SetEventObject( win ); \
1379 event.SetId( win->GetId() ); \
1380 event.SetTimestamp( gdk_event->time ); \
1381 } \
1382
1383 static void AdjustEventButtonState(wxMouseEvent& event)
1384 {
1385 // GDK reports the old state of the button for a button press event, but
1386 // for compatibility with MSW and common sense we want m_leftDown be TRUE
1387 // for a LEFT_DOWN event, not FALSE, so we will invert
1388 // left/right/middleDown for the corresponding click events
1389
1390 if ((event.GetEventType() == wxEVT_LEFT_DOWN) ||
1391 (event.GetEventType() == wxEVT_LEFT_DCLICK) ||
1392 (event.GetEventType() == wxEVT_LEFT_UP))
1393 {
1394 event.m_leftDown = !event.m_leftDown;
1395 return;
1396 }
1397
1398 if ((event.GetEventType() == wxEVT_MIDDLE_DOWN) ||
1399 (event.GetEventType() == wxEVT_MIDDLE_DCLICK) ||
1400 (event.GetEventType() == wxEVT_MIDDLE_UP))
1401 {
1402 event.m_middleDown = !event.m_middleDown;
1403 return;
1404 }
1405
1406 if ((event.GetEventType() == wxEVT_RIGHT_DOWN) ||
1407 (event.GetEventType() == wxEVT_RIGHT_DCLICK) ||
1408 (event.GetEventType() == wxEVT_RIGHT_UP))
1409 {
1410 event.m_rightDown = !event.m_rightDown;
1411 return;
1412 }
1413 }
1414
1415 // find the window to send the mouse event too
1416 static
1417 wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
1418 {
1419 wxCoord xx = x;
1420 wxCoord yy = y;
1421
1422 if (win->m_wxwindow)
1423 {
1424 GtkPizza *pizza = GTK_PIZZA(win->m_wxwindow);
1425 xx += pizza->xoffset;
1426 yy += pizza->yoffset;
1427 }
1428
1429 wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
1430 while (node)
1431 {
1432 wxWindowGTK *child = node->GetData();
1433
1434 node = node->GetNext();
1435 if (!child->IsShown())
1436 continue;
1437
1438 if (child->IsTransparentForMouse())
1439 {
1440 // wxStaticBox is transparent in the box itself
1441 int xx1 = child->m_x;
1442 int yy1 = child->m_y;
1443 int xx2 = child->m_x + child->m_width;
1444 int yy2 = child->m_y + child->m_height;
1445
1446 // left
1447 if (((xx >= xx1) && (xx <= xx1+10) && (yy >= yy1) && (yy <= yy2)) ||
1448 // right
1449 ((xx >= xx2-10) && (xx <= xx2) && (yy >= yy1) && (yy <= yy2)) ||
1450 // top
1451 ((xx >= xx1) && (xx <= xx2) && (yy >= yy1) && (yy <= yy1+10)) ||
1452 // bottom
1453 ((xx >= xx1) && (xx <= xx2) && (yy >= yy2-1) && (yy <= yy2)))
1454 {
1455 win = child;
1456 x -= child->m_x;
1457 y -= child->m_y;
1458 break;
1459 }
1460
1461 }
1462 else
1463 {
1464 if ((child->m_wxwindow == (GtkWidget*) NULL) &&
1465 (child->m_x <= xx) &&
1466 (child->m_y <= yy) &&
1467 (child->m_x+child->m_width >= xx) &&
1468 (child->m_y+child->m_height >= yy))
1469 {
1470 win = child;
1471 x -= child->m_x;
1472 y -= child->m_y;
1473 break;
1474 }
1475 }
1476 }
1477
1478 return win;
1479 }
1480
1481 //-----------------------------------------------------------------------------
1482 // "button_press_event"
1483 //-----------------------------------------------------------------------------
1484
1485 static gint gtk_window_button_press_callback( GtkWidget *widget,
1486 GdkEventButton *gdk_event,
1487 wxWindowGTK *win )
1488 {
1489 DEBUG_MAIN_THREAD
1490
1491 if (g_isIdle)
1492 wxapp_install_idle_handler();
1493
1494 /*
1495 wxPrintf( wxT("1) OnButtonPress from ") );
1496 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1497 wxPrintf( win->GetClassInfo()->GetClassName() );
1498 wxPrintf( wxT(".\n") );
1499 */
1500 if (!win->m_hasVMT) return FALSE;
1501 if (g_blockEventsOnDrag) return TRUE;
1502 if (g_blockEventsOnScroll) return TRUE;
1503
1504 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1505
1506 if (win->m_wxwindow && (g_focusWindow != win) && win->AcceptsFocus())
1507 {
1508 gtk_widget_grab_focus( win->m_wxwindow );
1509 /*
1510 wxPrintf( wxT("GrabFocus from ") );
1511 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1512 wxPrintf( win->GetClassInfo()->GetClassName() );
1513 wxPrintf( wxT(".\n") );
1514 */
1515 }
1516
1517 // GDK sends surplus button down event
1518 // before a double click event. We
1519 // need to filter these out.
1520 if (gdk_event->type == GDK_BUTTON_PRESS)
1521 {
1522 GdkEvent *peek_event = gdk_event_peek();
1523 if (peek_event)
1524 {
1525 if ((peek_event->type == GDK_2BUTTON_PRESS) ||
1526 (peek_event->type == GDK_3BUTTON_PRESS))
1527 {
1528 gdk_event_free( peek_event );
1529 return TRUE;
1530 }
1531 else
1532 {
1533 gdk_event_free( peek_event );
1534 }
1535 }
1536 }
1537
1538 wxEventType event_type = wxEVT_NULL;
1539
1540 #ifdef __WXGTK20__
1541 if ( gdk_event->type == GDK_2BUTTON_PRESS &&
1542 gdk_event->button >= 1 && gdk_event->button <= 3 )
1543 {
1544 // Reset GDK internal timestamp variables in order to disable GDK
1545 // triple click events. GDK will then next time believe no button has
1546 // been clicked just before, and send a normal button click event.
1547 GdkDisplay* display = gtk_widget_get_display (widget);
1548 display->button_click_time[1] = 0;
1549 display->button_click_time[0] = 0;
1550 }
1551 #endif // GTK 2+
1552
1553 if (gdk_event->button == 1)
1554 {
1555 // note that GDK generates triple click events which are not supported
1556 // by wxWindows but still have to be passed to the app as otherwise
1557 // clicks would simply go missing
1558 switch (gdk_event->type)
1559 {
1560 // we shouldn't get triple clicks at all for GTK2 because we
1561 // suppress them artificially using the code above but we still
1562 // should map them to something for GTK1 and not just ignore them
1563 // as this would lose clicks
1564 case GDK_3BUTTON_PRESS: // we could also map this to DCLICK...
1565 case GDK_BUTTON_PRESS:
1566 event_type = wxEVT_LEFT_DOWN;
1567 break;
1568
1569 case GDK_2BUTTON_PRESS:
1570 event_type = wxEVT_LEFT_DCLICK;
1571 break;
1572
1573 default:
1574 // just to silence gcc warnings
1575 ;
1576 }
1577 }
1578 else if (gdk_event->button == 2)
1579 {
1580 switch (gdk_event->type)
1581 {
1582 case GDK_3BUTTON_PRESS:
1583 case GDK_BUTTON_PRESS:
1584 event_type = wxEVT_MIDDLE_DOWN;
1585 break;
1586
1587 case GDK_2BUTTON_PRESS:
1588 event_type = wxEVT_MIDDLE_DCLICK;
1589 break;
1590
1591 default:
1592 ;
1593 }
1594 }
1595 else if (gdk_event->button == 3)
1596 {
1597 switch (gdk_event->type)
1598 {
1599 case GDK_3BUTTON_PRESS:
1600 case GDK_BUTTON_PRESS:
1601 event_type = wxEVT_RIGHT_DOWN;
1602 break;
1603
1604 case GDK_2BUTTON_PRESS:
1605 event_type = wxEVT_RIGHT_DCLICK;
1606 break;
1607
1608 default:
1609 ;
1610 }
1611 }
1612 else if (gdk_event->button == 4 || gdk_event->button == 5)
1613 {
1614 if (gdk_event->type == GDK_BUTTON_PRESS )
1615 {
1616 event_type = wxEVT_MOUSEWHEEL;
1617 }
1618 }
1619
1620 if ( event_type == wxEVT_NULL )
1621 {
1622 // unknown mouse button or click type
1623 return FALSE;
1624 }
1625
1626 wxMouseEvent event( event_type );
1627 InitMouseEvent( win, event, gdk_event );
1628
1629 AdjustEventButtonState(event);
1630
1631 // wxListBox actually get mouse events from the item, so we need to give it
1632 // a chance to correct this
1633 win->FixUpMouseEvent(widget, event.m_x, event.m_y);
1634
1635 // find the correct window to send the event too: it may be a different one
1636 // from the one which got it at GTK+ level because some control don't have
1637 // their own X window and thus cannot get any events.
1638 if ( !g_captureWindow )
1639 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1640
1641 gs_timeLastClick = gdk_event->time;
1642
1643 /*
1644 wxPrintf( wxT("2) OnButtonPress from ") );
1645 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1646 wxPrintf( win->GetClassInfo()->GetClassName() );
1647 wxPrintf( wxT(".\n") );
1648 */
1649
1650 #ifndef __WXGTK20__
1651 if (event_type == wxEVT_LEFT_DCLICK)
1652 {
1653 // GTK 1.2 crashes when intercepting double
1654 // click events from both wxSpinButton and
1655 // wxSpinCtrl
1656 if (GTK_IS_SPIN_BUTTON(win->m_widget))
1657 {
1658 // Just disable this event for now.
1659 return FALSE;
1660 }
1661 }
1662 #endif
1663
1664 if (win->GetEventHandler()->ProcessEvent( event ))
1665 {
1666 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "button_press_event" );
1667 return TRUE;
1668 }
1669
1670 return FALSE;
1671 }
1672
1673 //-----------------------------------------------------------------------------
1674 // "button_release_event"
1675 //-----------------------------------------------------------------------------
1676
1677 static gint gtk_window_button_release_callback( GtkWidget *widget,
1678 GdkEventButton *gdk_event,
1679 wxWindowGTK *win )
1680 {
1681 DEBUG_MAIN_THREAD
1682
1683 if (g_isIdle)
1684 wxapp_install_idle_handler();
1685
1686 if (!win->m_hasVMT) return FALSE;
1687 if (g_blockEventsOnDrag) return FALSE;
1688 if (g_blockEventsOnScroll) return FALSE;
1689
1690 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1691
1692 wxEventType event_type = wxEVT_NULL;
1693
1694 switch (gdk_event->button)
1695 {
1696 case 1:
1697 event_type = wxEVT_LEFT_UP;
1698 break;
1699
1700 case 2:
1701 event_type = wxEVT_MIDDLE_UP;
1702 break;
1703
1704 case 3:
1705 event_type = wxEVT_RIGHT_UP;
1706 break;
1707
1708 default:
1709 // unknwon button, don't process
1710 return FALSE;
1711 }
1712
1713 wxMouseEvent event( event_type );
1714 InitMouseEvent( win, event, gdk_event );
1715
1716 AdjustEventButtonState(event);
1717
1718 // same wxListBox hack as above
1719 win->FixUpMouseEvent(widget, event.m_x, event.m_y);
1720
1721 if ( event_type == wxEVT_RIGHT_UP )
1722 {
1723 // generate a "context menu" event: this is similar to wxEVT_RIGHT_UP
1724 // except that:
1725 //
1726 // (a) it's a command event and so is propagated to the parent
1727 // (b) under MSW it can be generated from kbd too
1728 // (c) it uses screen coords (because of (a))
1729 wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU,
1730 win->GetId(),
1731 win->ClientToScreen(event.GetPosition()));
1732 (void)win->GetEventHandler()->ProcessEvent(evtCtx);
1733 }
1734
1735 if ( !g_captureWindow )
1736 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1737
1738 if (win->GetEventHandler()->ProcessEvent( event ))
1739 {
1740 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "button_release_event" );
1741 return TRUE;
1742 }
1743
1744 return FALSE;
1745 }
1746
1747 //-----------------------------------------------------------------------------
1748 // "motion_notify_event"
1749 //-----------------------------------------------------------------------------
1750
1751 static gint gtk_window_motion_notify_callback( GtkWidget *widget,
1752 GdkEventMotion *gdk_event,
1753 wxWindowGTK *win )
1754 {
1755 DEBUG_MAIN_THREAD
1756
1757 if (g_isIdle)
1758 wxapp_install_idle_handler();
1759
1760 if (!win->m_hasVMT) return FALSE;
1761 if (g_blockEventsOnDrag) return FALSE;
1762 if (g_blockEventsOnScroll) return FALSE;
1763
1764 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1765
1766 if (gdk_event->is_hint)
1767 {
1768 int x = 0;
1769 int y = 0;
1770 GdkModifierType state;
1771 gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
1772 gdk_event->x = x;
1773 gdk_event->y = y;
1774 }
1775
1776 /*
1777 printf( "OnMotion from " );
1778 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1779 printf( win->GetClassInfo()->GetClassName() );
1780 printf( ".\n" );
1781 */
1782
1783 wxMouseEvent event( wxEVT_MOTION );
1784 InitMouseEvent(win, event, gdk_event);
1785
1786 if ( g_captureWindow )
1787 {
1788 // synthetize a mouse enter or leave event if needed
1789 GdkWindow *winUnderMouse = gdk_window_at_pointer(NULL, NULL);
1790 // This seems to be necessary and actually been added to
1791 // GDK itself in version 2.0.X
1792 gdk_flush();
1793
1794 bool hasMouse = winUnderMouse == gdk_event->window;
1795 if ( hasMouse != g_captureWindowHasMouse )
1796 {
1797 // the mouse changed window
1798 g_captureWindowHasMouse = hasMouse;
1799
1800 wxMouseEvent event(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW
1801 : wxEVT_LEAVE_WINDOW);
1802 InitMouseEvent(win, event, gdk_event);
1803 event.SetEventObject(win);
1804 win->GetEventHandler()->ProcessEvent(event);
1805 }
1806 }
1807 else // no capture
1808 {
1809 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1810 }
1811
1812 if (win->GetEventHandler()->ProcessEvent( event ))
1813 {
1814 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "motion_notify_event" );
1815 return TRUE;
1816 }
1817
1818 return FALSE;
1819 }
1820
1821 #ifdef __WXGTK20__
1822 //-----------------------------------------------------------------------------
1823 // "mouse_wheel_event"
1824 //-----------------------------------------------------------------------------
1825
1826 static gint gtk_window_wheel_callback (GtkWidget * widget,
1827 GdkEventScroll * gdk_event,
1828 wxWindowGTK * win)
1829 {
1830 DEBUG_MAIN_THREAD
1831
1832 if (g_isIdle)
1833 wxapp_install_idle_handler();
1834
1835 wxEventType event_type = wxEVT_NULL;
1836 if (gdk_event->direction == GDK_SCROLL_UP)
1837 event_type = wxEVT_MOUSEWHEEL;
1838 else if (gdk_event->direction == GDK_SCROLL_DOWN)
1839 event_type = wxEVT_MOUSEWHEEL;
1840 else
1841 return FALSE;
1842
1843 wxMouseEvent event( event_type );
1844 // Can't use InitMouse macro because scroll events don't have button
1845 event.SetTimestamp( gdk_event->time );
1846 event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK);
1847 event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK);
1848 event.m_altDown = (gdk_event->state & GDK_MOD1_MASK);
1849 event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK);
1850 event.m_leftDown = (gdk_event->state & GDK_BUTTON1_MASK);
1851 event.m_middleDown = (gdk_event->state & GDK_BUTTON2_MASK);
1852 event.m_rightDown = (gdk_event->state & GDK_BUTTON3_MASK);
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 wxWindows 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 Show( TRUE );
2733
2734 return TRUE;
2735 }
2736
2737 wxWindowGTK::~wxWindowGTK()
2738 {
2739 SendDestroyEvent();
2740
2741 if (g_focusWindow == this)
2742 g_focusWindow = NULL;
2743
2744 if (g_activeFrame == this)
2745 g_activeFrame = NULL;
2746
2747 if ( g_delayedFocus == this )
2748 g_delayedFocus = NULL;
2749
2750 m_isBeingDeleted = TRUE;
2751 m_hasVMT = FALSE;
2752
2753 if (m_widget)
2754 Show( FALSE );
2755
2756 DestroyChildren();
2757
2758 #ifdef HAVE_XIM
2759 if (m_ic)
2760 gdk_ic_destroy (m_ic);
2761 if (m_icattr)
2762 gdk_ic_attr_destroy (m_icattr);
2763 #endif
2764
2765 if (m_widgetStyle)
2766 {
2767 #if DISABLE_STYLE_IF_BROKEN_THEME
2768 // don't delete if it's a pixmap theme style
2769 if (!m_widgetStyle->engine_data)
2770 gtk_style_unref( m_widgetStyle );
2771 #endif
2772 m_widgetStyle = (GtkStyle*) NULL;
2773 }
2774
2775 if (m_wxwindow)
2776 {
2777 gtk_widget_destroy( m_wxwindow );
2778 m_wxwindow = (GtkWidget*) NULL;
2779 }
2780
2781 if (m_widget)
2782 {
2783 gtk_widget_destroy( m_widget );
2784 m_widget = (GtkWidget*) NULL;
2785 }
2786 }
2787
2788 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
2789 {
2790 wxCHECK_MSG( !m_needParent || parent, FALSE, wxT("Need complete parent.") );
2791
2792 // This turns -1 into 30 so that a minimal window is
2793 // visible even although -1,-1 has been given as the
2794 // size of the window. the same trick is used in other
2795 // ports and should make debugging easier.
2796 m_width = WidthDefault(size.x) ;
2797 m_height = HeightDefault(size.y);
2798
2799 m_x = (int)pos.x;
2800 m_y = (int)pos.y;
2801
2802 // some reasonable defaults
2803 if (!parent)
2804 {
2805 if (m_x == -1)
2806 {
2807 m_x = (gdk_screen_width () - m_width) / 2;
2808 if (m_x < 10) m_x = 10;
2809 }
2810 if (m_y == -1)
2811 {
2812 m_y = (gdk_screen_height () - m_height) / 2;
2813 if (m_y < 10) m_y = 10;
2814 }
2815 }
2816
2817 return TRUE;
2818 }
2819
2820 void wxWindowGTK::PostCreation()
2821 {
2822 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2823
2824 if (m_wxwindow)
2825 {
2826 if (!m_noExpose)
2827 {
2828 // these get reported to wxWindows -> wxPaintEvent
2829
2830 gtk_pizza_set_external( GTK_PIZZA(m_wxwindow), TRUE );
2831
2832 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "expose_event",
2833 GTK_SIGNAL_FUNC(gtk_window_expose_callback), (gpointer)this );
2834
2835 #ifndef __WXGTK20__
2836 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "draw",
2837 GTK_SIGNAL_FUNC(gtk_window_draw_callback), (gpointer)this );
2838
2839 if (!HasFlag(wxFULL_REPAINT_ON_RESIZE))
2840 {
2841 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "event",
2842 GTK_SIGNAL_FUNC(gtk_window_event_event_callback), (gpointer)this );
2843 }
2844 #else
2845 // gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), !HasFlag( wxFULL_REPAINT_ON_RESIZE ) );
2846 #endif
2847
2848 #ifdef __WXGTK20__
2849 // Create input method handler
2850 m_imContext = (GtkIMMulticontext*) gtk_im_multicontext_new ();
2851
2852 // Cannot handle drawing preedited text yet
2853 gtk_im_context_set_use_preedit( (GtkIMContext*) m_imContext, FALSE );
2854
2855 g_signal_connect (G_OBJECT (m_imContext), "commit",
2856 G_CALLBACK (gtk_wxwindow_commit_cb), this);
2857 #endif
2858 }
2859
2860 // these are called when the "sunken" or "raised" borders are drawn
2861 gtk_signal_connect( GTK_OBJECT(m_widget), "expose_event",
2862 GTK_SIGNAL_FUNC(gtk_window_own_expose_callback), (gpointer)this );
2863
2864 #ifndef __WXGTK20__
2865 gtk_signal_connect( GTK_OBJECT(m_widget), "draw",
2866 GTK_SIGNAL_FUNC(gtk_window_own_draw_callback), (gpointer)this );
2867 #endif
2868 }
2869
2870 // focus handling
2871
2872 if (m_focusWidget == NULL)
2873 m_focusWidget = m_widget;
2874
2875 gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_in_event",
2876 GTK_SIGNAL_FUNC(gtk_window_focus_in_callback), (gpointer)this );
2877
2878 gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_out_event",
2879 GTK_SIGNAL_FUNC(gtk_window_focus_out_callback), (gpointer)this );
2880
2881 // connect to the various key and mouse handlers
2882
2883 GtkWidget *connect_widget = GetConnectWidget();
2884
2885 ConnectWidget( connect_widget );
2886
2887 /* We cannot set colours, fonts and cursors before the widget has
2888 been realized, so we do this directly after realization */
2889 gtk_signal_connect( GTK_OBJECT(connect_widget), "realize",
2890 GTK_SIGNAL_FUNC(gtk_window_realized_callback), (gpointer) this );
2891
2892 if (m_wxwindow)
2893 {
2894 // Catch native resize events
2895 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "size_allocate",
2896 GTK_SIGNAL_FUNC(gtk_window_size_callback), (gpointer)this );
2897
2898 // Initialize XIM support
2899 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "realize",
2900 GTK_SIGNAL_FUNC(gtk_wxwindow_realized_callback), (gpointer) this );
2901
2902 // And resize XIM window
2903 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "size_allocate",
2904 GTK_SIGNAL_FUNC(gtk_wxwindow_size_callback), (gpointer)this );
2905 }
2906
2907 if ( !GTK_IS_COMBO(m_widget))
2908 {
2909 // This is needed if we want to add our windows into native
2910 // GTK control, such as the toolbar. With this callback, the
2911 // toolbar gets to know the correct size (the one set by the
2912 // programmer). Sadly, it misbehaves for wxComboBox. FIXME
2913 // when moving to GTK 2.0.
2914 gtk_signal_connect( GTK_OBJECT(m_widget), "size_request",
2915 GTK_SIGNAL_FUNC(wxgtk_window_size_request_callback),
2916 (gpointer) this );
2917 }
2918
2919 m_hasVMT = TRUE;
2920 }
2921
2922 void wxWindowGTK::ConnectWidget( GtkWidget *widget )
2923 {
2924 gtk_signal_connect( GTK_OBJECT(widget), "key_press_event",
2925 GTK_SIGNAL_FUNC(gtk_window_key_press_callback), (gpointer)this );
2926
2927 gtk_signal_connect( GTK_OBJECT(widget), "key_release_event",
2928 GTK_SIGNAL_FUNC(gtk_window_key_release_callback), (gpointer)this );
2929
2930 gtk_signal_connect( GTK_OBJECT(widget), "button_press_event",
2931 GTK_SIGNAL_FUNC(gtk_window_button_press_callback), (gpointer)this );
2932
2933 gtk_signal_connect( GTK_OBJECT(widget), "button_release_event",
2934 GTK_SIGNAL_FUNC(gtk_window_button_release_callback), (gpointer)this );
2935
2936 gtk_signal_connect( GTK_OBJECT(widget), "motion_notify_event",
2937 GTK_SIGNAL_FUNC(gtk_window_motion_notify_callback), (gpointer)this );
2938
2939 #ifdef __WXGTK20__
2940 gtk_signal_connect( GTK_OBJECT(widget), "scroll_event",
2941 GTK_SIGNAL_FUNC(gtk_window_wheel_callback), (gpointer)this );
2942 #endif
2943
2944 gtk_signal_connect( GTK_OBJECT(widget), "enter_notify_event",
2945 GTK_SIGNAL_FUNC(gtk_window_enter_callback), (gpointer)this );
2946
2947 gtk_signal_connect( GTK_OBJECT(widget), "leave_notify_event",
2948 GTK_SIGNAL_FUNC(gtk_window_leave_callback), (gpointer)this );
2949 }
2950
2951 bool wxWindowGTK::Destroy()
2952 {
2953 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2954
2955 m_hasVMT = FALSE;
2956
2957 return wxWindowBase::Destroy();
2958 }
2959
2960 void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
2961 {
2962 gtk_pizza_set_size( GTK_PIZZA(m_parent->m_wxwindow), m_widget, x, y, width, height );
2963 }
2964
2965 void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
2966 {
2967 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2968 wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") );
2969
2970 /*
2971 printf( "DoSetSize: name %s, x,y,w,h: %d,%d,%d,%d \n", GetName().c_str(), x,y,width,height );
2972 */
2973
2974 if (m_resizing) return; /* I don't like recursions */
2975 m_resizing = TRUE;
2976
2977 int currentX, currentY;
2978 GetPosition(&currentX, &currentY);
2979 if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2980 x = currentX;
2981 if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2982 y = currentY;
2983 AdjustForParentClientOrigin(x, y, sizeFlags);
2984
2985 if (m_parent->m_wxwindow == NULL) /* i.e. wxNotebook */
2986 {
2987 /* don't set the size for children of wxNotebook, just take the values. */
2988 m_x = x;
2989 m_y = y;
2990 m_width = width;
2991 m_height = height;
2992 }
2993 else
2994 {
2995 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
2996 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
2997 {
2998 if (x != -1) m_x = x + pizza->xoffset;
2999 if (y != -1) m_y = y + pizza->yoffset;
3000 }
3001 else
3002 {
3003 m_x = x + pizza->xoffset;
3004 m_y = y + pizza->yoffset;
3005 }
3006 if (width != -1) m_width = width;
3007 if (height != -1) m_height = height;
3008
3009 if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH)
3010 {
3011 if (width == -1) m_width = 80;
3012 }
3013
3014 if ((sizeFlags & wxSIZE_AUTO_HEIGHT) == wxSIZE_AUTO_HEIGHT)
3015 {
3016 if (height == -1) m_height = 26;
3017 }
3018
3019 int minWidth = GetMinWidth(),
3020 minHeight = GetMinHeight(),
3021 maxWidth = GetMaxWidth(),
3022 maxHeight = GetMaxHeight();
3023
3024 if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth;
3025 if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight;
3026 if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth;
3027 if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
3028
3029 int border = 0;
3030 int bottom_border = 0;
3031
3032 #ifndef __WXGTK20__
3033 if (GTK_WIDGET_CAN_DEFAULT(m_widget))
3034 {
3035 /* the default button has a border around it */
3036 border = 6;
3037 bottom_border = 5;
3038 }
3039 #endif
3040
3041 DoMoveWindow( m_x-border,
3042 m_y-border,
3043 m_width+2*border,
3044 m_height+border+bottom_border );
3045 }
3046
3047 if (m_hasScrolling)
3048 {
3049 /* Sometimes the client area changes size without the
3050 whole windows's size changing, but if the whole
3051 windows's size doesn't change, no wxSizeEvent will
3052 normally be sent. Here we add an extra test if
3053 the client test has been changed and this will
3054 be used then. */
3055 GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
3056 }
3057
3058 /*
3059 wxPrintf( "OnSize sent from " );
3060 if (GetClassInfo() && GetClassInfo()->GetClassName())
3061 wxPrintf( GetClassInfo()->GetClassName() );
3062 wxPrintf( " %d %d %d %d\n", (int)m_x, (int)m_y, (int)m_width, (int)m_height );
3063 */
3064
3065 if (!m_nativeSizeEvent)
3066 {
3067 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
3068 event.SetEventObject( this );
3069 GetEventHandler()->ProcessEvent( event );
3070 }
3071
3072 m_resizing = FALSE;
3073 }
3074
3075 void wxWindowGTK::OnInternalIdle()
3076 {
3077 // Update invalidated regions.
3078 GtkUpdate();
3079
3080 // Synthetize activate events.
3081 if ( g_sendActivateEvent != -1 )
3082 {
3083 bool activate = g_sendActivateEvent != 0;
3084
3085 // do it only once
3086 g_sendActivateEvent = -1;
3087
3088 wxTheApp->SetActive(activate, (wxWindow *)g_focusWindowLast);
3089 }
3090
3091 if ( g_activeFrameLostFocus )
3092 {
3093 if ( g_activeFrame )
3094 {
3095 wxLogTrace(wxT("activate"), wxT("Deactivating frame %p (from idle)"), g_activeFrame);
3096 wxActivateEvent event(wxEVT_ACTIVATE, FALSE, g_activeFrame->GetId());
3097 event.SetEventObject(g_activeFrame);
3098 g_activeFrame->GetEventHandler()->ProcessEvent(event);
3099 g_activeFrame = NULL;
3100 }
3101 g_activeFrameLostFocus = FALSE;
3102 }
3103
3104 wxCursor cursor = m_cursor;
3105 if (g_globalCursor.Ok()) cursor = g_globalCursor;
3106
3107 if (cursor.Ok())
3108 {
3109 /* I now set the cursor anew in every OnInternalIdle call
3110 as setting the cursor in a parent window also effects the
3111 windows above so that checking for the current cursor is
3112 not possible. */
3113
3114 if (m_wxwindow)
3115 {
3116 GdkWindow *window = GTK_PIZZA(m_wxwindow)->bin_window;
3117 if (window)
3118 gdk_window_set_cursor( window, cursor.GetCursor() );
3119
3120 if (!g_globalCursor.Ok())
3121 cursor = *wxSTANDARD_CURSOR;
3122
3123 window = m_widget->window;
3124 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
3125 gdk_window_set_cursor( window, cursor.GetCursor() );
3126
3127 }
3128 else
3129 {
3130
3131 GdkWindow *window = m_widget->window;
3132 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
3133 gdk_window_set_cursor( window, cursor.GetCursor() );
3134
3135 }
3136 }
3137
3138 if (wxUpdateUIEvent::CanUpdate(this))
3139 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
3140 }
3141
3142 void wxWindowGTK::DoGetSize( int *width, int *height ) const
3143 {
3144 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3145
3146 if (width) (*width) = m_width;
3147 if (height) (*height) = m_height;
3148 }
3149
3150 void wxWindowGTK::DoSetClientSize( int width, int height )
3151 {
3152 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3153
3154 if (!m_wxwindow)
3155 {
3156 SetSize( width, height );
3157 }
3158 else
3159 {
3160 int dw = 0;
3161 int dh = 0;
3162
3163 #ifndef __WXUNIVERSAL__
3164 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3165 {
3166 /* when using GTK 1.2 we set the shadow border size to 2 */
3167 dw += 2 * 2;
3168 dh += 2 * 2;
3169 }
3170 if (HasFlag(wxSIMPLE_BORDER))
3171 {
3172 /* when using GTK 1.2 we set the simple border size to 1 */
3173 dw += 1 * 2;
3174 dh += 1 * 2;
3175 }
3176 #endif // __WXUNIVERSAL__
3177
3178 if (m_hasScrolling)
3179 {
3180 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3181
3182 GtkRequisition vscroll_req;
3183 vscroll_req.width = 2;
3184 vscroll_req.height = 2;
3185 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3186 (scroll_window->vscrollbar, &vscroll_req );
3187
3188 GtkRequisition hscroll_req;
3189 hscroll_req.width = 2;
3190 hscroll_req.height = 2;
3191 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3192 (scroll_window->hscrollbar, &hscroll_req );
3193
3194 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3195
3196 if (scroll_window->vscrollbar_visible)
3197 {
3198 dw += vscroll_req.width;
3199 dw += scroll_class->scrollbar_spacing;
3200 }
3201
3202 if (scroll_window->hscrollbar_visible)
3203 {
3204 dh += hscroll_req.height;
3205 dh += scroll_class->scrollbar_spacing;
3206 }
3207 }
3208
3209 SetSize( width+dw, height+dh );
3210 }
3211 }
3212
3213 void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
3214 {
3215 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3216
3217 if (!m_wxwindow)
3218 {
3219 if (width) (*width) = m_width;
3220 if (height) (*height) = m_height;
3221 }
3222 else
3223 {
3224 int dw = 0;
3225 int dh = 0;
3226
3227 #ifndef __WXUNIVERSAL__
3228 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3229 {
3230 /* when using GTK 1.2 we set the shadow border size to 2 */
3231 dw += 2 * 2;
3232 dh += 2 * 2;
3233 }
3234 if (HasFlag(wxSIMPLE_BORDER))
3235 {
3236 /* when using GTK 1.2 we set the simple border size to 1 */
3237 dw += 1 * 2;
3238 dh += 1 * 2;
3239 }
3240 #endif // __WXUNIVERSAL__
3241
3242 if (m_hasScrolling)
3243 {
3244 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3245
3246 GtkRequisition vscroll_req;
3247 vscroll_req.width = 2;
3248 vscroll_req.height = 2;
3249 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3250 (scroll_window->vscrollbar, &vscroll_req );
3251
3252 GtkRequisition hscroll_req;
3253 hscroll_req.width = 2;
3254 hscroll_req.height = 2;
3255 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3256 (scroll_window->hscrollbar, &hscroll_req );
3257
3258 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3259
3260 if (scroll_window->vscrollbar_visible)
3261 {
3262 dw += vscroll_req.width;
3263 dw += scroll_class->scrollbar_spacing;
3264 }
3265
3266 if (scroll_window->hscrollbar_visible)
3267 {
3268 dh += hscroll_req.height;
3269 dh += scroll_class->scrollbar_spacing;
3270 }
3271 }
3272
3273 if (width) (*width) = m_width - dw;
3274 if (height) (*height) = m_height - dh;
3275 }
3276
3277 /*
3278 printf( "GetClientSize, name %s ", GetName().c_str() );
3279 if (width) printf( " width = %d", (*width) );
3280 if (height) printf( " height = %d", (*height) );
3281 printf( "\n" );
3282 */
3283 }
3284
3285 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
3286 {
3287 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3288
3289 int dx = 0;
3290 int dy = 0;
3291 if (m_parent && m_parent->m_wxwindow)
3292 {
3293 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
3294 dx = pizza->xoffset;
3295 dy = pizza->yoffset;
3296 }
3297
3298 if (x) (*x) = m_x - dx;
3299 if (y) (*y) = m_y - dy;
3300 }
3301
3302 void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
3303 {
3304 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3305
3306 if (!m_widget->window) return;
3307
3308 GdkWindow *source = (GdkWindow *) NULL;
3309 if (m_wxwindow)
3310 source = GTK_PIZZA(m_wxwindow)->bin_window;
3311 else
3312 source = m_widget->window;
3313
3314 int org_x = 0;
3315 int org_y = 0;
3316 gdk_window_get_origin( source, &org_x, &org_y );
3317
3318 if (!m_wxwindow)
3319 {
3320 if (GTK_WIDGET_NO_WINDOW (m_widget))
3321 {
3322 org_x += m_widget->allocation.x;
3323 org_y += m_widget->allocation.y;
3324 }
3325 }
3326
3327 if (x) *x += org_x;
3328 if (y) *y += org_y;
3329 }
3330
3331 void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
3332 {
3333 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3334
3335 if (!m_widget->window) return;
3336
3337 GdkWindow *source = (GdkWindow *) NULL;
3338 if (m_wxwindow)
3339 source = GTK_PIZZA(m_wxwindow)->bin_window;
3340 else
3341 source = m_widget->window;
3342
3343 int org_x = 0;
3344 int org_y = 0;
3345 gdk_window_get_origin( source, &org_x, &org_y );
3346
3347 if (!m_wxwindow)
3348 {
3349 if (GTK_WIDGET_NO_WINDOW (m_widget))
3350 {
3351 org_x += m_widget->allocation.x;
3352 org_y += m_widget->allocation.y;
3353 }
3354 }
3355
3356 if (x) *x -= org_x;
3357 if (y) *y -= org_y;
3358 }
3359
3360 bool wxWindowGTK::Show( bool show )
3361 {
3362 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3363
3364 if (!wxWindowBase::Show(show))
3365 {
3366 // nothing to do
3367 return FALSE;
3368 }
3369
3370 if (show)
3371 gtk_widget_show( m_widget );
3372 else
3373 gtk_widget_hide( m_widget );
3374
3375 wxShowEvent eventShow(GetId(), show);
3376 eventShow.m_eventObject = this;
3377
3378 GetEventHandler()->ProcessEvent(eventShow);
3379
3380 return TRUE;
3381 }
3382
3383 static void wxWindowNotifyEnable(wxWindowGTK* win, bool enable)
3384 {
3385 win->OnParentEnable(enable);
3386
3387 // Recurse, so that children have the opportunity to Do The Right Thing
3388 // and reset colours that have been messed up by a parent's (really ancestor's)
3389 // Enable call
3390 for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
3391 node;
3392 node = node->GetNext() )
3393 {
3394 wxWindow *child = node->GetData();
3395 if (!child->IsKindOf(CLASSINFO(wxDialog)) && !child->IsKindOf(CLASSINFO(wxFrame)))
3396 wxWindowNotifyEnable(child, enable);
3397 }
3398 }
3399
3400 bool wxWindowGTK::Enable( bool enable )
3401 {
3402 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3403
3404 if (!wxWindowBase::Enable(enable))
3405 {
3406 // nothing to do
3407 return FALSE;
3408 }
3409
3410 gtk_widget_set_sensitive( m_widget, enable );
3411 if ( m_wxwindow )
3412 gtk_widget_set_sensitive( m_wxwindow, enable );
3413
3414 wxWindowNotifyEnable(this, enable);
3415
3416 return TRUE;
3417 }
3418
3419 int wxWindowGTK::GetCharHeight() const
3420 {
3421 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
3422
3423 wxCHECK_MSG( m_font.Ok(), 12, wxT("invalid font") );
3424
3425 #ifdef __WXGTK20__
3426 PangoContext *context = NULL;
3427 if (m_widget)
3428 context = gtk_widget_get_pango_context( m_widget );
3429
3430 if (!context)
3431 return 0;
3432
3433 PangoFontDescription *desc = m_font.GetNativeFontInfo()->description;
3434 PangoLayout *layout = pango_layout_new(context);
3435 pango_layout_set_font_description(layout, desc);
3436 pango_layout_set_text(layout, "H", 1);
3437 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3438
3439 PangoRectangle rect;
3440 pango_layout_line_get_extents(line, NULL, &rect);
3441
3442 g_object_unref( G_OBJECT( layout ) );
3443
3444 return (int) (rect.height / PANGO_SCALE);
3445 #else
3446 GdkFont *font = m_font.GetInternalFont( 1.0 );
3447
3448 return font->ascent + font->descent;
3449 #endif
3450 }
3451
3452 int wxWindowGTK::GetCharWidth() const
3453 {
3454 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
3455
3456 wxCHECK_MSG( m_font.Ok(), 8, wxT("invalid font") );
3457
3458 #ifdef __WXGTK20__
3459 PangoContext *context = NULL;
3460 if (m_widget)
3461 context = gtk_widget_get_pango_context( m_widget );
3462
3463 if (!context)
3464 return 0;
3465
3466 PangoFontDescription *desc = m_font.GetNativeFontInfo()->description;
3467 PangoLayout *layout = pango_layout_new(context);
3468 pango_layout_set_font_description(layout, desc);
3469 pango_layout_set_text(layout, "g", 1);
3470 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3471
3472 PangoRectangle rect;
3473 pango_layout_line_get_extents(line, NULL, &rect);
3474
3475 g_object_unref( G_OBJECT( layout ) );
3476
3477 return (int) (rect.width / PANGO_SCALE);
3478 #else
3479 GdkFont *font = m_font.GetInternalFont( 1.0 );
3480
3481 return gdk_string_width( font, "g" );
3482 #endif
3483 }
3484
3485 void wxWindowGTK::GetTextExtent( const wxString& string,
3486 int *x,
3487 int *y,
3488 int *descent,
3489 int *externalLeading,
3490 const wxFont *theFont ) const
3491 {
3492 wxFont fontToUse = m_font;
3493 if (theFont) fontToUse = *theFont;
3494
3495 wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") );
3496
3497 if (string.IsEmpty())
3498 {
3499 if (x) (*x) = 0;
3500 if (y) (*y) = 0;
3501 return;
3502 }
3503
3504 #ifdef __WXGTK20__
3505 PangoContext *context = NULL;
3506 if (m_widget)
3507 context = gtk_widget_get_pango_context( m_widget );
3508
3509 if (!context)
3510 {
3511 if (x) (*x) = 0;
3512 if (y) (*y) = 0;
3513 return;
3514 }
3515
3516 PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
3517 PangoLayout *layout = pango_layout_new(context);
3518 pango_layout_set_font_description(layout, desc);
3519 {
3520 #if wxUSE_UNICODE
3521 const wxCharBuffer data = wxConvUTF8.cWC2MB( string );
3522 pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data ));
3523 #else
3524 const wxWCharBuffer wdata = wxConvLocal.cMB2WC( string );
3525 const wxCharBuffer data = wxConvUTF8.cWC2MB( wdata );
3526 pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data ));
3527 #endif
3528 }
3529 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3530
3531 PangoRectangle rect;
3532 pango_layout_line_get_extents(line, NULL, &rect);
3533
3534 if (x) (*x) = (wxCoord) (rect.width / PANGO_SCALE);
3535 if (y) (*y) = (wxCoord) (rect.height / PANGO_SCALE);
3536 if (descent)
3537 {
3538 // Do something about metrics here
3539 (*descent) = 0;
3540 }
3541 if (externalLeading) (*externalLeading) = 0; // ??
3542
3543 g_object_unref( G_OBJECT( layout ) );
3544 #else
3545 GdkFont *font = fontToUse.GetInternalFont( 1.0 );
3546 if (x) (*x) = gdk_string_width( font, wxGTK_CONV( string ) );
3547 if (y) (*y) = font->ascent + font->descent;
3548 if (descent) (*descent) = font->descent;
3549 if (externalLeading) (*externalLeading) = 0; // ??
3550 #endif
3551 }
3552
3553 void wxWindowGTK::SetFocus()
3554 {
3555 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3556
3557 if ( m_hasFocus )
3558 {
3559 // don't do anything if we already have focus
3560 return;
3561 }
3562
3563 if (m_wxwindow)
3564 {
3565 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
3566 {
3567 gtk_widget_grab_focus (m_wxwindow);
3568 }
3569 }
3570 else if (m_widget)
3571 {
3572 if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
3573 {
3574 if (!GTK_WIDGET_REALIZED(m_widget))
3575 {
3576 // we can't set the focus to the widget now so we remember that
3577 // it should be focused and will do it later, during the idle
3578 // time, as soon as we can
3579 wxLogTrace(TRACE_FOCUS,
3580 _T("Delaying setting focus to %s(%s)"),
3581 GetClassInfo()->GetClassName(), GetLabel().c_str());
3582
3583 g_delayedFocus = this;
3584 }
3585 else
3586 {
3587 wxLogTrace(TRACE_FOCUS,
3588 _T("Setting focus to %s(%s)"),
3589 GetClassInfo()->GetClassName(), GetLabel().c_str());
3590
3591 gtk_widget_grab_focus (m_widget);
3592 }
3593 }
3594 else if (GTK_IS_CONTAINER(m_widget))
3595 {
3596 SET_CONTAINER_FOCUS( m_widget, GTK_DIR_TAB_FORWARD );
3597 }
3598 else
3599 {
3600 wxLogTrace(TRACE_FOCUS,
3601 _T("Can't set focus to %s(%s)"),
3602 GetClassInfo()->GetClassName(), GetLabel().c_str());
3603 }
3604 }
3605 }
3606
3607 bool wxWindowGTK::AcceptsFocus() const
3608 {
3609 return m_acceptsFocus && wxWindowBase::AcceptsFocus();
3610 }
3611
3612 bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
3613 {
3614 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3615
3616 wxWindowGTK *oldParent = m_parent,
3617 *newParent = (wxWindowGTK *)newParentBase;
3618
3619 wxASSERT( GTK_IS_WIDGET(m_widget) );
3620
3621 if ( !wxWindowBase::Reparent(newParent) )
3622 return FALSE;
3623
3624 wxASSERT( GTK_IS_WIDGET(m_widget) );
3625
3626 /* prevent GTK from deleting the widget arbitrarily */
3627 gtk_widget_ref( m_widget );
3628
3629 if (oldParent)
3630 {
3631 gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
3632 }
3633
3634 wxASSERT( GTK_IS_WIDGET(m_widget) );
3635
3636 if (newParent)
3637 {
3638 /* insert GTK representation */
3639 (*(newParent->m_insertCallback))(newParent, this);
3640 }
3641
3642 /* reverse: prevent GTK from deleting the widget arbitrarily */
3643 gtk_widget_unref( m_widget );
3644
3645 return TRUE;
3646 }
3647
3648 void wxWindowGTK::DoAddChild(wxWindowGTK *child)
3649 {
3650 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
3651
3652 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
3653
3654 wxASSERT_MSG( (m_insertCallback != NULL), wxT("invalid child insertion function") );
3655
3656 /* add to list */
3657 AddChild( child );
3658
3659 /* insert GTK representation */
3660 (*m_insertCallback)(this, child);
3661 }
3662
3663 void wxWindowGTK::Raise()
3664 {
3665 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3666
3667 if (!m_widget->window) return;
3668
3669 gdk_window_raise( m_widget->window );
3670 }
3671
3672 void wxWindowGTK::Lower()
3673 {
3674 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3675
3676 if (!m_widget->window) return;
3677
3678 gdk_window_lower( m_widget->window );
3679 }
3680
3681 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
3682 {
3683 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3684
3685 if (cursor == m_cursor)
3686 return FALSE;
3687
3688 if (g_isIdle)
3689 wxapp_install_idle_handler();
3690
3691 if (cursor == wxNullCursor)
3692 return wxWindowBase::SetCursor( *wxSTANDARD_CURSOR );
3693 else
3694 return wxWindowBase::SetCursor( cursor );
3695 }
3696
3697 void wxWindowGTK::WarpPointer( int x, int y )
3698 {
3699 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3700
3701 // We provide this function ourselves as it is
3702 // missing in GDK (top of this file).
3703
3704 GdkWindow *window = (GdkWindow*) NULL;
3705 if (m_wxwindow)
3706 window = GTK_PIZZA(m_wxwindow)->bin_window;
3707 else
3708 window = GetConnectWidget()->window;
3709
3710 if (window)
3711 gdk_window_warp_pointer( window, x, y );
3712 }
3713
3714
3715 void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
3716 {
3717 if (!m_widget) return;
3718 if (!m_widget->window) return;
3719
3720 #ifndef __WXGTK20__
3721 if (g_isIdle)
3722 wxapp_install_idle_handler();
3723
3724 wxRect myRect(0,0,0,0);
3725 if (m_wxwindow && rect)
3726 {
3727 myRect.SetSize(wxSize( m_wxwindow->allocation.width,
3728 m_wxwindow->allocation.height));
3729 myRect.Intersect(*rect);
3730 if (!myRect.width || !myRect.height)
3731 // nothing to do, rectangle is empty
3732 return;
3733 rect = &myRect;
3734 }
3735
3736 if (eraseBackground && m_wxwindow && m_wxwindow->window)
3737 {
3738 if (rect)
3739 {
3740 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3741 m_clearRegion.Union( rect->x, rect->y, rect->width, rect->height );
3742 }
3743 else
3744 {
3745 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3746 m_clearRegion.Clear();
3747 m_clearRegion.Union( 0, 0, m_wxwindow->allocation.width, m_wxwindow->allocation.height );
3748 }
3749 }
3750
3751 if (rect)
3752 {
3753 if (m_wxwindow)
3754 {
3755 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3756 m_updateRegion.Union( rect->x, rect->y, rect->width, rect->height );
3757 }
3758 else
3759 {
3760 GdkRectangle gdk_rect;
3761 gdk_rect.x = rect->x;
3762 gdk_rect.y = rect->y;
3763 gdk_rect.width = rect->width;
3764 gdk_rect.height = rect->height;
3765 gtk_widget_draw( m_widget, &gdk_rect );
3766 }
3767 }
3768 else
3769 {
3770 if (m_wxwindow)
3771 {
3772 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3773 m_updateRegion.Clear();
3774 m_updateRegion.Union( 0, 0, m_wxwindow->allocation.width, m_wxwindow->allocation.height );
3775 }
3776 else
3777 {
3778 gtk_widget_draw( m_widget, (GdkRectangle*) NULL );
3779 }
3780 }
3781 #else
3782 if (m_wxwindow)
3783 {
3784 if (rect)
3785 {
3786 GdkRectangle gdk_rect;
3787 gdk_rect.x = rect->x;
3788 gdk_rect.y = rect->y;
3789 gdk_rect.width = rect->width;
3790 gdk_rect.height = rect->height;
3791 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, &gdk_rect, TRUE );
3792 }
3793 else
3794 {
3795 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, NULL, TRUE );
3796 }
3797 }
3798 #endif
3799 }
3800
3801 void wxWindowGTK::Update()
3802 {
3803 GtkUpdate();
3804 }
3805
3806 void wxWindowGTK::GtkUpdate()
3807 {
3808 #ifdef __WXGTK20__
3809 if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window)
3810 gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE );
3811 #else
3812 if (!m_updateRegion.IsEmpty())
3813 GtkSendPaintEvents();
3814 #endif
3815 }
3816
3817 void wxWindowGTK::GtkSendPaintEvents()
3818 {
3819 if (!m_wxwindow)
3820 {
3821 #ifndef __WXGTK20__
3822 m_clearRegion.Clear();
3823 #endif
3824 m_updateRegion.Clear();
3825 return;
3826 }
3827
3828 // Clip to paint region in wxClientDC
3829 m_clipPaintRegion = TRUE;
3830
3831 #ifndef __WXGTK20__
3832 // widget to draw on
3833 GtkPizza *pizza = GTK_PIZZA (m_wxwindow);
3834
3835 // later for GTK 2.0, too.
3836 if (GetThemeEnabled())
3837 {
3838 // find ancestor from which to steal background
3839 wxWindow *parent = GetParent();
3840 while (parent && !parent->IsTopLevel())
3841 parent = parent->GetParent();
3842 if (!parent)
3843 parent = (wxWindow*)this;
3844
3845 wxRegionIterator upd( m_updateRegion );
3846 while (upd)
3847 {
3848 GdkRectangle rect;
3849 rect.x = upd.GetX();
3850 rect.y = upd.GetY();
3851 rect.width = upd.GetWidth();
3852 rect.height = upd.GetHeight();
3853
3854 gtk_paint_flat_box( parent->m_widget->style,
3855 pizza->bin_window,
3856 GTK_STATE_NORMAL,
3857 GTK_SHADOW_NONE,
3858 &rect,
3859 parent->m_widget,
3860 (char *)"base",
3861 0, 0, -1, -1 );
3862
3863 upd ++;
3864 }
3865 }
3866 else
3867 #endif
3868
3869 #ifdef __WXGTK20__
3870 {
3871 wxWindowDC dc( (wxWindow*)this );
3872 dc.SetClippingRegion( m_updateRegion );
3873
3874 wxEraseEvent erase_event( GetId(), &dc );
3875 erase_event.SetEventObject( this );
3876
3877 GetEventHandler()->ProcessEvent(erase_event);
3878 }
3879 #else
3880 // if (!m_clearRegion.IsEmpty()) // Always send an erase event under GTK 1.2
3881 {
3882 wxWindowDC dc( (wxWindow*)this );
3883 if (m_clearRegion.IsEmpty())
3884 dc.SetClippingRegion( m_updateRegion );
3885 else
3886 dc.SetClippingRegion( m_clearRegion );
3887
3888 wxEraseEvent erase_event( GetId(), &dc );
3889 erase_event.SetEventObject( this );
3890
3891 if (!GetEventHandler()->ProcessEvent(erase_event))
3892 {
3893 if (!g_eraseGC)
3894 {
3895 g_eraseGC = gdk_gc_new( pizza->bin_window );
3896 gdk_gc_set_fill( g_eraseGC, GDK_SOLID );
3897 }
3898 gdk_gc_set_foreground( g_eraseGC, m_backgroundColour.GetColor() );
3899
3900 wxRegionIterator upd( m_clearRegion );
3901 while (upd)
3902 {
3903 gdk_draw_rectangle( pizza->bin_window, g_eraseGC, 1,
3904 upd.GetX(), upd.GetY(), upd.GetWidth(), upd.GetHeight() );
3905 upd ++;
3906 }
3907 }
3908 m_clearRegion.Clear();
3909 }
3910 #endif
3911
3912 wxNcPaintEvent nc_paint_event( GetId() );
3913 nc_paint_event.SetEventObject( this );
3914 GetEventHandler()->ProcessEvent( nc_paint_event );
3915
3916 wxPaintEvent paint_event( GetId() );
3917 paint_event.SetEventObject( this );
3918 GetEventHandler()->ProcessEvent( paint_event );
3919
3920 m_clipPaintRegion = FALSE;
3921
3922 #ifndef __WXUNIVERSAL__
3923 #ifndef __WXGTK20__
3924 // The following code will result in all window-less widgets
3925 // being redrawn because the wxWindows class is allowed to
3926 // paint over the window-less widgets.
3927
3928 GList *children = pizza->children;
3929 while (children)
3930 {
3931 GtkPizzaChild *child = (GtkPizzaChild*) children->data;
3932 children = children->next;
3933
3934 if (GTK_WIDGET_NO_WINDOW (child->widget) &&
3935 GTK_WIDGET_DRAWABLE (child->widget))
3936 {
3937 // Get intersection of widget area and update region
3938 wxRegion region( m_updateRegion );
3939
3940 GdkEventExpose gdk_event;
3941 gdk_event.type = GDK_EXPOSE;
3942 gdk_event.window = pizza->bin_window;
3943 gdk_event.count = 0;
3944
3945 wxRegionIterator upd( m_updateRegion );
3946 while (upd)
3947 {
3948 GdkRectangle rect;
3949 rect.x = upd.GetX();
3950 rect.y = upd.GetY();
3951 rect.width = upd.GetWidth();
3952 rect.height = upd.GetHeight();
3953
3954 if (gtk_widget_intersect (child->widget, &rect, &gdk_event.area))
3955 {
3956 gtk_widget_event (child->widget, (GdkEvent*) &gdk_event);
3957 }
3958
3959 upd ++;
3960 }
3961 }
3962 }
3963 #endif
3964 #endif
3965
3966 m_updateRegion.Clear();
3967 }
3968
3969 void wxWindowGTK::ClearBackground()
3970 {
3971 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3972
3973 #ifndef __WXGTK20__
3974 if (m_wxwindow && m_wxwindow->window)
3975 {
3976 m_clearRegion.Clear();
3977 wxSize size( GetClientSize() );
3978 m_clearRegion.Union( 0,0,size.x,size.y );
3979
3980 // Better do this in idle?
3981 GtkUpdate();
3982 }
3983 #endif
3984 }
3985
3986 #if wxUSE_TOOLTIPS
3987 void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
3988 {
3989 wxWindowBase::DoSetToolTip(tip);
3990
3991 if (m_tooltip)
3992 m_tooltip->Apply( (wxWindow *)this );
3993 }
3994
3995 void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
3996 {
3997 gtk_tooltips_set_tip( tips, GetConnectWidget(), wxConvCurrent->cWX2MB(tip), (gchar*) NULL );
3998 }
3999 #endif // wxUSE_TOOLTIPS
4000
4001 void wxWindowGTK::GtkSetBackgroundColour( const wxColour &colour )
4002 {
4003 GdkWindow *window = (GdkWindow*) NULL;
4004 if (m_wxwindow)
4005 window = GTK_PIZZA(m_wxwindow)->bin_window;
4006 else
4007 window = GetConnectWidget()->window;
4008
4009 wxASSERT( window );
4010
4011 // We need the pixel value e.g. for background clearing.
4012 m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
4013
4014 if (m_wxwindow)
4015 {
4016 // wxMSW doesn't clear the window here, either.
4017 gdk_window_set_background( window, m_backgroundColour.GetColor() );
4018 }
4019
4020 ApplyWidgetStyle();
4021 }
4022
4023 bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
4024 {
4025 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
4026
4027 if (!wxWindowBase::SetBackgroundColour(colour))
4028 return FALSE;
4029
4030 GdkWindow *window = (GdkWindow*) NULL;
4031 if (m_wxwindow)
4032 window = GTK_PIZZA(m_wxwindow)->bin_window;
4033 else
4034 window = GetConnectWidget()->window;
4035
4036 if (!window)
4037 {
4038 // indicate that a new style has been set
4039 // but it couldn't get applied as the
4040 // widget hasn't been realized yet.
4041 m_delayedBackgroundColour = TRUE;
4042 return TRUE;
4043 }
4044 else
4045 {
4046 GtkSetBackgroundColour( colour );
4047 }
4048
4049 return TRUE;
4050 }
4051
4052 void wxWindowGTK::GtkSetForegroundColour( const wxColour &colour )
4053 {
4054 GdkWindow *window = (GdkWindow*) NULL;
4055 if (m_wxwindow)
4056 window = GTK_PIZZA(m_wxwindow)->bin_window;
4057 else
4058 window = GetConnectWidget()->window;
4059
4060 wxASSERT( window );
4061
4062 ApplyWidgetStyle();
4063 }
4064
4065 bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
4066 {
4067 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
4068
4069 if (!wxWindowBase::SetForegroundColour(colour))
4070 {
4071 // don't leave if the GTK widget has just
4072 // been realized
4073 if (!m_delayedForegroundColour) return FALSE;
4074 }
4075
4076 GdkWindow *window = (GdkWindow*) NULL;
4077 if (m_wxwindow)
4078 window = GTK_PIZZA(m_wxwindow)->bin_window;
4079 else
4080 window = GetConnectWidget()->window;
4081
4082 if (!window)
4083 {
4084 // indicate that a new style has been set
4085 // but it couldn't get applied as the
4086 // widget hasn't been realized yet.
4087 m_delayedForegroundColour = TRUE;
4088 }
4089 else
4090 {
4091 GtkSetForegroundColour( colour );
4092 }
4093
4094 return TRUE;
4095 }
4096
4097 #ifdef __WXGTK20__
4098 PangoContext *wxWindowGTK::GtkGetPangoDefaultContext()
4099 {
4100 return gtk_widget_get_pango_context( m_widget );
4101 }
4102
4103 PangoContext *wxWindowGTK::GtkGetPangoX11Context()
4104 {
4105 if (!m_x11Context)
4106 m_x11Context = pango_x_get_context( gdk_display );
4107
4108 return m_x11Context;
4109 }
4110 #endif
4111
4112 GtkStyle *wxWindowGTK::GetWidgetStyle()
4113 {
4114 if (m_widgetStyle)
4115 {
4116 GtkStyle *remake = gtk_style_copy( m_widgetStyle );
4117
4118 // FIXME: no more klass in 2.0
4119 #ifndef __WXGTK20__
4120 remake->klass = m_widgetStyle->klass;
4121 #endif
4122
4123 gtk_style_unref( m_widgetStyle );
4124 m_widgetStyle = remake;
4125 }
4126 else
4127 {
4128 GtkStyle *def = gtk_rc_get_style( m_widget );
4129
4130 if (!def)
4131 def = gtk_widget_get_default_style();
4132
4133 m_widgetStyle = gtk_style_copy( def );
4134
4135 // FIXME: no more klass in 2.0
4136 #ifndef __WXGTK20__
4137 m_widgetStyle->klass = def->klass;
4138 #endif
4139 }
4140
4141 return m_widgetStyle;
4142 }
4143
4144 void wxWindowGTK::SetWidgetStyle()
4145 {
4146 #if DISABLE_STYLE_IF_BROKEN_THEME
4147 if (m_widget->style->engine_data)
4148 {
4149 static bool s_warningPrinted = FALSE;
4150 if (!s_warningPrinted)
4151 {
4152 printf( "wxWindows warning: Widget styles disabled due to buggy GTK theme.\n" );
4153 s_warningPrinted = TRUE;
4154 }
4155 m_widgetStyle = m_widget->style;
4156 return;
4157 }
4158 #endif
4159
4160 GtkStyle *style = GetWidgetStyle();
4161
4162 if (m_font != wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ))
4163 {
4164 #ifdef __WXGTK20__
4165 pango_font_description_free( style->font_desc );
4166 style->font_desc = pango_font_description_copy( m_font.GetNativeFontInfo()->description );
4167 #else
4168 gdk_font_unref( style->font );
4169 style->font = gdk_font_ref( m_font.GetInternalFont( 1.0 ) );
4170 #endif
4171 }
4172
4173 if (m_foregroundColour.Ok())
4174 {
4175 m_foregroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) );
4176 if (m_foregroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT))
4177 {
4178 style->fg[GTK_STATE_NORMAL] = *m_foregroundColour.GetColor();
4179 style->fg[GTK_STATE_PRELIGHT] = *m_foregroundColour.GetColor();
4180 style->fg[GTK_STATE_ACTIVE] = *m_foregroundColour.GetColor();
4181 }
4182 else
4183 {
4184 // Try to restore the gtk default style. This is still a little
4185 // oversimplified for what is probably really needed here for controls
4186 // other than buttons, but is better than not being able to (re)set a
4187 // control's foreground colour to *wxBLACK -- RL
4188 GtkStyle *def = gtk_rc_get_style( m_widget );
4189
4190 if (!def)
4191 def = gtk_widget_get_default_style();
4192
4193 style->fg[GTK_STATE_NORMAL] = def->fg[GTK_STATE_NORMAL];
4194 style->fg[GTK_STATE_PRELIGHT] = def->fg[GTK_STATE_PRELIGHT];
4195 style->fg[GTK_STATE_ACTIVE] = def->fg[GTK_STATE_ACTIVE];
4196 }
4197 }
4198
4199 if (m_backgroundColour.Ok())
4200 {
4201 m_backgroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) );
4202 if (m_backgroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE))
4203 {
4204 style->bg[GTK_STATE_NORMAL] = *m_backgroundColour.GetColor();
4205 style->base[GTK_STATE_NORMAL] = *m_backgroundColour.GetColor();
4206 style->bg[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor();
4207 style->base[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor();
4208 style->bg[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor();
4209 style->base[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor();
4210 style->bg[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor();
4211 style->base[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor();
4212 }
4213 else
4214 {
4215 // Try to restore the gtk default style. This is still a little
4216 // oversimplified for what is probably really needed here for controls
4217 // other than buttons, but is better than not being able to (re)set a
4218 // control's background colour to default grey and means resetting a
4219 // button to wxSYS_COLOUR_BTNFACE will restore its usual highlighting
4220 // behavior -- RL
4221 GtkStyle *def = gtk_rc_get_style( m_widget );
4222
4223 if (!def)
4224 def = gtk_widget_get_default_style();
4225
4226 style->bg[GTK_STATE_NORMAL] = def->bg[GTK_STATE_NORMAL];
4227 style->base[GTK_STATE_NORMAL] = def->base[GTK_STATE_NORMAL];
4228 style->bg[GTK_STATE_PRELIGHT] = def->bg[GTK_STATE_PRELIGHT];
4229 style->base[GTK_STATE_PRELIGHT] = def->base[GTK_STATE_PRELIGHT];
4230 style->bg[GTK_STATE_ACTIVE] = def->bg[GTK_STATE_ACTIVE];
4231 style->base[GTK_STATE_ACTIVE] = def->base[GTK_STATE_ACTIVE];
4232 style->bg[GTK_STATE_INSENSITIVE] = def->bg[GTK_STATE_INSENSITIVE];
4233 style->base[GTK_STATE_INSENSITIVE] = def->base[GTK_STATE_INSENSITIVE];
4234 }
4235 }
4236 }
4237
4238 void wxWindowGTK::ApplyWidgetStyle()
4239 {
4240 }
4241
4242 //-----------------------------------------------------------------------------
4243 // Pop-up menu stuff
4244 //-----------------------------------------------------------------------------
4245
4246 #if wxUSE_MENUS_NATIVE
4247
4248 extern "C"
4249 void gtk_pop_hide_callback( GtkWidget *WXUNUSED(widget), bool* is_waiting )
4250 {
4251 *is_waiting = FALSE;
4252 }
4253
4254 static void SetInvokingWindow( wxMenu *menu, wxWindowGTK *win )
4255 {
4256 menu->SetInvokingWindow( win );
4257 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
4258 while (node)
4259 {
4260 wxMenuItem *menuitem = node->GetData();
4261 if (menuitem->IsSubMenu())
4262 {
4263 SetInvokingWindow( menuitem->GetSubMenu(), win );
4264 }
4265
4266 node = node->GetNext();
4267 }
4268 }
4269
4270 // used to pass the coordinates from wxWindowGTK::DoPopupMenu() to
4271 // wxPopupMenuPositionCallback()
4272 //
4273 // should be safe even in the MT case as the user can hardly popup 2 menus
4274 // simultaneously, can he?
4275 static gint gs_pop_x = 0;
4276 static gint gs_pop_y = 0;
4277
4278 extern "C" void wxPopupMenuPositionCallback( GtkMenu *menu,
4279 gint *x, gint *y,
4280 #ifdef __WXGTK20__
4281 gboolean * WXUNUSED(whatever),
4282 #endif
4283 gpointer WXUNUSED(user_data) )
4284 {
4285 // ensure that the menu appears entirely on screen
4286 GtkRequisition req;
4287 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &req);
4288
4289 wxSize sizeScreen = wxGetDisplaySize();
4290
4291 gint xmax = sizeScreen.x - req.width,
4292 ymax = sizeScreen.y - req.height;
4293
4294 *x = gs_pop_x < xmax ? gs_pop_x : xmax;
4295 *y = gs_pop_y < ymax ? gs_pop_y : ymax;
4296 }
4297
4298 bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
4299 {
4300 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
4301
4302 wxCHECK_MSG( menu != NULL, FALSE, wxT("invalid popup-menu") );
4303
4304 SetInvokingWindow( menu, this );
4305
4306 menu->UpdateUI();
4307
4308 gs_pop_x = x;
4309 gs_pop_y = y;
4310 ClientToScreen( &gs_pop_x, &gs_pop_y );
4311
4312 bool is_waiting = TRUE;
4313
4314 gtk_signal_connect( GTK_OBJECT(menu->m_menu),
4315 "hide",
4316 GTK_SIGNAL_FUNC(gtk_pop_hide_callback),
4317 (gpointer)&is_waiting );
4318
4319 gtk_menu_popup(
4320 GTK_MENU(menu->m_menu),
4321 (GtkWidget *) NULL, // parent menu shell
4322 (GtkWidget *) NULL, // parent menu item
4323 wxPopupMenuPositionCallback, // function to position it
4324 NULL, // client data
4325 0, // button used to activate it
4326 #ifdef __WXGTK20__
4327 gtk_get_current_event_time()
4328 #else
4329 gs_timeLastClick // the time of activation
4330 #endif
4331 );
4332
4333 while (is_waiting)
4334 {
4335 gtk_main_iteration();
4336 }
4337
4338 return TRUE;
4339 }
4340
4341 #endif // wxUSE_MENUS_NATIVE
4342
4343 #if wxUSE_DRAG_AND_DROP
4344
4345 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
4346 {
4347 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4348
4349 GtkWidget *dnd_widget = GetConnectWidget();
4350
4351 if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
4352
4353 if (m_dropTarget) delete m_dropTarget;
4354 m_dropTarget = dropTarget;
4355
4356 if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
4357 }
4358
4359 #endif // wxUSE_DRAG_AND_DROP
4360
4361 GtkWidget* wxWindowGTK::GetConnectWidget()
4362 {
4363 GtkWidget *connect_widget = m_widget;
4364 if (m_wxwindow) connect_widget = m_wxwindow;
4365
4366 return connect_widget;
4367 }
4368
4369 bool wxWindowGTK::IsOwnGtkWindow( GdkWindow *window )
4370 {
4371 if (m_wxwindow)
4372 return (window == GTK_PIZZA(m_wxwindow)->bin_window);
4373
4374 return (window == m_widget->window);
4375 }
4376
4377 bool wxWindowGTK::SetFont( const wxFont &font )
4378 {
4379 if (!wxWindowBase::SetFont(font) || !m_widget)
4380 {
4381 return FALSE;
4382 }
4383
4384 wxColour sysbg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
4385 if ( sysbg == m_backgroundColour )
4386 {
4387 m_backgroundColour = wxNullColour;
4388 ApplyWidgetStyle();
4389 m_backgroundColour = sysbg;
4390 }
4391 else
4392 {
4393 ApplyWidgetStyle();
4394 }
4395
4396 return TRUE;
4397 }
4398
4399 void wxWindowGTK::DoCaptureMouse()
4400 {
4401 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4402
4403 GdkWindow *window = (GdkWindow*) NULL;
4404 if (m_wxwindow)
4405 window = GTK_PIZZA(m_wxwindow)->bin_window;
4406 else
4407 window = GetConnectWidget()->window;
4408
4409 wxCHECK_RET( window, _T("CaptureMouse() failed") );
4410
4411 wxCursor* cursor = & m_cursor;
4412 if (!cursor->Ok())
4413 cursor = wxSTANDARD_CURSOR;
4414
4415 gdk_pointer_grab( window, FALSE,
4416 (GdkEventMask)
4417 (GDK_BUTTON_PRESS_MASK |
4418 GDK_BUTTON_RELEASE_MASK |
4419 GDK_POINTER_MOTION_HINT_MASK |
4420 GDK_POINTER_MOTION_MASK),
4421 (GdkWindow *) NULL,
4422 cursor->GetCursor(),
4423 (guint32)GDK_CURRENT_TIME );
4424 g_captureWindow = this;
4425 g_captureWindowHasMouse = TRUE;
4426 }
4427
4428 void wxWindowGTK::DoReleaseMouse()
4429 {
4430 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4431
4432 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
4433
4434 g_captureWindow = (wxWindowGTK*) NULL;
4435
4436 GdkWindow *window = (GdkWindow*) NULL;
4437 if (m_wxwindow)
4438 window = GTK_PIZZA(m_wxwindow)->bin_window;
4439 else
4440 window = GetConnectWidget()->window;
4441
4442 if (!window)
4443 return;
4444
4445 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
4446 }
4447
4448 /* static */
4449 wxWindow *wxWindowBase::GetCapture()
4450 {
4451 return (wxWindow *)g_captureWindow;
4452 }
4453
4454 bool wxWindowGTK::IsRetained() const
4455 {
4456 return FALSE;
4457 }
4458
4459 void wxWindowGTK::SetScrollbar( int orient, int pos, int thumbVisible,
4460 int range, bool refresh )
4461 {
4462 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4463
4464 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4465
4466 m_hasScrolling = TRUE;
4467
4468 if (orient == wxHORIZONTAL)
4469 {
4470 float fpos = (float)pos;
4471 float frange = (float)range;
4472 float fthumb = (float)thumbVisible;
4473 if (fpos > frange-fthumb) fpos = frange-fthumb;
4474 if (fpos < 0.0) fpos = 0.0;
4475
4476 if ((fabs(frange-m_hAdjust->upper) < 0.2) &&
4477 (fabs(fthumb-m_hAdjust->page_size) < 0.2))
4478 {
4479 SetScrollPos( orient, pos, refresh );
4480 return;
4481 }
4482
4483 m_oldHorizontalPos = fpos;
4484
4485 m_hAdjust->lower = 0.0;
4486 m_hAdjust->upper = frange;
4487 m_hAdjust->value = fpos;
4488 m_hAdjust->step_increment = 1.0;
4489 m_hAdjust->page_increment = (float)(wxMax(fthumb,0));
4490 m_hAdjust->page_size = fthumb;
4491 }
4492 else
4493 {
4494 float fpos = (float)pos;
4495 float frange = (float)range;
4496 float fthumb = (float)thumbVisible;
4497 if (fpos > frange-fthumb) fpos = frange-fthumb;
4498 if (fpos < 0.0) fpos = 0.0;
4499
4500 if ((fabs(frange-m_vAdjust->upper) < 0.2) &&
4501 (fabs(fthumb-m_vAdjust->page_size) < 0.2))
4502 {
4503 SetScrollPos( orient, pos, refresh );
4504 return;
4505 }
4506
4507 m_oldVerticalPos = fpos;
4508
4509 m_vAdjust->lower = 0.0;
4510 m_vAdjust->upper = frange;
4511 m_vAdjust->value = fpos;
4512 m_vAdjust->step_increment = 1.0;
4513 m_vAdjust->page_increment = (float)(wxMax(fthumb,0));
4514 m_vAdjust->page_size = fthumb;
4515 }
4516
4517 if (orient == wxHORIZONTAL)
4518 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "changed" );
4519 else
4520 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "changed" );
4521 }
4522
4523 void wxWindowGTK::SetScrollPos( int orient, int pos, bool WXUNUSED(refresh) )
4524 {
4525 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4526
4527 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4528
4529 if (orient == wxHORIZONTAL)
4530 {
4531 float fpos = (float)pos;
4532 if (fpos > m_hAdjust->upper - m_hAdjust->page_size) fpos = m_hAdjust->upper - m_hAdjust->page_size;
4533 if (fpos < 0.0) fpos = 0.0;
4534 m_oldHorizontalPos = fpos;
4535
4536 if (fabs(fpos-m_hAdjust->value) < 0.2) return;
4537 m_hAdjust->value = fpos;
4538 }
4539 else
4540 {
4541 float fpos = (float)pos;
4542 if (fpos > m_vAdjust->upper - m_vAdjust->page_size) fpos = m_vAdjust->upper - m_vAdjust->page_size;
4543 if (fpos < 0.0) fpos = 0.0;
4544 m_oldVerticalPos = fpos;
4545
4546 if (fabs(fpos-m_vAdjust->value) < 0.2) return;
4547 m_vAdjust->value = fpos;
4548 }
4549
4550 if (m_wxwindow->window)
4551 {
4552 if (orient == wxHORIZONTAL)
4553 {
4554 gtk_signal_disconnect_by_func( GTK_OBJECT(m_hAdjust),
4555 (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this );
4556
4557 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "value_changed" );
4558
4559 gtk_signal_connect( GTK_OBJECT(m_hAdjust), "value_changed",
4560 (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this );
4561 }
4562 else
4563 {
4564 gtk_signal_disconnect_by_func( GTK_OBJECT(m_vAdjust),
4565 (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this );
4566
4567 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "value_changed" );
4568
4569 gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed",
4570 (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this );
4571 }
4572 }
4573 }
4574
4575 int wxWindowGTK::GetScrollThumb( int orient ) const
4576 {
4577 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4578
4579 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4580
4581 if (orient == wxHORIZONTAL)
4582 return (int)(m_hAdjust->page_size+0.5);
4583 else
4584 return (int)(m_vAdjust->page_size+0.5);
4585 }
4586
4587 int wxWindowGTK::GetScrollPos( int orient ) const
4588 {
4589 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4590
4591 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4592
4593 if (orient == wxHORIZONTAL)
4594 return (int)(m_hAdjust->value+0.5);
4595 else
4596 return (int)(m_vAdjust->value+0.5);
4597 }
4598
4599 int wxWindowGTK::GetScrollRange( int orient ) const
4600 {
4601 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4602
4603 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4604
4605 if (orient == wxHORIZONTAL)
4606 return (int)(m_hAdjust->upper+0.5);
4607 else
4608 return (int)(m_vAdjust->upper+0.5);
4609 }
4610
4611 void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
4612 {
4613 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4614
4615 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4616
4617 // No scrolling requested.
4618 if ((dx == 0) && (dy == 0)) return;
4619
4620 #ifndef __WXGTK20__
4621 if (!m_updateRegion.IsEmpty())
4622 {
4623 m_updateRegion.Offset( dx, dy );
4624
4625 int cw = 0;
4626 int ch = 0;
4627 GetClientSize( &cw, &ch );
4628 m_updateRegion.Intersect( 0, 0, cw, ch );
4629 }
4630
4631 if (!m_clearRegion.IsEmpty())
4632 {
4633 m_clearRegion.Offset( dx, dy );
4634
4635 int cw = 0;
4636 int ch = 0;
4637 GetClientSize( &cw, &ch );
4638 m_clearRegion.Intersect( 0, 0, cw, ch );
4639 }
4640 #endif
4641
4642 m_clipPaintRegion = TRUE;
4643
4644 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy );
4645
4646 m_clipPaintRegion = FALSE;
4647 }
4648
4649
4650 // Find the wxWindow at the current mouse position, also returning the mouse
4651 // position.
4652 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4653 {
4654 pt = wxGetMousePosition();
4655 wxWindow* found = wxFindWindowAtPoint(pt);
4656 return found;
4657 }
4658
4659 // Get the current mouse position.
4660 wxPoint wxGetMousePosition()
4661 {
4662 /* This crashes when used within wxHelpContext,
4663 so we have to use the X-specific implementation below.
4664 gint x, y;
4665 GdkModifierType *mask;
4666 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4667
4668 return wxPoint(x, y);
4669 */
4670
4671 int x, y;
4672 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
4673
4674 Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY();
4675 Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
4676 Window rootReturn, childReturn;
4677 int rootX, rootY, winX, winY;
4678 unsigned int maskReturn;
4679
4680 XQueryPointer (display,
4681 rootWindow,
4682 &rootReturn,
4683 &childReturn,
4684 &rootX, &rootY, &winX, &winY, &maskReturn);
4685 return wxPoint(rootX, rootY);
4686
4687 }
4688
4689 // ----------------------------------------------------------------------------
4690 // wxDCModule
4691 // ----------------------------------------------------------------------------
4692
4693 class wxWinModule : public wxModule
4694 {
4695 public:
4696 bool OnInit();
4697 void OnExit();
4698
4699 private:
4700 DECLARE_DYNAMIC_CLASS(wxWinModule)
4701 };
4702
4703 IMPLEMENT_DYNAMIC_CLASS(wxWinModule, wxModule)
4704
4705 bool wxWinModule::OnInit()
4706 {
4707 // g_eraseGC = gdk_gc_new( GDK_ROOT_PARENT() );
4708 // gdk_gc_set_fill( g_eraseGC, GDK_SOLID );
4709
4710 return TRUE;
4711 }
4712
4713 void wxWinModule::OnExit()
4714 {
4715 if (g_eraseGC)
4716 gdk_gc_unref( g_eraseGC );
4717 }
4718
4719 // vi:sts=4:sw=4:et