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