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