]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/window.cpp
suppress warnings about implicit float -> int conversions
[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 switch (gdk_event->type)
1543 {
1544 case GDK_BUTTON_PRESS: event_type = wxEVT_LEFT_DOWN; break;
1545 case GDK_2BUTTON_PRESS: event_type = wxEVT_LEFT_DCLICK; break;
1546 case GDK_3BUTTON_PRESS: return FALSE;
1547 default: break;
1548 }
1549 }
1550 else if (gdk_event->button == 2)
1551 {
1552 switch (gdk_event->type)
1553 {
1554 case GDK_BUTTON_PRESS: event_type = wxEVT_MIDDLE_DOWN; break;
1555 case GDK_2BUTTON_PRESS: event_type = wxEVT_MIDDLE_DCLICK; break;
1556 default: break;
1557 }
1558 }
1559 else if (gdk_event->button == 3)
1560 {
1561 switch (gdk_event->type)
1562 {
1563 case GDK_BUTTON_PRESS: event_type = wxEVT_RIGHT_DOWN; break;
1564 case GDK_2BUTTON_PRESS: event_type = wxEVT_RIGHT_DCLICK; break;
1565 default: break;
1566 }
1567 }
1568 else if (gdk_event->button == 4)
1569 {
1570 switch (gdk_event->type)
1571 {
1572 case GDK_BUTTON_PRESS: event_type = wxEVT_MOUSEWHEEL; break;
1573 default: break;
1574 }
1575 }
1576 else if (gdk_event->button == 5)
1577 {
1578 switch (gdk_event->type)
1579 {
1580 case GDK_BUTTON_PRESS: event_type = wxEVT_MOUSEWHEEL; break;
1581 default: break;
1582 }
1583 }
1584
1585 if ( event_type == wxEVT_NULL )
1586 {
1587 // unknown mouse button or click type
1588 return FALSE;
1589 }
1590
1591 wxMouseEvent event( event_type );
1592 InitMouseEvent( win, event, gdk_event );
1593
1594 AdjustEventButtonState(event);
1595
1596 // wxListBox actually get mouse events from the item, so we need to give it
1597 // a chance to correct this
1598 win->FixUpMouseEvent(widget, event.m_x, event.m_y);
1599
1600 // find the correct window to send the event too: it may be a different one
1601 // from the one which got it at GTK+ level because some control don't have
1602 // their own X window and thus cannot get any events.
1603 if ( !g_captureWindow )
1604 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1605
1606 gs_timeLastClick = gdk_event->time;
1607
1608 /*
1609 wxPrintf( wxT("2) OnButtonPress from ") );
1610 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1611 wxPrintf( win->GetClassInfo()->GetClassName() );
1612 wxPrintf( wxT(".\n") );
1613 */
1614
1615 #ifndef __WXGTK20__
1616 if (event_type == wxEVT_LEFT_DCLICK)
1617 {
1618 // GTK 1.2 crashes when intercepting double
1619 // click events from both wxSpinButton and
1620 // wxSpinCtrl
1621 if (GTK_IS_SPIN_BUTTON(win->m_widget))
1622 {
1623 // Just disable this event for now.
1624 return FALSE;
1625 }
1626 }
1627 #endif
1628
1629 if (win->GetEventHandler()->ProcessEvent( event ))
1630 {
1631 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "button_press_event" );
1632 return TRUE;
1633 }
1634
1635 return FALSE;
1636 }
1637
1638 //-----------------------------------------------------------------------------
1639 // "button_release_event"
1640 //-----------------------------------------------------------------------------
1641
1642 static gint gtk_window_button_release_callback( GtkWidget *widget,
1643 GdkEventButton *gdk_event,
1644 wxWindowGTK *win )
1645 {
1646 DEBUG_MAIN_THREAD
1647
1648 if (g_isIdle)
1649 wxapp_install_idle_handler();
1650
1651 if (!win->m_hasVMT) return FALSE;
1652 if (g_blockEventsOnDrag) return FALSE;
1653 if (g_blockEventsOnScroll) return FALSE;
1654
1655 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1656
1657 wxEventType event_type = wxEVT_NULL;
1658
1659 switch (gdk_event->button)
1660 {
1661 case 1:
1662 event_type = wxEVT_LEFT_UP;
1663 break;
1664
1665 case 2:
1666 event_type = wxEVT_MIDDLE_UP;
1667 break;
1668
1669 case 3:
1670 event_type = wxEVT_RIGHT_UP;
1671 break;
1672
1673 default:
1674 // unknwon button, don't process
1675 return FALSE;
1676 }
1677
1678 wxMouseEvent event( event_type );
1679 InitMouseEvent( win, event, gdk_event );
1680
1681 AdjustEventButtonState(event);
1682
1683 // same wxListBox hack as above
1684 win->FixUpMouseEvent(widget, event.m_x, event.m_y);
1685
1686 if ( event_type == wxEVT_RIGHT_UP )
1687 {
1688 // generate a "context menu" event: this is similar to wxEVT_RIGHT_UP
1689 // except that:
1690 //
1691 // (a) it's a command event and so is propagated to the parent
1692 // (b) under MSW it can be generated from kbd too
1693 // (c) it uses screen coords (because of (a))
1694 wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU,
1695 win->GetId(),
1696 win->ClientToScreen(event.GetPosition()));
1697 (void)win->GetEventHandler()->ProcessEvent(evtCtx);
1698 }
1699
1700 if ( !g_captureWindow )
1701 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1702
1703 if (win->GetEventHandler()->ProcessEvent( event ))
1704 {
1705 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "button_release_event" );
1706 return TRUE;
1707 }
1708
1709 return FALSE;
1710 }
1711
1712 //-----------------------------------------------------------------------------
1713 // "motion_notify_event"
1714 //-----------------------------------------------------------------------------
1715
1716 static gint gtk_window_motion_notify_callback( GtkWidget *widget,
1717 GdkEventMotion *gdk_event,
1718 wxWindowGTK *win )
1719 {
1720 DEBUG_MAIN_THREAD
1721
1722 if (g_isIdle)
1723 wxapp_install_idle_handler();
1724
1725 if (!win->m_hasVMT) return FALSE;
1726 if (g_blockEventsOnDrag) return FALSE;
1727 if (g_blockEventsOnScroll) return FALSE;
1728
1729 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
1730
1731 if (gdk_event->is_hint)
1732 {
1733 int x = 0;
1734 int y = 0;
1735 GdkModifierType state;
1736 gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
1737 gdk_event->x = x;
1738 gdk_event->y = y;
1739 }
1740
1741 /*
1742 printf( "OnMotion from " );
1743 if (win->GetClassInfo() && win->GetClassInfo()->GetClassName())
1744 printf( win->GetClassInfo()->GetClassName() );
1745 printf( ".\n" );
1746 */
1747
1748 wxMouseEvent event( wxEVT_MOTION );
1749 InitMouseEvent(win, event, gdk_event);
1750
1751 if ( g_captureWindow )
1752 {
1753 // synthetize a mouse enter or leave event if needed
1754 GdkWindow *winUnderMouse = gdk_window_at_pointer(NULL, NULL);
1755 // This seems to be necessary and actually been added to
1756 // GDK itself in version 2.0.X
1757 gdk_flush();
1758
1759 bool hasMouse = winUnderMouse == gdk_event->window;
1760 if ( hasMouse != g_captureWindowHasMouse )
1761 {
1762 // the mouse changed window
1763 g_captureWindowHasMouse = hasMouse;
1764
1765 wxMouseEvent event(g_captureWindowHasMouse ? wxEVT_ENTER_WINDOW
1766 : wxEVT_LEAVE_WINDOW);
1767 InitMouseEvent(win, event, gdk_event);
1768 event.SetEventObject(win);
1769 win->GetEventHandler()->ProcessEvent(event);
1770 }
1771 }
1772 else // no capture
1773 {
1774 win = FindWindowForMouseEvent(win, event.m_x, event.m_y);
1775 }
1776
1777 if (win->GetEventHandler()->ProcessEvent( event ))
1778 {
1779 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "motion_notify_event" );
1780 return TRUE;
1781 }
1782
1783 return FALSE;
1784 }
1785
1786 #ifdef __WXGTK20__
1787 //-----------------------------------------------------------------------------
1788 // "mouse_wheel_event"
1789 //-----------------------------------------------------------------------------
1790
1791 static gint gtk_window_wheel_callback (GtkWidget * widget,
1792 GdkEventScroll * gdk_event,
1793 wxWindowGTK * win)
1794 {
1795 DEBUG_MAIN_THREAD
1796
1797 if (g_isIdle)
1798 wxapp_install_idle_handler();
1799
1800 wxEventType event_type = wxEVT_NULL;
1801 if (gdk_event->direction == GDK_SCROLL_UP)
1802 event_type = wxEVT_MOUSEWHEEL;
1803 else if (gdk_event->direction == GDK_SCROLL_DOWN)
1804 event_type = wxEVT_MOUSEWHEEL;
1805 else
1806 return FALSE;
1807
1808 wxMouseEvent event( event_type );
1809 // Can't use InitMouse macro because scroll events don't have button
1810 event.SetTimestamp( gdk_event->time );
1811 event.m_shiftDown = (gdk_event->state & GDK_SHIFT_MASK);
1812 event.m_controlDown = (gdk_event->state & GDK_CONTROL_MASK);
1813 event.m_altDown = (gdk_event->state & GDK_MOD1_MASK);
1814 event.m_metaDown = (gdk_event->state & GDK_MOD2_MASK);
1815 event.m_leftDown = (gdk_event->state & GDK_BUTTON1_MASK);
1816 event.m_middleDown = (gdk_event->state & GDK_BUTTON2_MASK);
1817 event.m_rightDown = (gdk_event->state & GDK_BUTTON3_MASK);
1818 if (gdk_event->direction == GDK_SCROLL_UP)
1819 event.m_wheelRotation = 120;
1820 else
1821 event.m_wheelRotation = -120;
1822
1823 wxPoint pt = win->GetClientAreaOrigin();
1824 event.m_x = (wxCoord)gdk_event->x - pt.x;
1825 event.m_y = (wxCoord)gdk_event->y - pt.y;
1826
1827 event.SetEventObject( win );
1828 event.SetId( win->GetId() );
1829 event.SetTimestamp( gdk_event->time );
1830
1831 if (win->GetEventHandler()->ProcessEvent( event ))
1832 {
1833 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "scroll_event" );
1834 return TRUE;
1835 }
1836
1837 return FALSE;
1838 }
1839 #endif
1840
1841 //-----------------------------------------------------------------------------
1842 // "focus_in_event"
1843 //-----------------------------------------------------------------------------
1844
1845 // send the wxChildFocusEvent and wxFocusEvent, common code of
1846 // gtk_window_focus_in_callback() and SetFocus()
1847 static bool DoSendFocusEvents(wxWindow *win)
1848 {
1849 // Notify the parent keeping track of focus for the kbd navigation
1850 // purposes that we got it.
1851 wxChildFocusEvent eventChildFocus(win);
1852 (void)win->GetEventHandler()->ProcessEvent(eventChildFocus);
1853
1854 wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
1855 eventFocus.SetEventObject(win);
1856
1857 return win->GetEventHandler()->ProcessEvent(eventFocus);
1858 }
1859
1860 static gint gtk_window_focus_in_callback( GtkWidget *widget,
1861 GdkEvent *WXUNUSED(event),
1862 wxWindow *win )
1863 {
1864 DEBUG_MAIN_THREAD
1865
1866 if (g_isIdle)
1867 wxapp_install_idle_handler();
1868
1869 if (!win->m_hasVMT) return FALSE;
1870 if (g_blockEventsOnDrag) return FALSE;
1871
1872 switch ( g_sendActivateEvent )
1873 {
1874 case -1:
1875 // we've got focus from outside, synthetize wxActivateEvent
1876 g_sendActivateEvent = 1;
1877 break;
1878
1879 case 0:
1880 // another our window just lost focus, it was already ours before
1881 // - don't send any wxActivateEvent
1882 g_sendActivateEvent = -1;
1883 break;
1884 }
1885
1886 g_focusWindowLast =
1887 g_focusWindow = win;
1888
1889 wxLogTrace(TRACE_FOCUS,
1890 _T("%s: focus in"), win->GetName().c_str());
1891
1892 #ifdef HAVE_XIM
1893 if (win->m_ic)
1894 gdk_im_begin(win->m_ic, win->m_wxwindow->window);
1895 #endif
1896
1897 #if wxUSE_CARET
1898 // caret needs to be informed about focus change
1899 wxCaret *caret = win->GetCaret();
1900 if ( caret )
1901 {
1902 caret->OnSetFocus();
1903 }
1904 #endif // wxUSE_CARET
1905
1906 g_activeFrameLostFocus = FALSE;
1907
1908 wxWindowGTK *active = wxGetTopLevelParent(win);
1909 if ( active != g_activeFrame )
1910 {
1911 if ( g_activeFrame )
1912 {
1913 wxLogTrace(wxT("activate"), wxT("Deactivating frame %p (from focus_in)"), g_activeFrame);
1914 wxActivateEvent event(wxEVT_ACTIVATE, FALSE, g_activeFrame->GetId());
1915 event.SetEventObject(g_activeFrame);
1916 g_activeFrame->GetEventHandler()->ProcessEvent(event);
1917 }
1918
1919 wxLogTrace(wxT("activate"), wxT("Activating frame %p (from focus_in)"), active);
1920 g_activeFrame = active;
1921 wxActivateEvent event(wxEVT_ACTIVATE, TRUE, g_activeFrame->GetId());
1922 event.SetEventObject(g_activeFrame);
1923 g_activeFrame->GetEventHandler()->ProcessEvent(event);
1924
1925 // Don't send focus events in addition to activate
1926 // if (win == g_activeFrame)
1927 // return TRUE;
1928 }
1929
1930 // does the window itself think that it has the focus?
1931 if ( !win->m_hasFocus )
1932 {
1933 // not yet, notify it
1934 win->m_hasFocus = TRUE;
1935
1936 if ( DoSendFocusEvents(win) )
1937 {
1938 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_in_event" );
1939 return TRUE;
1940 }
1941 }
1942
1943 return FALSE;
1944 }
1945
1946 //-----------------------------------------------------------------------------
1947 // "focus_out_event"
1948 //-----------------------------------------------------------------------------
1949
1950 static gint gtk_window_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk_event, wxWindowGTK *win )
1951 {
1952 DEBUG_MAIN_THREAD
1953
1954 if (g_isIdle)
1955 wxapp_install_idle_handler();
1956
1957 if (!win->m_hasVMT) return FALSE;
1958 if (g_blockEventsOnDrag) return FALSE;
1959
1960 wxLogTrace( TRACE_FOCUS,
1961 _T("%s: focus out"), win->GetName().c_str() );
1962
1963 if ( !g_activeFrameLostFocus && g_activeFrame )
1964 {
1965 // VZ: commenting this out because it does happen (although not easy
1966 // to reproduce, I only see it when using wxMiniFrame and not
1967 // always) and makes using Mahogany quite annoying
1968 #if 0
1969 wxASSERT_MSG( wxGetTopLevelParent(win) == g_activeFrame,
1970 wxT("unfocusing window that hasn't gained focus properly") );
1971 #endif // 0
1972
1973 g_activeFrameLostFocus = TRUE;
1974 }
1975
1976 // if the focus goes out of our app alltogether, OnIdle() will send
1977 // wxActivateEvent, otherwise gtk_window_focus_in_callback() will reset
1978 // g_sendActivateEvent to -1
1979 g_sendActivateEvent = 0;
1980
1981 wxWindowGTK *winFocus = wxFindFocusedChild(win);
1982 if ( winFocus )
1983 win = winFocus;
1984
1985 g_focusWindow = (wxWindowGTK *)NULL;
1986
1987 #ifdef HAVE_XIM
1988 if (win->m_ic)
1989 gdk_im_end();
1990 #endif
1991
1992 #if wxUSE_CARET
1993 // caret needs to be informed about focus change
1994 wxCaret *caret = win->GetCaret();
1995 if ( caret )
1996 {
1997 caret->OnKillFocus();
1998 }
1999 #endif // wxUSE_CARET
2000
2001 // don't send the window a kill focus event if it thinks that it doesn't
2002 // have focus already
2003 if ( win->m_hasFocus )
2004 {
2005 win->m_hasFocus = FALSE;
2006
2007 wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
2008 event.SetEventObject( win );
2009
2010 if (win->GetEventHandler()->ProcessEvent( event ))
2011 {
2012 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "focus_out_event" );
2013 return TRUE;
2014 }
2015 }
2016
2017 return FALSE;
2018 }
2019
2020 //-----------------------------------------------------------------------------
2021 // "enter_notify_event"
2022 //-----------------------------------------------------------------------------
2023
2024 static
2025 gint gtk_window_enter_callback( GtkWidget *widget,
2026 GdkEventCrossing *gdk_event,
2027 wxWindowGTK *win )
2028 {
2029 DEBUG_MAIN_THREAD
2030
2031 if (g_isIdle)
2032 wxapp_install_idle_handler();
2033
2034 if (!win->m_hasVMT) return FALSE;
2035 if (g_blockEventsOnDrag) return FALSE;
2036
2037 // Event was emitted after a grab
2038 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
2039
2040 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
2041
2042 int x = 0;
2043 int y = 0;
2044 GdkModifierType state = (GdkModifierType)0;
2045
2046 gdk_window_get_pointer( widget->window, &x, &y, &state );
2047
2048 wxMouseEvent event( wxEVT_ENTER_WINDOW );
2049 InitMouseEvent(win, event, gdk_event);
2050 wxPoint pt = win->GetClientAreaOrigin();
2051 event.m_x = x + pt.x;
2052 event.m_y = y + pt.y;
2053
2054 if (win->GetEventHandler()->ProcessEvent( event ))
2055 {
2056 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "enter_notify_event" );
2057 return TRUE;
2058 }
2059
2060 return FALSE;
2061 }
2062
2063 //-----------------------------------------------------------------------------
2064 // "leave_notify_event"
2065 //-----------------------------------------------------------------------------
2066
2067 static gint gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxWindowGTK *win )
2068 {
2069 DEBUG_MAIN_THREAD
2070
2071 if (g_isIdle)
2072 wxapp_install_idle_handler();
2073
2074 if (!win->m_hasVMT) return FALSE;
2075 if (g_blockEventsOnDrag) return FALSE;
2076
2077 // Event was emitted after an ungrab
2078 if (gdk_event->mode != GDK_CROSSING_NORMAL) return FALSE;
2079
2080 if (!win->IsOwnGtkWindow( gdk_event->window )) return FALSE;
2081
2082 wxMouseEvent event( wxEVT_LEAVE_WINDOW );
2083 event.SetTimestamp( gdk_event->time );
2084 event.SetEventObject( win );
2085
2086 int x = 0;
2087 int y = 0;
2088 GdkModifierType state = (GdkModifierType)0;
2089
2090 gdk_window_get_pointer( widget->window, &x, &y, &state );
2091
2092 event.m_shiftDown = (state & GDK_SHIFT_MASK) != 0;
2093 event.m_controlDown = (state & GDK_CONTROL_MASK) != 0;
2094 event.m_altDown = (state & GDK_MOD1_MASK) != 0;
2095 event.m_metaDown = (state & GDK_MOD2_MASK) != 0;
2096 event.m_leftDown = (state & GDK_BUTTON1_MASK) != 0;
2097 event.m_middleDown = (state & GDK_BUTTON2_MASK) != 0;
2098 event.m_rightDown = (state & GDK_BUTTON3_MASK) != 0;
2099
2100 wxPoint pt = win->GetClientAreaOrigin();
2101 event.m_x = x + pt.x;
2102 event.m_y = y + pt.y;
2103
2104 if (win->GetEventHandler()->ProcessEvent( event ))
2105 {
2106 gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "leave_notify_event" );
2107 return TRUE;
2108 }
2109
2110 return FALSE;
2111 }
2112
2113 //-----------------------------------------------------------------------------
2114 // "value_changed" from m_vAdjust
2115 //-----------------------------------------------------------------------------
2116
2117 static void gtk_window_vscroll_callback( GtkAdjustment *adjust,
2118 SCROLLBAR_CBACK_ARG
2119 wxWindowGTK *win )
2120 {
2121 DEBUG_MAIN_THREAD
2122
2123 if (g_isIdle)
2124 wxapp_install_idle_handler();
2125
2126 if (g_blockEventsOnDrag) return;
2127
2128 if (!win->m_hasVMT) return;
2129
2130 float diff = adjust->value - win->m_oldVerticalPos;
2131 if (fabs(diff) < 0.2) return;
2132
2133 win->m_oldVerticalPos = adjust->value;
2134
2135 #ifndef __WXGTK20__
2136 GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(win->m_widget);
2137 #endif
2138 wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(sw->vscrollbar));
2139
2140 int value = (int)(adjust->value+0.5);
2141
2142 wxScrollWinEvent event( command, value, wxVERTICAL );
2143 event.SetEventObject( win );
2144 win->GetEventHandler()->ProcessEvent( event );
2145 }
2146
2147 //-----------------------------------------------------------------------------
2148 // "value_changed" from m_hAdjust
2149 //-----------------------------------------------------------------------------
2150
2151 static void gtk_window_hscroll_callback( GtkAdjustment *adjust,
2152 SCROLLBAR_CBACK_ARG
2153 wxWindowGTK *win )
2154 {
2155 DEBUG_MAIN_THREAD
2156
2157 if (g_isIdle)
2158 wxapp_install_idle_handler();
2159
2160 if (g_blockEventsOnDrag) return;
2161 if (!win->m_hasVMT) return;
2162
2163 float diff = adjust->value - win->m_oldHorizontalPos;
2164 if (fabs(diff) < 0.2) return;
2165
2166 #ifndef __WXGTK20__
2167 GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(win->m_widget);
2168 #endif
2169 wxEventType command = GtkScrollWinTypeToWx(GET_SCROLL_TYPE(sw->hscrollbar));
2170
2171 win->m_oldHorizontalPos = adjust->value;
2172
2173 int value = (int)(adjust->value+0.5);
2174
2175 wxScrollWinEvent event( command, value, wxHORIZONTAL );
2176 event.SetEventObject( win );
2177 win->GetEventHandler()->ProcessEvent( event );
2178 }
2179
2180 //-----------------------------------------------------------------------------
2181 // "button_press_event" from scrollbar
2182 //-----------------------------------------------------------------------------
2183
2184 static gint gtk_scrollbar_button_press_callback( GtkRange *widget,
2185 GdkEventButton *gdk_event,
2186 wxWindowGTK *win)
2187 {
2188 DEBUG_MAIN_THREAD
2189
2190 if (g_isIdle)
2191 wxapp_install_idle_handler();
2192
2193
2194 g_blockEventsOnScroll = TRUE;
2195
2196 // FIXME: there is no 'slider' field in GTK+ 2.0 any more
2197 #ifndef __WXGTK20__
2198 win->m_isScrolling = (gdk_event->window == widget->slider);
2199 #endif
2200
2201 return FALSE;
2202 }
2203
2204 //-----------------------------------------------------------------------------
2205 // "button_release_event" from scrollbar
2206 //-----------------------------------------------------------------------------
2207
2208 static gint gtk_scrollbar_button_release_callback( GtkRange *widget,
2209 GdkEventButton *WXUNUSED(gdk_event),
2210 wxWindowGTK *win)
2211 {
2212 DEBUG_MAIN_THREAD
2213
2214 // don't test here as we can release the mouse while being over
2215 // a different window than the slider
2216 //
2217 // if (gdk_event->window != widget->slider) return FALSE;
2218
2219 g_blockEventsOnScroll = FALSE;
2220
2221 if (win->m_isScrolling)
2222 {
2223 wxEventType command = wxEVT_SCROLLWIN_THUMBRELEASE;
2224 int value = -1;
2225 int dir = -1;
2226
2227 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(win->m_widget);
2228 if (widget == GTK_RANGE(scrolledWindow->hscrollbar))
2229 {
2230 value = (int)(win->m_hAdjust->value+0.5);
2231 dir = wxHORIZONTAL;
2232 }
2233 if (widget == GTK_RANGE(scrolledWindow->vscrollbar))
2234 {
2235 value = (int)(win->m_vAdjust->value+0.5);
2236 dir = wxVERTICAL;
2237 }
2238
2239 wxScrollWinEvent event( command, value, dir );
2240 event.SetEventObject( win );
2241 win->GetEventHandler()->ProcessEvent( event );
2242 }
2243
2244 win->m_isScrolling = FALSE;
2245
2246 return FALSE;
2247 }
2248
2249 // ----------------------------------------------------------------------------
2250 // this wxWindowBase function is implemented here (in platform-specific file)
2251 // because it is static and so couldn't be made virtual
2252 // ----------------------------------------------------------------------------
2253
2254 wxWindow *wxWindowBase::FindFocus()
2255 {
2256 // the cast is necessary when we compile in wxUniversal mode
2257 return (wxWindow *)g_focusWindow;
2258 }
2259
2260
2261 //-----------------------------------------------------------------------------
2262 // "realize" from m_widget
2263 //-----------------------------------------------------------------------------
2264
2265 /* We cannot set colours and fonts before the widget has
2266 been realized, so we do this directly after realization. */
2267
2268 static gint
2269 gtk_window_realized_callback( GtkWidget *m_widget, wxWindow *win )
2270 {
2271 DEBUG_MAIN_THREAD
2272
2273 if (g_isIdle)
2274 wxapp_install_idle_handler();
2275
2276 if (win->m_delayedBackgroundColour && !win->GetThemeEnabled())
2277 win->GtkSetBackgroundColour( win->GetBackgroundColour() );
2278
2279 if (win->m_delayedForegroundColour && !win->GetThemeEnabled())
2280 win->GtkSetForegroundColour( win->GetForegroundColour() );
2281
2282 #ifdef __WXGTK20__
2283 if (win->m_imContext)
2284 {
2285 GtkPizza *pizza = GTK_PIZZA( m_widget );
2286 gtk_im_context_set_client_window( (GtkIMContext*) win->m_imContext, pizza->bin_window );
2287 }
2288 #endif
2289
2290 wxWindowCreateEvent event( win );
2291 event.SetEventObject( win );
2292 win->GetEventHandler()->ProcessEvent( event );
2293
2294 return FALSE;
2295 }
2296
2297 //-----------------------------------------------------------------------------
2298 // "size_allocate"
2299 //-----------------------------------------------------------------------------
2300
2301 static
2302 void gtk_window_size_callback( GtkWidget *WXUNUSED(widget),
2303 GtkAllocation *WXUNUSED(alloc),
2304 wxWindow *win )
2305 {
2306 if (g_isIdle)
2307 wxapp_install_idle_handler();
2308
2309 if (!win->m_hasScrolling) return;
2310
2311 int client_width = 0;
2312 int client_height = 0;
2313 win->GetClientSize( &client_width, &client_height );
2314 if ((client_width == win->m_oldClientWidth) && (client_height == win->m_oldClientHeight))
2315 return;
2316
2317 win->m_oldClientWidth = client_width;
2318 win->m_oldClientHeight = client_height;
2319
2320 if (!win->m_nativeSizeEvent)
2321 {
2322 wxSizeEvent event( win->GetSize(), win->GetId() );
2323 event.SetEventObject( win );
2324 win->GetEventHandler()->ProcessEvent( event );
2325 }
2326 }
2327
2328
2329 #ifdef HAVE_XIM
2330 #define WXUNUSED_UNLESS_XIM(param) param
2331 #else
2332 #define WXUNUSED_UNLESS_XIM(param) WXUNUSED(param)
2333 #endif
2334
2335 /* Resize XIM window */
2336
2337 static
2338 void gtk_wxwindow_size_callback( GtkWidget* WXUNUSED_UNLESS_XIM(widget),
2339 GtkAllocation* WXUNUSED_UNLESS_XIM(alloc),
2340 wxWindowGTK* WXUNUSED_UNLESS_XIM(win) )
2341 {
2342 if (g_isIdle)
2343 wxapp_install_idle_handler();
2344
2345 #ifdef HAVE_XIM
2346 if (!win->m_ic)
2347 return;
2348
2349 if (gdk_ic_get_style (win->m_ic) & GDK_IM_PREEDIT_POSITION)
2350 {
2351 gint width, height;
2352
2353 gdk_window_get_size (widget->window, &width, &height);
2354 win->m_icattr->preedit_area.width = width;
2355 win->m_icattr->preedit_area.height = height;
2356 gdk_ic_set_attr (win->m_ic, win->m_icattr, GDK_IC_PREEDIT_AREA);
2357 }
2358 #endif // HAVE_XIM
2359 }
2360
2361 //-----------------------------------------------------------------------------
2362 // "realize" from m_wxwindow
2363 //-----------------------------------------------------------------------------
2364
2365 /* Initialize XIM support */
2366
2367 static gint
2368 gtk_wxwindow_realized_callback( GtkWidget * WXUNUSED_UNLESS_XIM(widget),
2369 wxWindowGTK * WXUNUSED_UNLESS_XIM(win) )
2370 {
2371 if (g_isIdle)
2372 wxapp_install_idle_handler();
2373
2374 #ifdef HAVE_XIM
2375 if (win->m_ic) return FALSE;
2376 if (!widget) return FALSE;
2377 if (!gdk_im_ready()) return FALSE;
2378
2379 win->m_icattr = gdk_ic_attr_new();
2380 if (!win->m_icattr) return FALSE;
2381
2382 gint width, height;
2383 GdkEventMask mask;
2384 GdkColormap *colormap;
2385 GdkICAttr *attr = win->m_icattr;
2386 unsigned attrmask = GDK_IC_ALL_REQ;
2387 GdkIMStyle style;
2388 GdkIMStyle supported_style = (GdkIMStyle)
2389 (GDK_IM_PREEDIT_NONE |
2390 GDK_IM_PREEDIT_NOTHING |
2391 GDK_IM_PREEDIT_POSITION |
2392 GDK_IM_STATUS_NONE |
2393 GDK_IM_STATUS_NOTHING);
2394
2395 if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
2396 supported_style = (GdkIMStyle)(supported_style & ~GDK_IM_PREEDIT_POSITION);
2397
2398 attr->style = style = gdk_im_decide_style (supported_style);
2399 attr->client_window = widget->window;
2400
2401 if ((colormap = gtk_widget_get_colormap (widget)) !=
2402 gtk_widget_get_default_colormap ())
2403 {
2404 attrmask |= GDK_IC_PREEDIT_COLORMAP;
2405 attr->preedit_colormap = colormap;
2406 }
2407
2408 attrmask |= GDK_IC_PREEDIT_FOREGROUND;
2409 attrmask |= GDK_IC_PREEDIT_BACKGROUND;
2410 attr->preedit_foreground = widget->style->fg[GTK_STATE_NORMAL];
2411 attr->preedit_background = widget->style->base[GTK_STATE_NORMAL];
2412
2413 switch (style & GDK_IM_PREEDIT_MASK)
2414 {
2415 case GDK_IM_PREEDIT_POSITION:
2416 if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
2417 {
2418 g_warning ("over-the-spot style requires fontset");
2419 break;
2420 }
2421
2422 gdk_window_get_size (widget->window, &width, &height);
2423
2424 attrmask |= GDK_IC_PREEDIT_POSITION_REQ;
2425 attr->spot_location.x = 0;
2426 attr->spot_location.y = height;
2427 attr->preedit_area.x = 0;
2428 attr->preedit_area.y = 0;
2429 attr->preedit_area.width = width;
2430 attr->preedit_area.height = height;
2431 attr->preedit_fontset = widget->style->font;
2432
2433 break;
2434 }
2435
2436 win->m_ic = gdk_ic_new (attr, (GdkICAttributesType)attrmask);
2437
2438 if (win->m_ic == NULL)
2439 g_warning ("Can't create input context.");
2440 else
2441 {
2442 mask = gdk_window_get_events (widget->window);
2443 mask = (GdkEventMask)(mask | gdk_ic_get_events (win->m_ic));
2444 gdk_window_set_events (widget->window, mask);
2445
2446 if (GTK_WIDGET_HAS_FOCUS(widget))
2447 gdk_im_begin (win->m_ic, widget->window);
2448 }
2449 #endif // HAVE_XIM
2450
2451 return FALSE;
2452 }
2453
2454 //-----------------------------------------------------------------------------
2455 // InsertChild for wxWindowGTK.
2456 //-----------------------------------------------------------------------------
2457
2458 /* Callback for wxWindowGTK. This very strange beast has to be used because
2459 * C++ has no virtual methods in a constructor. We have to emulate a
2460 * virtual function here as wxNotebook requires a different way to insert
2461 * a child in it. I had opted for creating a wxNotebookPage window class
2462 * which would have made this superfluous (such in the MDI window system),
2463 * but no-one was listening to me... */
2464
2465 static void wxInsertChildInWindow( wxWindowGTK* parent, wxWindowGTK* child )
2466 {
2467 /* the window might have been scrolled already, do we
2468 have to adapt the position */
2469 GtkPizza *pizza = GTK_PIZZA(parent->m_wxwindow);
2470 child->m_x += pizza->xoffset;
2471 child->m_y += pizza->yoffset;
2472
2473 gtk_pizza_put( GTK_PIZZA(parent->m_wxwindow),
2474 GTK_WIDGET(child->m_widget),
2475 child->m_x,
2476 child->m_y,
2477 child->m_width,
2478 child->m_height );
2479 }
2480
2481 //-----------------------------------------------------------------------------
2482 // global functions
2483 //-----------------------------------------------------------------------------
2484
2485 wxWindow *wxGetActiveWindow()
2486 {
2487 return wxWindow::FindFocus();
2488 }
2489
2490 //-----------------------------------------------------------------------------
2491 // wxWindowGTK
2492 //-----------------------------------------------------------------------------
2493
2494 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2495 // method
2496 #ifdef __WXUNIVERSAL__
2497 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK, wxWindowBase)
2498 #else // __WXGTK__
2499 IMPLEMENT_DYNAMIC_CLASS(wxWindow, wxWindowBase)
2500 #endif // __WXUNIVERSAL__/__WXGTK__
2501
2502 void wxWindowGTK::Init()
2503 {
2504 // GTK specific
2505 m_widget = (GtkWidget *) NULL;
2506 m_wxwindow = (GtkWidget *) NULL;
2507 m_focusWidget = (GtkWidget *) NULL;
2508
2509 // position/size
2510 m_x = 0;
2511 m_y = 0;
2512 m_width = 0;
2513 m_height = 0;
2514
2515 m_sizeSet = FALSE;
2516 m_hasVMT = FALSE;
2517 m_needParent = TRUE;
2518 m_isBeingDeleted = FALSE;
2519
2520 m_noExpose = FALSE;
2521 m_nativeSizeEvent = FALSE;
2522
2523 m_hasScrolling = FALSE;
2524 m_isScrolling = FALSE;
2525
2526 m_hAdjust = (GtkAdjustment*) NULL;
2527 m_vAdjust = (GtkAdjustment*) NULL;
2528 m_oldHorizontalPos =
2529 m_oldVerticalPos = 0.0;
2530 m_oldClientWidth =
2531 m_oldClientHeight = 0;
2532
2533 m_resizing = FALSE;
2534 m_widgetStyle = (GtkStyle*) NULL;
2535
2536 m_insertCallback = (wxInsertChildFunction) NULL;
2537
2538 m_acceptsFocus = FALSE;
2539 m_hasFocus = FALSE;
2540
2541 m_clipPaintRegion = FALSE;
2542
2543 m_cursor = *wxSTANDARD_CURSOR;
2544
2545 m_delayedForegroundColour = FALSE;
2546 m_delayedBackgroundColour = FALSE;
2547
2548 #ifdef __WXGTK20__
2549 m_imContext = NULL;
2550 m_x11Context = NULL;
2551 #else
2552 #ifdef HAVE_XIM
2553 m_ic = (GdkIC*) NULL;
2554 m_icattr = (GdkICAttr*) NULL;
2555 #endif
2556 #endif
2557 }
2558
2559 wxWindowGTK::wxWindowGTK()
2560 {
2561 Init();
2562 }
2563
2564 wxWindowGTK::wxWindowGTK( wxWindow *parent,
2565 wxWindowID id,
2566 const wxPoint &pos,
2567 const wxSize &size,
2568 long style,
2569 const wxString &name )
2570 {
2571 Init();
2572
2573 Create( parent, id, pos, size, style, name );
2574 }
2575
2576 bool wxWindowGTK::Create( wxWindow *parent,
2577 wxWindowID id,
2578 const wxPoint &pos,
2579 const wxSize &size,
2580 long style,
2581 const wxString &name )
2582 {
2583 if (!PreCreation( parent, pos, size ) ||
2584 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
2585 {
2586 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2587 return FALSE;
2588 }
2589
2590 m_insertCallback = wxInsertChildInWindow;
2591
2592 // always needed for background clearing
2593 m_delayedBackgroundColour = TRUE;
2594
2595 m_widget = gtk_scrolled_window_new( (GtkAdjustment *) NULL, (GtkAdjustment *) NULL );
2596 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
2597
2598 GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
2599
2600 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
2601 scroll_class->scrollbar_spacing = 0;
2602
2603 gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
2604
2605 m_hAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->hscrollbar) );
2606 m_vAdjust = gtk_range_get_adjustment( GTK_RANGE(scrolledWindow->vscrollbar) );
2607
2608 m_wxwindow = gtk_pizza_new();
2609
2610 #ifndef __WXUNIVERSAL__
2611 GtkPizza *pizza = GTK_PIZZA(m_wxwindow);
2612
2613 if (HasFlag(wxRAISED_BORDER))
2614 {
2615 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_OUT );
2616 }
2617 else if (HasFlag(wxSUNKEN_BORDER))
2618 {
2619 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_IN );
2620 }
2621 else if (HasFlag(wxSIMPLE_BORDER))
2622 {
2623 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_THIN );
2624 }
2625 else
2626 {
2627 gtk_pizza_set_shadow_type( pizza, GTK_MYSHADOW_NONE );
2628 }
2629 #endif // __WXUNIVERSAL__
2630
2631 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
2632
2633 GTK_WIDGET_SET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
2634 m_acceptsFocus = TRUE;
2635
2636 // I _really_ don't want scrollbars in the beginning
2637 m_vAdjust->lower = 0.0;
2638 m_vAdjust->upper = 1.0;
2639 m_vAdjust->value = 0.0;
2640 m_vAdjust->step_increment = 1.0;
2641 m_vAdjust->page_increment = 1.0;
2642 m_vAdjust->page_size = 5.0;
2643 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "changed" );
2644 m_hAdjust->lower = 0.0;
2645 m_hAdjust->upper = 1.0;
2646 m_hAdjust->value = 0.0;
2647 m_hAdjust->step_increment = 1.0;
2648 m_hAdjust->page_increment = 1.0;
2649 m_hAdjust->page_size = 5.0;
2650 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "changed" );
2651
2652 // these handlers block mouse events to any window during scrolling such as
2653 // motion events and prevent GTK and wxWindows from fighting over where the
2654 // slider should be
2655
2656 gtk_signal_connect( GTK_OBJECT(scrolledWindow->vscrollbar), "button_press_event",
2657 (GtkSignalFunc)gtk_scrollbar_button_press_callback, (gpointer) this );
2658
2659 gtk_signal_connect( GTK_OBJECT(scrolledWindow->hscrollbar), "button_press_event",
2660 (GtkSignalFunc)gtk_scrollbar_button_press_callback, (gpointer) this );
2661
2662 gtk_signal_connect( GTK_OBJECT(scrolledWindow->vscrollbar), "button_release_event",
2663 (GtkSignalFunc)gtk_scrollbar_button_release_callback, (gpointer) this );
2664
2665 gtk_signal_connect( GTK_OBJECT(scrolledWindow->hscrollbar), "button_release_event",
2666 (GtkSignalFunc)gtk_scrollbar_button_release_callback, (gpointer) this );
2667
2668 // these handlers get notified when screen updates are required either when
2669 // scrolling or when the window size (and therefore scrollbar configuration)
2670 // has changed
2671
2672 gtk_signal_connect( GTK_OBJECT(m_hAdjust), "value_changed",
2673 (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this );
2674 gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed",
2675 (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this );
2676
2677 #ifdef __WXGTK20__
2678 // Create input method handler
2679 m_imContext = (GtkIMMulticontext*) gtk_im_multicontext_new ();
2680
2681 // Cannot handle drawing preedited text yet
2682 gtk_im_context_set_use_preedit( (GtkIMContext*) m_imContext, FALSE );
2683
2684 g_signal_connect (G_OBJECT (m_imContext), "commit",
2685 G_CALLBACK (gtk_wxwindow_commit_cb), this);
2686 #endif
2687
2688 gtk_widget_show( m_wxwindow );
2689
2690 if (m_parent)
2691 m_parent->DoAddChild( this );
2692
2693 m_focusWidget = m_wxwindow;
2694
2695 PostCreation();
2696
2697 Show( TRUE );
2698
2699 return TRUE;
2700 }
2701
2702 wxWindowGTK::~wxWindowGTK()
2703 {
2704 SendDestroyEvent();
2705
2706 if (g_focusWindow == this)
2707 g_focusWindow = NULL;
2708
2709 if (g_activeFrame == this)
2710 g_activeFrame = NULL;
2711
2712 if ( g_delayedFocus == this )
2713 g_delayedFocus = NULL;
2714
2715 m_isBeingDeleted = TRUE;
2716 m_hasVMT = FALSE;
2717
2718 if (m_widget)
2719 Show( FALSE );
2720
2721 DestroyChildren();
2722
2723 #ifdef HAVE_XIM
2724 if (m_ic)
2725 gdk_ic_destroy (m_ic);
2726 if (m_icattr)
2727 gdk_ic_attr_destroy (m_icattr);
2728 #endif
2729
2730 if (m_widgetStyle)
2731 {
2732 #if DISABLE_STYLE_IF_BROKEN_THEME
2733 // don't delete if it's a pixmap theme style
2734 if (!m_widgetStyle->engine_data)
2735 gtk_style_unref( m_widgetStyle );
2736 #endif
2737 m_widgetStyle = (GtkStyle*) NULL;
2738 }
2739
2740 if (m_wxwindow)
2741 {
2742 gtk_widget_destroy( m_wxwindow );
2743 m_wxwindow = (GtkWidget*) NULL;
2744 }
2745
2746 if (m_widget)
2747 {
2748 gtk_widget_destroy( m_widget );
2749 m_widget = (GtkWidget*) NULL;
2750 }
2751 }
2752
2753 bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos, const wxSize &size )
2754 {
2755 wxCHECK_MSG( !m_needParent || parent, FALSE, wxT("Need complete parent.") );
2756
2757 // This turns -1 into 30 so that a minimal window is
2758 // visible even although -1,-1 has been given as the
2759 // size of the window. the same trick is used in other
2760 // ports and should make debugging easier.
2761 m_width = WidthDefault(size.x) ;
2762 m_height = HeightDefault(size.y);
2763
2764 m_x = (int)pos.x;
2765 m_y = (int)pos.y;
2766
2767 // some reasonable defaults
2768 if (!parent)
2769 {
2770 if (m_x == -1)
2771 {
2772 m_x = (gdk_screen_width () - m_width) / 2;
2773 if (m_x < 10) m_x = 10;
2774 }
2775 if (m_y == -1)
2776 {
2777 m_y = (gdk_screen_height () - m_height) / 2;
2778 if (m_y < 10) m_y = 10;
2779 }
2780 }
2781
2782 return TRUE;
2783 }
2784
2785 void wxWindowGTK::PostCreation()
2786 {
2787 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2788
2789 if (m_wxwindow)
2790 {
2791 if (!m_noExpose)
2792 {
2793 // these get reported to wxWindows -> wxPaintEvent
2794
2795 gtk_pizza_set_external( GTK_PIZZA(m_wxwindow), TRUE );
2796
2797 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "expose_event",
2798 GTK_SIGNAL_FUNC(gtk_window_expose_callback), (gpointer)this );
2799
2800 #ifndef __WXGTK20__
2801 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "draw",
2802 GTK_SIGNAL_FUNC(gtk_window_draw_callback), (gpointer)this );
2803
2804 if (!HasFlag(wxFULL_REPAINT_ON_RESIZE))
2805 {
2806 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "event",
2807 GTK_SIGNAL_FUNC(gtk_window_event_event_callback), (gpointer)this );
2808 }
2809 #else
2810 // gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow), !HasFlag( wxFULL_REPAINT_ON_RESIZE ) );
2811 #endif
2812
2813 #ifdef __WXGTK20__
2814 // Create input method handler
2815 m_imContext = (GtkIMMulticontext*) gtk_im_multicontext_new ();
2816
2817 // Cannot handle drawing preedited text yet
2818 gtk_im_context_set_use_preedit( (GtkIMContext*) m_imContext, FALSE );
2819
2820 g_signal_connect (G_OBJECT (m_imContext), "commit",
2821 G_CALLBACK (gtk_wxwindow_commit_cb), this);
2822 #endif
2823 }
2824
2825 // these are called when the "sunken" or "raised" borders are drawn
2826 gtk_signal_connect( GTK_OBJECT(m_widget), "expose_event",
2827 GTK_SIGNAL_FUNC(gtk_window_own_expose_callback), (gpointer)this );
2828
2829 #ifndef __WXGTK20__
2830 gtk_signal_connect( GTK_OBJECT(m_widget), "draw",
2831 GTK_SIGNAL_FUNC(gtk_window_own_draw_callback), (gpointer)this );
2832 #endif
2833 }
2834
2835 // focus handling
2836
2837 if (m_focusWidget == NULL)
2838 m_focusWidget = m_widget;
2839
2840 gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_in_event",
2841 GTK_SIGNAL_FUNC(gtk_window_focus_in_callback), (gpointer)this );
2842
2843 gtk_signal_connect( GTK_OBJECT(m_focusWidget), "focus_out_event",
2844 GTK_SIGNAL_FUNC(gtk_window_focus_out_callback), (gpointer)this );
2845
2846 // connect to the various key and mouse handlers
2847
2848 GtkWidget *connect_widget = GetConnectWidget();
2849
2850 ConnectWidget( connect_widget );
2851
2852 /* We cannot set colours, fonts and cursors before the widget has
2853 been realized, so we do this directly after realization */
2854 gtk_signal_connect( GTK_OBJECT(connect_widget), "realize",
2855 GTK_SIGNAL_FUNC(gtk_window_realized_callback), (gpointer) this );
2856
2857 if (m_wxwindow)
2858 {
2859 // Catch native resize events
2860 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "size_allocate",
2861 GTK_SIGNAL_FUNC(gtk_window_size_callback), (gpointer)this );
2862
2863 // Initialize XIM support
2864 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "realize",
2865 GTK_SIGNAL_FUNC(gtk_wxwindow_realized_callback), (gpointer) this );
2866
2867 // And resize XIM window
2868 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "size_allocate",
2869 GTK_SIGNAL_FUNC(gtk_wxwindow_size_callback), (gpointer)this );
2870 }
2871
2872 if ( !GTK_IS_COMBO(m_widget))
2873 {
2874 // This is needed if we want to add our windows into native
2875 // GTK control, such as the toolbar. With this callback, the
2876 // toolbar gets to know the correct size (the one set by the
2877 // programmer). Sadly, it misbehaves for wxComboBox. FIXME
2878 // when moving to GTK 2.0.
2879 gtk_signal_connect( GTK_OBJECT(m_widget), "size_request",
2880 GTK_SIGNAL_FUNC(wxgtk_window_size_request_callback),
2881 (gpointer) this );
2882 }
2883
2884 m_hasVMT = TRUE;
2885 }
2886
2887 void wxWindowGTK::ConnectWidget( GtkWidget *widget )
2888 {
2889 gtk_signal_connect( GTK_OBJECT(widget), "key_press_event",
2890 GTK_SIGNAL_FUNC(gtk_window_key_press_callback), (gpointer)this );
2891
2892 gtk_signal_connect( GTK_OBJECT(widget), "key_release_event",
2893 GTK_SIGNAL_FUNC(gtk_window_key_release_callback), (gpointer)this );
2894
2895 gtk_signal_connect( GTK_OBJECT(widget), "button_press_event",
2896 GTK_SIGNAL_FUNC(gtk_window_button_press_callback), (gpointer)this );
2897
2898 gtk_signal_connect( GTK_OBJECT(widget), "button_release_event",
2899 GTK_SIGNAL_FUNC(gtk_window_button_release_callback), (gpointer)this );
2900
2901 gtk_signal_connect( GTK_OBJECT(widget), "motion_notify_event",
2902 GTK_SIGNAL_FUNC(gtk_window_motion_notify_callback), (gpointer)this );
2903
2904 #ifdef __WXGTK20__
2905 gtk_signal_connect( GTK_OBJECT(widget), "scroll_event",
2906 GTK_SIGNAL_FUNC(gtk_window_wheel_callback), (gpointer)this );
2907 #endif
2908
2909 gtk_signal_connect( GTK_OBJECT(widget), "enter_notify_event",
2910 GTK_SIGNAL_FUNC(gtk_window_enter_callback), (gpointer)this );
2911
2912 gtk_signal_connect( GTK_OBJECT(widget), "leave_notify_event",
2913 GTK_SIGNAL_FUNC(gtk_window_leave_callback), (gpointer)this );
2914 }
2915
2916 bool wxWindowGTK::Destroy()
2917 {
2918 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2919
2920 m_hasVMT = FALSE;
2921
2922 return wxWindowBase::Destroy();
2923 }
2924
2925 void wxWindowGTK::DoMoveWindow(int x, int y, int width, int height)
2926 {
2927 gtk_pizza_set_size( GTK_PIZZA(m_parent->m_wxwindow), m_widget, x, y, width, height );
2928 }
2929
2930 void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags )
2931 {
2932 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
2933 wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") );
2934
2935 /*
2936 printf( "DoSetSize: name %s, x,y,w,h: %d,%d,%d,%d \n", GetName().c_str(), x,y,width,height );
2937 */
2938
2939 if (m_resizing) return; /* I don't like recursions */
2940 m_resizing = TRUE;
2941
2942 int currentX, currentY;
2943 GetPosition(&currentX, &currentY);
2944 if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2945 x = currentX;
2946 if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
2947 y = currentY;
2948 AdjustForParentClientOrigin(x, y, sizeFlags);
2949
2950 if (m_parent->m_wxwindow == NULL) /* i.e. wxNotebook */
2951 {
2952 /* don't set the size for children of wxNotebook, just take the values. */
2953 m_x = x;
2954 m_y = y;
2955 m_width = width;
2956 m_height = height;
2957 }
2958 else
2959 {
2960 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
2961 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
2962 {
2963 if (x != -1) m_x = x + pizza->xoffset;
2964 if (y != -1) m_y = y + pizza->yoffset;
2965 }
2966 else
2967 {
2968 m_x = x + pizza->xoffset;
2969 m_y = y + pizza->yoffset;
2970 }
2971 if (width != -1) m_width = width;
2972 if (height != -1) m_height = height;
2973
2974 if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH)
2975 {
2976 if (width == -1) m_width = 80;
2977 }
2978
2979 if ((sizeFlags & wxSIZE_AUTO_HEIGHT) == wxSIZE_AUTO_HEIGHT)
2980 {
2981 if (height == -1) m_height = 26;
2982 }
2983
2984 int minWidth = GetMinWidth(),
2985 minHeight = GetMinHeight(),
2986 maxWidth = GetMaxWidth(),
2987 maxHeight = GetMaxHeight();
2988
2989 if ((minWidth != -1) && (m_width < minWidth)) m_width = minWidth;
2990 if ((minHeight != -1) && (m_height < minHeight)) m_height = minHeight;
2991 if ((maxWidth != -1) && (m_width > maxWidth)) m_width = maxWidth;
2992 if ((maxHeight != -1) && (m_height > maxHeight)) m_height = maxHeight;
2993
2994 int border = 0;
2995 int bottom_border = 0;
2996
2997 #ifndef __WXGTK20__
2998 if (GTK_WIDGET_CAN_DEFAULT(m_widget))
2999 {
3000 /* the default button has a border around it */
3001 border = 6;
3002 bottom_border = 5;
3003 }
3004 #endif
3005
3006 DoMoveWindow( m_x-border,
3007 m_y-border,
3008 m_width+2*border,
3009 m_height+border+bottom_border );
3010 }
3011
3012 if (m_hasScrolling)
3013 {
3014 /* Sometimes the client area changes size without the
3015 whole windows's size changing, but if the whole
3016 windows's size doesn't change, no wxSizeEvent will
3017 normally be sent. Here we add an extra test if
3018 the client test has been changed and this will
3019 be used then. */
3020 GetClientSize( &m_oldClientWidth, &m_oldClientHeight );
3021 }
3022
3023 /*
3024 wxPrintf( "OnSize sent from " );
3025 if (GetClassInfo() && GetClassInfo()->GetClassName())
3026 wxPrintf( GetClassInfo()->GetClassName() );
3027 wxPrintf( " %d %d %d %d\n", (int)m_x, (int)m_y, (int)m_width, (int)m_height );
3028 */
3029
3030 if (!m_nativeSizeEvent)
3031 {
3032 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
3033 event.SetEventObject( this );
3034 GetEventHandler()->ProcessEvent( event );
3035 }
3036
3037 m_resizing = FALSE;
3038 }
3039
3040 void wxWindowGTK::OnInternalIdle()
3041 {
3042 // Update invalidated regions.
3043 GtkUpdate();
3044
3045 // Synthetize activate events.
3046 if ( g_sendActivateEvent != -1 )
3047 {
3048 bool activate = g_sendActivateEvent != 0;
3049
3050 // do it only once
3051 g_sendActivateEvent = -1;
3052
3053 wxTheApp->SetActive(activate, (wxWindow *)g_focusWindowLast);
3054 }
3055
3056 if ( g_activeFrameLostFocus )
3057 {
3058 if ( g_activeFrame )
3059 {
3060 wxLogTrace(wxT("activate"), wxT("Deactivating frame %p (from idle)"), g_activeFrame);
3061 wxActivateEvent event(wxEVT_ACTIVATE, FALSE, g_activeFrame->GetId());
3062 event.SetEventObject(g_activeFrame);
3063 g_activeFrame->GetEventHandler()->ProcessEvent(event);
3064 g_activeFrame = NULL;
3065 }
3066 g_activeFrameLostFocus = FALSE;
3067 }
3068
3069 wxCursor cursor = m_cursor;
3070 if (g_globalCursor.Ok()) cursor = g_globalCursor;
3071
3072 if (cursor.Ok())
3073 {
3074 /* I now set the cursor anew in every OnInternalIdle call
3075 as setting the cursor in a parent window also effects the
3076 windows above so that checking for the current cursor is
3077 not possible. */
3078
3079 if (m_wxwindow)
3080 {
3081 GdkWindow *window = GTK_PIZZA(m_wxwindow)->bin_window;
3082 if (window)
3083 gdk_window_set_cursor( window, cursor.GetCursor() );
3084
3085 if (!g_globalCursor.Ok())
3086 cursor = *wxSTANDARD_CURSOR;
3087
3088 window = m_widget->window;
3089 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
3090 gdk_window_set_cursor( window, cursor.GetCursor() );
3091
3092 }
3093 else
3094 {
3095
3096 GdkWindow *window = m_widget->window;
3097 if ((window) && !(GTK_WIDGET_NO_WINDOW(m_widget)))
3098 gdk_window_set_cursor( window, cursor.GetCursor() );
3099
3100 }
3101 }
3102
3103 if (wxUpdateUIEvent::CanUpdate(this))
3104 UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
3105 }
3106
3107 void wxWindowGTK::DoGetSize( int *width, int *height ) const
3108 {
3109 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3110
3111 if (width) (*width) = m_width;
3112 if (height) (*height) = m_height;
3113 }
3114
3115 void wxWindowGTK::DoSetClientSize( int width, int height )
3116 {
3117 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3118
3119 if (!m_wxwindow)
3120 {
3121 SetSize( width, height );
3122 }
3123 else
3124 {
3125 int dw = 0;
3126 int dh = 0;
3127
3128 #ifndef __WXUNIVERSAL__
3129 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3130 {
3131 /* when using GTK 1.2 we set the shadow border size to 2 */
3132 dw += 2 * 2;
3133 dh += 2 * 2;
3134 }
3135 if (HasFlag(wxSIMPLE_BORDER))
3136 {
3137 /* when using GTK 1.2 we set the simple border size to 1 */
3138 dw += 1 * 2;
3139 dh += 1 * 2;
3140 }
3141 #endif // __WXUNIVERSAL__
3142
3143 if (m_hasScrolling)
3144 {
3145 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3146
3147 GtkRequisition vscroll_req;
3148 vscroll_req.width = 2;
3149 vscroll_req.height = 2;
3150 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3151 (scroll_window->vscrollbar, &vscroll_req );
3152
3153 GtkRequisition hscroll_req;
3154 hscroll_req.width = 2;
3155 hscroll_req.height = 2;
3156 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3157 (scroll_window->hscrollbar, &hscroll_req );
3158
3159 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3160
3161 if (scroll_window->vscrollbar_visible)
3162 {
3163 dw += vscroll_req.width;
3164 dw += scroll_class->scrollbar_spacing;
3165 }
3166
3167 if (scroll_window->hscrollbar_visible)
3168 {
3169 dh += hscroll_req.height;
3170 dh += scroll_class->scrollbar_spacing;
3171 }
3172 }
3173
3174 SetSize( width+dw, height+dh );
3175 }
3176 }
3177
3178 void wxWindowGTK::DoGetClientSize( int *width, int *height ) const
3179 {
3180 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3181
3182 if (!m_wxwindow)
3183 {
3184 if (width) (*width) = m_width;
3185 if (height) (*height) = m_height;
3186 }
3187 else
3188 {
3189 int dw = 0;
3190 int dh = 0;
3191
3192 #ifndef __WXUNIVERSAL__
3193 if (HasFlag(wxRAISED_BORDER) || HasFlag(wxSUNKEN_BORDER))
3194 {
3195 /* when using GTK 1.2 we set the shadow border size to 2 */
3196 dw += 2 * 2;
3197 dh += 2 * 2;
3198 }
3199 if (HasFlag(wxSIMPLE_BORDER))
3200 {
3201 /* when using GTK 1.2 we set the simple border size to 1 */
3202 dw += 1 * 2;
3203 dh += 1 * 2;
3204 }
3205 #endif // __WXUNIVERSAL__
3206
3207 if (m_hasScrolling)
3208 {
3209 GtkScrolledWindow *scroll_window = GTK_SCROLLED_WINDOW(m_widget);
3210
3211 GtkRequisition vscroll_req;
3212 vscroll_req.width = 2;
3213 vscroll_req.height = 2;
3214 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->vscrollbar) )->size_request )
3215 (scroll_window->vscrollbar, &vscroll_req );
3216
3217 GtkRequisition hscroll_req;
3218 hscroll_req.width = 2;
3219 hscroll_req.height = 2;
3220 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window->hscrollbar) )->size_request )
3221 (scroll_window->hscrollbar, &hscroll_req );
3222
3223 GtkScrolledWindowClass *scroll_class = GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget) );
3224
3225 if (scroll_window->vscrollbar_visible)
3226 {
3227 dw += vscroll_req.width;
3228 dw += scroll_class->scrollbar_spacing;
3229 }
3230
3231 if (scroll_window->hscrollbar_visible)
3232 {
3233 dh += hscroll_req.height;
3234 dh += scroll_class->scrollbar_spacing;
3235 }
3236 }
3237
3238 if (width) (*width) = m_width - dw;
3239 if (height) (*height) = m_height - dh;
3240 }
3241
3242 /*
3243 printf( "GetClientSize, name %s ", GetName().c_str() );
3244 if (width) printf( " width = %d", (*width) );
3245 if (height) printf( " height = %d", (*height) );
3246 printf( "\n" );
3247 */
3248 }
3249
3250 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
3251 {
3252 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3253
3254 int dx = 0;
3255 int dy = 0;
3256 if (m_parent && m_parent->m_wxwindow)
3257 {
3258 GtkPizza *pizza = GTK_PIZZA(m_parent->m_wxwindow);
3259 dx = pizza->xoffset;
3260 dy = pizza->yoffset;
3261 }
3262
3263 if (x) (*x) = m_x - dx;
3264 if (y) (*y) = m_y - dy;
3265 }
3266
3267 void wxWindowGTK::DoClientToScreen( int *x, int *y ) const
3268 {
3269 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3270
3271 if (!m_widget->window) return;
3272
3273 GdkWindow *source = (GdkWindow *) NULL;
3274 if (m_wxwindow)
3275 source = GTK_PIZZA(m_wxwindow)->bin_window;
3276 else
3277 source = m_widget->window;
3278
3279 int org_x = 0;
3280 int org_y = 0;
3281 gdk_window_get_origin( source, &org_x, &org_y );
3282
3283 if (!m_wxwindow)
3284 {
3285 if (GTK_WIDGET_NO_WINDOW (m_widget))
3286 {
3287 org_x += m_widget->allocation.x;
3288 org_y += m_widget->allocation.y;
3289 }
3290 }
3291
3292 if (x) *x += org_x;
3293 if (y) *y += org_y;
3294 }
3295
3296 void wxWindowGTK::DoScreenToClient( int *x, int *y ) const
3297 {
3298 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3299
3300 if (!m_widget->window) return;
3301
3302 GdkWindow *source = (GdkWindow *) NULL;
3303 if (m_wxwindow)
3304 source = GTK_PIZZA(m_wxwindow)->bin_window;
3305 else
3306 source = m_widget->window;
3307
3308 int org_x = 0;
3309 int org_y = 0;
3310 gdk_window_get_origin( source, &org_x, &org_y );
3311
3312 if (!m_wxwindow)
3313 {
3314 if (GTK_WIDGET_NO_WINDOW (m_widget))
3315 {
3316 org_x += m_widget->allocation.x;
3317 org_y += m_widget->allocation.y;
3318 }
3319 }
3320
3321 if (x) *x -= org_x;
3322 if (y) *y -= org_y;
3323 }
3324
3325 bool wxWindowGTK::Show( bool show )
3326 {
3327 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3328
3329 if (!wxWindowBase::Show(show))
3330 {
3331 // nothing to do
3332 return FALSE;
3333 }
3334
3335 if (show)
3336 gtk_widget_show( m_widget );
3337 else
3338 gtk_widget_hide( m_widget );
3339
3340 wxShowEvent eventShow(GetId(), show);
3341 eventShow.m_eventObject = this;
3342
3343 GetEventHandler()->ProcessEvent(eventShow);
3344
3345 return TRUE;
3346 }
3347
3348 static void wxWindowNotifyEnable(wxWindowGTK* win, bool enable)
3349 {
3350 win->OnParentEnable(enable);
3351
3352 // Recurse, so that children have the opportunity to Do The Right Thing
3353 // and reset colours that have been messed up by a parent's (really ancestor's)
3354 // Enable call
3355 for ( wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst();
3356 node;
3357 node = node->GetNext() )
3358 {
3359 wxWindow *child = node->GetData();
3360 if (!child->IsKindOf(CLASSINFO(wxDialog)) && !child->IsKindOf(CLASSINFO(wxFrame)))
3361 wxWindowNotifyEnable(child, enable);
3362 }
3363 }
3364
3365 bool wxWindowGTK::Enable( bool enable )
3366 {
3367 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3368
3369 if (!wxWindowBase::Enable(enable))
3370 {
3371 // nothing to do
3372 return FALSE;
3373 }
3374
3375 gtk_widget_set_sensitive( m_widget, enable );
3376 if ( m_wxwindow )
3377 gtk_widget_set_sensitive( m_wxwindow, enable );
3378
3379 wxWindowNotifyEnable(this, enable);
3380
3381 return TRUE;
3382 }
3383
3384 int wxWindowGTK::GetCharHeight() const
3385 {
3386 wxCHECK_MSG( (m_widget != NULL), 12, wxT("invalid window") );
3387
3388 wxCHECK_MSG( m_font.Ok(), 12, wxT("invalid font") );
3389
3390 #ifdef __WXGTK20__
3391 PangoContext *context = NULL;
3392 if (m_widget)
3393 context = gtk_widget_get_pango_context( m_widget );
3394
3395 if (!context)
3396 return 0;
3397
3398 PangoFontDescription *desc = m_font.GetNativeFontInfo()->description;
3399 PangoLayout *layout = pango_layout_new(context);
3400 pango_layout_set_font_description(layout, desc);
3401 pango_layout_set_text(layout, "H", 1);
3402 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3403
3404 PangoRectangle rect;
3405 pango_layout_line_get_extents(line, NULL, &rect);
3406
3407 g_object_unref( G_OBJECT( layout ) );
3408
3409 return (int) (rect.height / PANGO_SCALE);
3410 #else
3411 GdkFont *font = m_font.GetInternalFont( 1.0 );
3412
3413 return font->ascent + font->descent;
3414 #endif
3415 }
3416
3417 int wxWindowGTK::GetCharWidth() const
3418 {
3419 wxCHECK_MSG( (m_widget != NULL), 8, wxT("invalid window") );
3420
3421 wxCHECK_MSG( m_font.Ok(), 8, wxT("invalid font") );
3422
3423 #ifdef __WXGTK20__
3424 PangoContext *context = NULL;
3425 if (m_widget)
3426 context = gtk_widget_get_pango_context( m_widget );
3427
3428 if (!context)
3429 return 0;
3430
3431 PangoFontDescription *desc = m_font.GetNativeFontInfo()->description;
3432 PangoLayout *layout = pango_layout_new(context);
3433 pango_layout_set_font_description(layout, desc);
3434 pango_layout_set_text(layout, "g", 1);
3435 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3436
3437 PangoRectangle rect;
3438 pango_layout_line_get_extents(line, NULL, &rect);
3439
3440 g_object_unref( G_OBJECT( layout ) );
3441
3442 return (int) (rect.width / PANGO_SCALE);
3443 #else
3444 GdkFont *font = m_font.GetInternalFont( 1.0 );
3445
3446 return gdk_string_width( font, "g" );
3447 #endif
3448 }
3449
3450 void wxWindowGTK::GetTextExtent( const wxString& string,
3451 int *x,
3452 int *y,
3453 int *descent,
3454 int *externalLeading,
3455 const wxFont *theFont ) const
3456 {
3457 wxFont fontToUse = m_font;
3458 if (theFont) fontToUse = *theFont;
3459
3460 wxCHECK_RET( fontToUse.Ok(), wxT("invalid font") );
3461
3462 if (string.IsEmpty())
3463 {
3464 if (x) (*x) = 0;
3465 if (y) (*y) = 0;
3466 return;
3467 }
3468
3469 #ifdef __WXGTK20__
3470 PangoContext *context = NULL;
3471 if (m_widget)
3472 context = gtk_widget_get_pango_context( m_widget );
3473
3474 if (!context)
3475 {
3476 if (x) (*x) = 0;
3477 if (y) (*y) = 0;
3478 return;
3479 }
3480
3481 PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
3482 PangoLayout *layout = pango_layout_new(context);
3483 pango_layout_set_font_description(layout, desc);
3484 {
3485 #if wxUSE_UNICODE
3486 const wxCharBuffer data = wxConvUTF8.cWC2MB( string );
3487 pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data ));
3488 #else
3489 const wxWCharBuffer wdata = wxConvLocal.cMB2WC( string );
3490 const wxCharBuffer data = wxConvUTF8.cWC2MB( wdata );
3491 pango_layout_set_text(layout, (const char*) data, strlen( (const char*) data ));
3492 #endif
3493 }
3494 PangoLayoutLine *line = (PangoLayoutLine *)pango_layout_get_lines(layout)->data;
3495
3496 PangoRectangle rect;
3497 pango_layout_line_get_extents(line, NULL, &rect);
3498
3499 if (x) (*x) = (wxCoord) (rect.width / PANGO_SCALE);
3500 if (y) (*y) = (wxCoord) (rect.height / PANGO_SCALE);
3501 if (descent)
3502 {
3503 // Do something about metrics here
3504 (*descent) = 0;
3505 }
3506 if (externalLeading) (*externalLeading) = 0; // ??
3507
3508 g_object_unref( G_OBJECT( layout ) );
3509 #else
3510 GdkFont *font = fontToUse.GetInternalFont( 1.0 );
3511 if (x) (*x) = gdk_string_width( font, wxGTK_CONV( string ) );
3512 if (y) (*y) = font->ascent + font->descent;
3513 if (descent) (*descent) = font->descent;
3514 if (externalLeading) (*externalLeading) = 0; // ??
3515 #endif
3516 }
3517
3518 void wxWindowGTK::SetFocus()
3519 {
3520 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3521
3522 if ( m_hasFocus )
3523 {
3524 // don't do anything if we already have focus
3525 return;
3526 }
3527
3528 if (m_wxwindow)
3529 {
3530 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow))
3531 {
3532 gtk_widget_grab_focus (m_wxwindow);
3533 }
3534 }
3535 else if (m_widget)
3536 {
3537 if (GTK_WIDGET_CAN_FOCUS(m_widget) && !GTK_WIDGET_HAS_FOCUS (m_widget) )
3538 {
3539 if (!GTK_WIDGET_REALIZED(m_widget))
3540 {
3541 // we can't set the focus to the widget now so we remember that
3542 // it should be focused and will do it later, during the idle
3543 // time, as soon as we can
3544 wxLogTrace(TRACE_FOCUS,
3545 _T("Delaying setting focus to %s(%s)"),
3546 GetClassInfo()->GetClassName(), GetLabel().c_str());
3547
3548 g_delayedFocus = this;
3549 }
3550 else
3551 {
3552 wxLogTrace(TRACE_FOCUS,
3553 _T("Setting focus to %s(%s)"),
3554 GetClassInfo()->GetClassName(), GetLabel().c_str());
3555
3556 gtk_widget_grab_focus (m_widget);
3557 }
3558 }
3559 else if (GTK_IS_CONTAINER(m_widget))
3560 {
3561 SET_CONTAINER_FOCUS( m_widget, GTK_DIR_TAB_FORWARD );
3562 }
3563 else
3564 {
3565 wxLogTrace(TRACE_FOCUS,
3566 _T("Can't set focus to %s(%s)"),
3567 GetClassInfo()->GetClassName(), GetLabel().c_str());
3568 }
3569 }
3570 }
3571
3572 bool wxWindowGTK::AcceptsFocus() const
3573 {
3574 return m_acceptsFocus && wxWindowBase::AcceptsFocus();
3575 }
3576
3577 bool wxWindowGTK::Reparent( wxWindowBase *newParentBase )
3578 {
3579 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3580
3581 wxWindowGTK *oldParent = m_parent,
3582 *newParent = (wxWindowGTK *)newParentBase;
3583
3584 wxASSERT( GTK_IS_WIDGET(m_widget) );
3585
3586 if ( !wxWindowBase::Reparent(newParent) )
3587 return FALSE;
3588
3589 wxASSERT( GTK_IS_WIDGET(m_widget) );
3590
3591 /* prevent GTK from deleting the widget arbitrarily */
3592 gtk_widget_ref( m_widget );
3593
3594 if (oldParent)
3595 {
3596 gtk_container_remove( GTK_CONTAINER(m_widget->parent), m_widget );
3597 }
3598
3599 wxASSERT( GTK_IS_WIDGET(m_widget) );
3600
3601 if (newParent)
3602 {
3603 /* insert GTK representation */
3604 (*(newParent->m_insertCallback))(newParent, this);
3605 }
3606
3607 /* reverse: prevent GTK from deleting the widget arbitrarily */
3608 gtk_widget_unref( m_widget );
3609
3610 return TRUE;
3611 }
3612
3613 void wxWindowGTK::DoAddChild(wxWindowGTK *child)
3614 {
3615 wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
3616
3617 wxASSERT_MSG( (child != NULL), wxT("invalid child window") );
3618
3619 wxASSERT_MSG( (m_insertCallback != NULL), wxT("invalid child insertion function") );
3620
3621 /* add to list */
3622 AddChild( child );
3623
3624 /* insert GTK representation */
3625 (*m_insertCallback)(this, child);
3626 }
3627
3628 void wxWindowGTK::Raise()
3629 {
3630 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3631
3632 if (!m_widget->window) return;
3633
3634 gdk_window_raise( m_widget->window );
3635 }
3636
3637 void wxWindowGTK::Lower()
3638 {
3639 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3640
3641 if (!m_widget->window) return;
3642
3643 gdk_window_lower( m_widget->window );
3644 }
3645
3646 bool wxWindowGTK::SetCursor( const wxCursor &cursor )
3647 {
3648 wxCHECK_MSG( (m_widget != NULL), FALSE, wxT("invalid window") );
3649
3650 if (cursor == m_cursor)
3651 return FALSE;
3652
3653 if (g_isIdle)
3654 wxapp_install_idle_handler();
3655
3656 if (cursor == wxNullCursor)
3657 return wxWindowBase::SetCursor( *wxSTANDARD_CURSOR );
3658 else
3659 return wxWindowBase::SetCursor( cursor );
3660 }
3661
3662 void wxWindowGTK::WarpPointer( int x, int y )
3663 {
3664 wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
3665
3666 // We provide this function ourselves as it is
3667 // missing in GDK (top of this file).
3668
3669 GdkWindow *window = (GdkWindow*) NULL;
3670 if (m_wxwindow)
3671 window = GTK_PIZZA(m_wxwindow)->bin_window;
3672 else
3673 window = GetConnectWidget()->window;
3674
3675 if (window)
3676 gdk_window_warp_pointer( window, x, y );
3677 }
3678
3679
3680 void wxWindowGTK::Refresh( bool eraseBackground, const wxRect *rect )
3681 {
3682 if (!m_widget) return;
3683 if (!m_widget->window) return;
3684
3685 #ifndef __WXGTK20__
3686 if (g_isIdle)
3687 wxapp_install_idle_handler();
3688
3689 wxRect myRect(0,0,0,0);
3690 if (m_wxwindow && rect)
3691 {
3692 myRect.SetSize(wxSize( m_wxwindow->allocation.width,
3693 m_wxwindow->allocation.height));
3694 myRect.Intersect(*rect);
3695 if (!myRect.width || !myRect.height)
3696 // nothing to do, rectangle is empty
3697 return;
3698 rect = &myRect;
3699 }
3700
3701 if (eraseBackground && m_wxwindow && m_wxwindow->window)
3702 {
3703 if (rect)
3704 {
3705 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3706 m_clearRegion.Union( rect->x, rect->y, rect->width, rect->height );
3707 }
3708 else
3709 {
3710 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3711 m_clearRegion.Clear();
3712 m_clearRegion.Union( 0, 0, m_wxwindow->allocation.width, m_wxwindow->allocation.height );
3713 }
3714 }
3715
3716 if (rect)
3717 {
3718 if (m_wxwindow)
3719 {
3720 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3721 m_updateRegion.Union( rect->x, rect->y, rect->width, rect->height );
3722 }
3723 else
3724 {
3725 GdkRectangle gdk_rect;
3726 gdk_rect.x = rect->x;
3727 gdk_rect.y = rect->y;
3728 gdk_rect.width = rect->width;
3729 gdk_rect.height = rect->height;
3730 gtk_widget_draw( m_widget, &gdk_rect );
3731 }
3732 }
3733 else
3734 {
3735 if (m_wxwindow)
3736 {
3737 // Schedule for later Updating in ::Update() or ::OnInternalIdle().
3738 m_updateRegion.Clear();
3739 m_updateRegion.Union( 0, 0, m_wxwindow->allocation.width, m_wxwindow->allocation.height );
3740 }
3741 else
3742 {
3743 gtk_widget_draw( m_widget, (GdkRectangle*) NULL );
3744 }
3745 }
3746 #else
3747 if (m_wxwindow)
3748 {
3749 if (rect)
3750 {
3751 GdkRectangle gdk_rect;
3752 gdk_rect.x = rect->x;
3753 gdk_rect.y = rect->y;
3754 gdk_rect.width = rect->width;
3755 gdk_rect.height = rect->height;
3756 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, &gdk_rect, TRUE );
3757 }
3758 else
3759 {
3760 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow)->bin_window, NULL, TRUE );
3761 }
3762 }
3763 #endif
3764 }
3765
3766 void wxWindowGTK::Update()
3767 {
3768 GtkUpdate();
3769 }
3770
3771 void wxWindowGTK::GtkUpdate()
3772 {
3773 #ifdef __WXGTK20__
3774 if (m_wxwindow && GTK_PIZZA(m_wxwindow)->bin_window)
3775 gdk_window_process_updates( GTK_PIZZA(m_wxwindow)->bin_window, FALSE );
3776 #else
3777 if (!m_updateRegion.IsEmpty())
3778 GtkSendPaintEvents();
3779 #endif
3780 }
3781
3782 void wxWindowGTK::GtkSendPaintEvents()
3783 {
3784 if (!m_wxwindow)
3785 {
3786 #ifndef __WXGTK20__
3787 m_clearRegion.Clear();
3788 #endif
3789 m_updateRegion.Clear();
3790 return;
3791 }
3792
3793 // Clip to paint region in wxClientDC
3794 m_clipPaintRegion = TRUE;
3795
3796 #ifndef __WXGTK20__
3797 // widget to draw on
3798 GtkPizza *pizza = GTK_PIZZA (m_wxwindow);
3799
3800 // later for GTK 2.0, too.
3801 if (GetThemeEnabled())
3802 {
3803 // find ancestor from which to steal background
3804 wxWindow *parent = GetParent();
3805 while (parent && !parent->IsTopLevel())
3806 parent = parent->GetParent();
3807 if (!parent)
3808 parent = (wxWindow*)this;
3809
3810 wxRegionIterator upd( m_updateRegion );
3811 while (upd)
3812 {
3813 GdkRectangle rect;
3814 rect.x = upd.GetX();
3815 rect.y = upd.GetY();
3816 rect.width = upd.GetWidth();
3817 rect.height = upd.GetHeight();
3818
3819 gtk_paint_flat_box( parent->m_widget->style,
3820 pizza->bin_window,
3821 GTK_STATE_NORMAL,
3822 GTK_SHADOW_NONE,
3823 &rect,
3824 parent->m_widget,
3825 (char *)"base",
3826 0, 0, -1, -1 );
3827
3828 upd ++;
3829 }
3830 }
3831 else
3832 #endif
3833
3834 #ifdef __WXGTK20__
3835 {
3836 wxWindowDC dc( (wxWindow*)this );
3837 dc.SetClippingRegion( m_updateRegion );
3838
3839 wxEraseEvent erase_event( GetId(), &dc );
3840 erase_event.SetEventObject( this );
3841
3842 GetEventHandler()->ProcessEvent(erase_event);
3843 }
3844 #else
3845 // if (!m_clearRegion.IsEmpty()) // Always send an erase event under GTK 1.2
3846 {
3847 wxWindowDC dc( (wxWindow*)this );
3848 if (m_clearRegion.IsEmpty())
3849 dc.SetClippingRegion( m_updateRegion );
3850 else
3851 dc.SetClippingRegion( m_clearRegion );
3852
3853 wxEraseEvent erase_event( GetId(), &dc );
3854 erase_event.SetEventObject( this );
3855
3856 if (!GetEventHandler()->ProcessEvent(erase_event))
3857 {
3858 if (!g_eraseGC)
3859 {
3860 g_eraseGC = gdk_gc_new( pizza->bin_window );
3861 gdk_gc_set_fill( g_eraseGC, GDK_SOLID );
3862 }
3863 gdk_gc_set_foreground( g_eraseGC, m_backgroundColour.GetColor() );
3864
3865 wxRegionIterator upd( m_clearRegion );
3866 while (upd)
3867 {
3868 gdk_draw_rectangle( pizza->bin_window, g_eraseGC, 1,
3869 upd.GetX(), upd.GetY(), upd.GetWidth(), upd.GetHeight() );
3870 upd ++;
3871 }
3872 }
3873 m_clearRegion.Clear();
3874 }
3875 #endif
3876
3877 wxNcPaintEvent nc_paint_event( GetId() );
3878 nc_paint_event.SetEventObject( this );
3879 GetEventHandler()->ProcessEvent( nc_paint_event );
3880
3881 wxPaintEvent paint_event( GetId() );
3882 paint_event.SetEventObject( this );
3883 GetEventHandler()->ProcessEvent( paint_event );
3884
3885 m_clipPaintRegion = FALSE;
3886
3887 #ifndef __WXUNIVERSAL__
3888 #ifndef __WXGTK20__
3889 // The following code will result in all window-less widgets
3890 // being redrawn because the wxWindows class is allowed to
3891 // paint over the window-less widgets.
3892
3893 GList *children = pizza->children;
3894 while (children)
3895 {
3896 GtkPizzaChild *child = (GtkPizzaChild*) children->data;
3897 children = children->next;
3898
3899 if (GTK_WIDGET_NO_WINDOW (child->widget) &&
3900 GTK_WIDGET_DRAWABLE (child->widget))
3901 {
3902 // Get intersection of widget area and update region
3903 wxRegion region( m_updateRegion );
3904
3905 GdkEventExpose gdk_event;
3906 gdk_event.type = GDK_EXPOSE;
3907 gdk_event.window = pizza->bin_window;
3908 gdk_event.count = 0;
3909
3910 wxRegionIterator upd( m_updateRegion );
3911 while (upd)
3912 {
3913 GdkRectangle rect;
3914 rect.x = upd.GetX();
3915 rect.y = upd.GetY();
3916 rect.width = upd.GetWidth();
3917 rect.height = upd.GetHeight();
3918
3919 if (gtk_widget_intersect (child->widget, &rect, &gdk_event.area))
3920 {
3921 gtk_widget_event (child->widget, (GdkEvent*) &gdk_event);
3922 }
3923
3924 upd ++;
3925 }
3926 }
3927 }
3928 #endif
3929 #endif
3930
3931 m_updateRegion.Clear();
3932 }
3933
3934 void wxWindowGTK::ClearBackground()
3935 {
3936 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
3937
3938 #ifndef __WXGTK20__
3939 if (m_wxwindow && m_wxwindow->window)
3940 {
3941 m_clearRegion.Clear();
3942 wxSize size( GetClientSize() );
3943 m_clearRegion.Union( 0,0,size.x,size.y );
3944
3945 // Better do this in idle?
3946 GtkUpdate();
3947 }
3948 #endif
3949 }
3950
3951 #if wxUSE_TOOLTIPS
3952 void wxWindowGTK::DoSetToolTip( wxToolTip *tip )
3953 {
3954 wxWindowBase::DoSetToolTip(tip);
3955
3956 if (m_tooltip)
3957 m_tooltip->Apply( (wxWindow *)this );
3958 }
3959
3960 void wxWindowGTK::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
3961 {
3962 gtk_tooltips_set_tip( tips, GetConnectWidget(), wxConvCurrent->cWX2MB(tip), (gchar*) NULL );
3963 }
3964 #endif // wxUSE_TOOLTIPS
3965
3966 void wxWindowGTK::GtkSetBackgroundColour( const wxColour &colour )
3967 {
3968 GdkWindow *window = (GdkWindow*) NULL;
3969 if (m_wxwindow)
3970 window = GTK_PIZZA(m_wxwindow)->bin_window;
3971 else
3972 window = GetConnectWidget()->window;
3973
3974 wxASSERT( window );
3975
3976 // We need the pixel value e.g. for background clearing.
3977 m_backgroundColour.CalcPixel( gdk_window_get_colormap( window ) );
3978
3979 if (m_wxwindow)
3980 {
3981 // wxMSW doesn't clear the window here, either.
3982 gdk_window_set_background( window, m_backgroundColour.GetColor() );
3983 }
3984
3985 ApplyWidgetStyle();
3986 }
3987
3988 bool wxWindowGTK::SetBackgroundColour( const wxColour &colour )
3989 {
3990 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
3991
3992 if (!wxWindowBase::SetBackgroundColour(colour))
3993 return FALSE;
3994
3995 GdkWindow *window = (GdkWindow*) NULL;
3996 if (m_wxwindow)
3997 window = GTK_PIZZA(m_wxwindow)->bin_window;
3998 else
3999 window = GetConnectWidget()->window;
4000
4001 if (!window)
4002 {
4003 // indicate that a new style has been set
4004 // but it couldn't get applied as the
4005 // widget hasn't been realized yet.
4006 m_delayedBackgroundColour = TRUE;
4007 return TRUE;
4008 }
4009 else
4010 {
4011 GtkSetBackgroundColour( colour );
4012 }
4013
4014 return TRUE;
4015 }
4016
4017 void wxWindowGTK::GtkSetForegroundColour( const wxColour &colour )
4018 {
4019 GdkWindow *window = (GdkWindow*) NULL;
4020 if (m_wxwindow)
4021 window = GTK_PIZZA(m_wxwindow)->bin_window;
4022 else
4023 window = GetConnectWidget()->window;
4024
4025 wxASSERT( window );
4026
4027 ApplyWidgetStyle();
4028 }
4029
4030 bool wxWindowGTK::SetForegroundColour( const wxColour &colour )
4031 {
4032 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
4033
4034 if (!wxWindowBase::SetForegroundColour(colour))
4035 {
4036 // don't leave if the GTK widget has just
4037 // been realized
4038 if (!m_delayedForegroundColour) return FALSE;
4039 }
4040
4041 GdkWindow *window = (GdkWindow*) NULL;
4042 if (m_wxwindow)
4043 window = GTK_PIZZA(m_wxwindow)->bin_window;
4044 else
4045 window = GetConnectWidget()->window;
4046
4047 if (!window)
4048 {
4049 // indicate that a new style has been set
4050 // but it couldn't get applied as the
4051 // widget hasn't been realized yet.
4052 m_delayedForegroundColour = TRUE;
4053 }
4054 else
4055 {
4056 GtkSetForegroundColour( colour );
4057 }
4058
4059 return TRUE;
4060 }
4061
4062 #ifdef __WXGTK20__
4063 PangoContext *wxWindowGTK::GtkGetPangoDefaultContext()
4064 {
4065 return gtk_widget_get_pango_context( m_widget );
4066 }
4067
4068 PangoContext *wxWindowGTK::GtkGetPangoX11Context()
4069 {
4070 if (!m_x11Context)
4071 m_x11Context = pango_x_get_context( gdk_display );
4072
4073 return m_x11Context;
4074 }
4075 #endif
4076
4077 GtkStyle *wxWindowGTK::GetWidgetStyle()
4078 {
4079 if (m_widgetStyle)
4080 {
4081 GtkStyle *remake = gtk_style_copy( m_widgetStyle );
4082
4083 // FIXME: no more klass in 2.0
4084 #ifndef __WXGTK20__
4085 remake->klass = m_widgetStyle->klass;
4086 #endif
4087
4088 gtk_style_unref( m_widgetStyle );
4089 m_widgetStyle = remake;
4090 }
4091 else
4092 {
4093 GtkStyle *def = gtk_rc_get_style( m_widget );
4094
4095 if (!def)
4096 def = gtk_widget_get_default_style();
4097
4098 m_widgetStyle = gtk_style_copy( def );
4099
4100 // FIXME: no more klass in 2.0
4101 #ifndef __WXGTK20__
4102 m_widgetStyle->klass = def->klass;
4103 #endif
4104 }
4105
4106 return m_widgetStyle;
4107 }
4108
4109 void wxWindowGTK::SetWidgetStyle()
4110 {
4111 #if DISABLE_STYLE_IF_BROKEN_THEME
4112 if (m_widget->style->engine_data)
4113 {
4114 static bool s_warningPrinted = FALSE;
4115 if (!s_warningPrinted)
4116 {
4117 printf( "wxWindows warning: Widget styles disabled due to buggy GTK theme.\n" );
4118 s_warningPrinted = TRUE;
4119 }
4120 m_widgetStyle = m_widget->style;
4121 return;
4122 }
4123 #endif
4124
4125 GtkStyle *style = GetWidgetStyle();
4126
4127 if (m_font != wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ))
4128 {
4129 #ifdef __WXGTK20__
4130 pango_font_description_free( style->font_desc );
4131 style->font_desc = pango_font_description_copy( m_font.GetNativeFontInfo()->description );
4132 #else
4133 gdk_font_unref( style->font );
4134 style->font = gdk_font_ref( m_font.GetInternalFont( 1.0 ) );
4135 #endif
4136 }
4137
4138 if (m_foregroundColour.Ok())
4139 {
4140 m_foregroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) );
4141 if (m_foregroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT))
4142 {
4143 style->fg[GTK_STATE_NORMAL] = *m_foregroundColour.GetColor();
4144 style->fg[GTK_STATE_PRELIGHT] = *m_foregroundColour.GetColor();
4145 style->fg[GTK_STATE_ACTIVE] = *m_foregroundColour.GetColor();
4146 }
4147 else
4148 {
4149 // Try to restore the gtk default style. This is still a little
4150 // oversimplified for what is probably really needed here for controls
4151 // other than buttons, but is better than not being able to (re)set a
4152 // control's foreground colour to *wxBLACK -- RL
4153 GtkStyle *def = gtk_rc_get_style( m_widget );
4154
4155 if (!def)
4156 def = gtk_widget_get_default_style();
4157
4158 style->fg[GTK_STATE_NORMAL] = def->fg[GTK_STATE_NORMAL];
4159 style->fg[GTK_STATE_PRELIGHT] = def->fg[GTK_STATE_PRELIGHT];
4160 style->fg[GTK_STATE_ACTIVE] = def->fg[GTK_STATE_ACTIVE];
4161 }
4162 }
4163
4164 if (m_backgroundColour.Ok())
4165 {
4166 m_backgroundColour.CalcPixel( gtk_widget_get_colormap( m_widget ) );
4167 if (m_backgroundColour != wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE))
4168 {
4169 style->bg[GTK_STATE_NORMAL] = *m_backgroundColour.GetColor();
4170 style->base[GTK_STATE_NORMAL] = *m_backgroundColour.GetColor();
4171 style->bg[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor();
4172 style->base[GTK_STATE_PRELIGHT] = *m_backgroundColour.GetColor();
4173 style->bg[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor();
4174 style->base[GTK_STATE_ACTIVE] = *m_backgroundColour.GetColor();
4175 style->bg[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor();
4176 style->base[GTK_STATE_INSENSITIVE] = *m_backgroundColour.GetColor();
4177 }
4178 else
4179 {
4180 // Try to restore the gtk default style. This is still a little
4181 // oversimplified for what is probably really needed here for controls
4182 // other than buttons, but is better than not being able to (re)set a
4183 // control's background colour to default grey and means resetting a
4184 // button to wxSYS_COLOUR_BTNFACE will restore its usual highlighting
4185 // behavior -- RL
4186 GtkStyle *def = gtk_rc_get_style( m_widget );
4187
4188 if (!def)
4189 def = gtk_widget_get_default_style();
4190
4191 style->bg[GTK_STATE_NORMAL] = def->bg[GTK_STATE_NORMAL];
4192 style->base[GTK_STATE_NORMAL] = def->base[GTK_STATE_NORMAL];
4193 style->bg[GTK_STATE_PRELIGHT] = def->bg[GTK_STATE_PRELIGHT];
4194 style->base[GTK_STATE_PRELIGHT] = def->base[GTK_STATE_PRELIGHT];
4195 style->bg[GTK_STATE_ACTIVE] = def->bg[GTK_STATE_ACTIVE];
4196 style->base[GTK_STATE_ACTIVE] = def->base[GTK_STATE_ACTIVE];
4197 style->bg[GTK_STATE_INSENSITIVE] = def->bg[GTK_STATE_INSENSITIVE];
4198 style->base[GTK_STATE_INSENSITIVE] = def->base[GTK_STATE_INSENSITIVE];
4199 }
4200 }
4201 }
4202
4203 void wxWindowGTK::ApplyWidgetStyle()
4204 {
4205 }
4206
4207 //-----------------------------------------------------------------------------
4208 // Pop-up menu stuff
4209 //-----------------------------------------------------------------------------
4210
4211 #if wxUSE_MENUS_NATIVE
4212
4213 extern "C"
4214 void gtk_pop_hide_callback( GtkWidget *WXUNUSED(widget), bool* is_waiting )
4215 {
4216 *is_waiting = FALSE;
4217 }
4218
4219 static void SetInvokingWindow( wxMenu *menu, wxWindowGTK *win )
4220 {
4221 menu->SetInvokingWindow( win );
4222 wxMenuItemList::compatibility_iterator node = menu->GetMenuItems().GetFirst();
4223 while (node)
4224 {
4225 wxMenuItem *menuitem = node->GetData();
4226 if (menuitem->IsSubMenu())
4227 {
4228 SetInvokingWindow( menuitem->GetSubMenu(), win );
4229 }
4230
4231 node = node->GetNext();
4232 }
4233 }
4234
4235 // used to pass the coordinates from wxWindowGTK::DoPopupMenu() to
4236 // wxPopupMenuPositionCallback()
4237 //
4238 // should be safe even in the MT case as the user can hardly popup 2 menus
4239 // simultaneously, can he?
4240 static gint gs_pop_x = 0;
4241 static gint gs_pop_y = 0;
4242
4243 extern "C" void wxPopupMenuPositionCallback( GtkMenu *menu,
4244 gint *x, gint *y,
4245 #ifdef __WXGTK20__
4246 gboolean * WXUNUSED(whatever),
4247 #endif
4248 gpointer WXUNUSED(user_data) )
4249 {
4250 // ensure that the menu appears entirely on screen
4251 GtkRequisition req;
4252 gtk_widget_get_child_requisition(GTK_WIDGET(menu), &req);
4253
4254 wxSize sizeScreen = wxGetDisplaySize();
4255
4256 gint xmax = sizeScreen.x - req.width,
4257 ymax = sizeScreen.y - req.height;
4258
4259 *x = gs_pop_x < xmax ? gs_pop_x : xmax;
4260 *y = gs_pop_y < ymax ? gs_pop_y : ymax;
4261 }
4262
4263 bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y )
4264 {
4265 wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid window") );
4266
4267 wxCHECK_MSG( menu != NULL, FALSE, wxT("invalid popup-menu") );
4268
4269 SetInvokingWindow( menu, this );
4270
4271 menu->UpdateUI();
4272
4273 gs_pop_x = x;
4274 gs_pop_y = y;
4275 ClientToScreen( &gs_pop_x, &gs_pop_y );
4276
4277 bool is_waiting = TRUE;
4278
4279 gtk_signal_connect( GTK_OBJECT(menu->m_menu),
4280 "hide",
4281 GTK_SIGNAL_FUNC(gtk_pop_hide_callback),
4282 (gpointer)&is_waiting );
4283
4284 gtk_menu_popup(
4285 GTK_MENU(menu->m_menu),
4286 (GtkWidget *) NULL, // parent menu shell
4287 (GtkWidget *) NULL, // parent menu item
4288 wxPopupMenuPositionCallback, // function to position it
4289 NULL, // client data
4290 0, // button used to activate it
4291 #ifdef __WXGTK20__
4292 gtk_get_current_event_time()
4293 #else
4294 gs_timeLastClick // the time of activation
4295 #endif
4296 );
4297
4298 while (is_waiting)
4299 {
4300 gtk_main_iteration();
4301 }
4302
4303 return TRUE;
4304 }
4305
4306 #endif // wxUSE_MENUS_NATIVE
4307
4308 #if wxUSE_DRAG_AND_DROP
4309
4310 void wxWindowGTK::SetDropTarget( wxDropTarget *dropTarget )
4311 {
4312 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4313
4314 GtkWidget *dnd_widget = GetConnectWidget();
4315
4316 if (m_dropTarget) m_dropTarget->UnregisterWidget( dnd_widget );
4317
4318 if (m_dropTarget) delete m_dropTarget;
4319 m_dropTarget = dropTarget;
4320
4321 if (m_dropTarget) m_dropTarget->RegisterWidget( dnd_widget );
4322 }
4323
4324 #endif // wxUSE_DRAG_AND_DROP
4325
4326 GtkWidget* wxWindowGTK::GetConnectWidget()
4327 {
4328 GtkWidget *connect_widget = m_widget;
4329 if (m_wxwindow) connect_widget = m_wxwindow;
4330
4331 return connect_widget;
4332 }
4333
4334 bool wxWindowGTK::IsOwnGtkWindow( GdkWindow *window )
4335 {
4336 if (m_wxwindow)
4337 return (window == GTK_PIZZA(m_wxwindow)->bin_window);
4338
4339 return (window == m_widget->window);
4340 }
4341
4342 bool wxWindowGTK::SetFont( const wxFont &font )
4343 {
4344 if (!wxWindowBase::SetFont(font) || !m_widget)
4345 {
4346 return FALSE;
4347 }
4348
4349 wxColour sysbg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
4350 if ( sysbg == m_backgroundColour )
4351 {
4352 m_backgroundColour = wxNullColour;
4353 ApplyWidgetStyle();
4354 m_backgroundColour = sysbg;
4355 }
4356 else
4357 {
4358 ApplyWidgetStyle();
4359 }
4360
4361 return TRUE;
4362 }
4363
4364 void wxWindowGTK::DoCaptureMouse()
4365 {
4366 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4367
4368 GdkWindow *window = (GdkWindow*) NULL;
4369 if (m_wxwindow)
4370 window = GTK_PIZZA(m_wxwindow)->bin_window;
4371 else
4372 window = GetConnectWidget()->window;
4373
4374 wxCHECK_RET( window, _T("CaptureMouse() failed") );
4375
4376 wxCursor* cursor = & m_cursor;
4377 if (!cursor->Ok())
4378 cursor = wxSTANDARD_CURSOR;
4379
4380 gdk_pointer_grab( window, FALSE,
4381 (GdkEventMask)
4382 (GDK_BUTTON_PRESS_MASK |
4383 GDK_BUTTON_RELEASE_MASK |
4384 GDK_POINTER_MOTION_HINT_MASK |
4385 GDK_POINTER_MOTION_MASK),
4386 (GdkWindow *) NULL,
4387 cursor->GetCursor(),
4388 (guint32)GDK_CURRENT_TIME );
4389 g_captureWindow = this;
4390 g_captureWindowHasMouse = TRUE;
4391 }
4392
4393 void wxWindowGTK::DoReleaseMouse()
4394 {
4395 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4396
4397 wxCHECK_RET( g_captureWindow, wxT("can't release mouse - not captured") );
4398
4399 g_captureWindow = (wxWindowGTK*) NULL;
4400
4401 GdkWindow *window = (GdkWindow*) NULL;
4402 if (m_wxwindow)
4403 window = GTK_PIZZA(m_wxwindow)->bin_window;
4404 else
4405 window = GetConnectWidget()->window;
4406
4407 if (!window)
4408 return;
4409
4410 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
4411 }
4412
4413 /* static */
4414 wxWindow *wxWindowBase::GetCapture()
4415 {
4416 return (wxWindow *)g_captureWindow;
4417 }
4418
4419 bool wxWindowGTK::IsRetained() const
4420 {
4421 return FALSE;
4422 }
4423
4424 void wxWindowGTK::SetScrollbar( int orient, int pos, int thumbVisible,
4425 int range, bool refresh )
4426 {
4427 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4428
4429 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4430
4431 m_hasScrolling = TRUE;
4432
4433 if (orient == wxHORIZONTAL)
4434 {
4435 float fpos = (float)pos;
4436 float frange = (float)range;
4437 float fthumb = (float)thumbVisible;
4438 if (fpos > frange-fthumb) fpos = frange-fthumb;
4439 if (fpos < 0.0) fpos = 0.0;
4440
4441 if ((fabs(frange-m_hAdjust->upper) < 0.2) &&
4442 (fabs(fthumb-m_hAdjust->page_size) < 0.2))
4443 {
4444 SetScrollPos( orient, pos, refresh );
4445 return;
4446 }
4447
4448 m_oldHorizontalPos = fpos;
4449
4450 m_hAdjust->lower = 0.0;
4451 m_hAdjust->upper = frange;
4452 m_hAdjust->value = fpos;
4453 m_hAdjust->step_increment = 1.0;
4454 m_hAdjust->page_increment = (float)(wxMax(fthumb,0));
4455 m_hAdjust->page_size = fthumb;
4456 }
4457 else
4458 {
4459 float fpos = (float)pos;
4460 float frange = (float)range;
4461 float fthumb = (float)thumbVisible;
4462 if (fpos > frange-fthumb) fpos = frange-fthumb;
4463 if (fpos < 0.0) fpos = 0.0;
4464
4465 if ((fabs(frange-m_vAdjust->upper) < 0.2) &&
4466 (fabs(fthumb-m_vAdjust->page_size) < 0.2))
4467 {
4468 SetScrollPos( orient, pos, refresh );
4469 return;
4470 }
4471
4472 m_oldVerticalPos = fpos;
4473
4474 m_vAdjust->lower = 0.0;
4475 m_vAdjust->upper = frange;
4476 m_vAdjust->value = fpos;
4477 m_vAdjust->step_increment = 1.0;
4478 m_vAdjust->page_increment = (float)(wxMax(fthumb,0));
4479 m_vAdjust->page_size = fthumb;
4480 }
4481
4482 if (orient == wxHORIZONTAL)
4483 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "changed" );
4484 else
4485 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "changed" );
4486 }
4487
4488 void wxWindowGTK::SetScrollPos( int orient, int pos, bool WXUNUSED(refresh) )
4489 {
4490 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4491
4492 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4493
4494 if (orient == wxHORIZONTAL)
4495 {
4496 float fpos = (float)pos;
4497 if (fpos > m_hAdjust->upper - m_hAdjust->page_size) fpos = m_hAdjust->upper - m_hAdjust->page_size;
4498 if (fpos < 0.0) fpos = 0.0;
4499 m_oldHorizontalPos = fpos;
4500
4501 if (fabs(fpos-m_hAdjust->value) < 0.2) return;
4502 m_hAdjust->value = fpos;
4503 }
4504 else
4505 {
4506 float fpos = (float)pos;
4507 if (fpos > m_vAdjust->upper - m_vAdjust->page_size) fpos = m_vAdjust->upper - m_vAdjust->page_size;
4508 if (fpos < 0.0) fpos = 0.0;
4509 m_oldVerticalPos = fpos;
4510
4511 if (fabs(fpos-m_vAdjust->value) < 0.2) return;
4512 m_vAdjust->value = fpos;
4513 }
4514
4515 if (m_wxwindow->window)
4516 {
4517 if (orient == wxHORIZONTAL)
4518 {
4519 gtk_signal_disconnect_by_func( GTK_OBJECT(m_hAdjust),
4520 (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this );
4521
4522 gtk_signal_emit_by_name( GTK_OBJECT(m_hAdjust), "value_changed" );
4523
4524 gtk_signal_connect( GTK_OBJECT(m_hAdjust), "value_changed",
4525 (GtkSignalFunc) gtk_window_hscroll_callback, (gpointer) this );
4526 }
4527 else
4528 {
4529 gtk_signal_disconnect_by_func( GTK_OBJECT(m_vAdjust),
4530 (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this );
4531
4532 gtk_signal_emit_by_name( GTK_OBJECT(m_vAdjust), "value_changed" );
4533
4534 gtk_signal_connect( GTK_OBJECT(m_vAdjust), "value_changed",
4535 (GtkSignalFunc) gtk_window_vscroll_callback, (gpointer) this );
4536 }
4537 }
4538 }
4539
4540 int wxWindowGTK::GetScrollThumb( int orient ) const
4541 {
4542 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4543
4544 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4545
4546 if (orient == wxHORIZONTAL)
4547 return (int)(m_hAdjust->page_size+0.5);
4548 else
4549 return (int)(m_vAdjust->page_size+0.5);
4550 }
4551
4552 int wxWindowGTK::GetScrollPos( int orient ) const
4553 {
4554 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4555
4556 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4557
4558 if (orient == wxHORIZONTAL)
4559 return (int)(m_hAdjust->value+0.5);
4560 else
4561 return (int)(m_vAdjust->value+0.5);
4562 }
4563
4564 int wxWindowGTK::GetScrollRange( int orient ) const
4565 {
4566 wxCHECK_MSG( m_widget != NULL, 0, wxT("invalid window") );
4567
4568 wxCHECK_MSG( m_wxwindow != NULL, 0, wxT("window needs client area for scrolling") );
4569
4570 if (orient == wxHORIZONTAL)
4571 return (int)(m_hAdjust->upper+0.5);
4572 else
4573 return (int)(m_vAdjust->upper+0.5);
4574 }
4575
4576 void wxWindowGTK::ScrollWindow( int dx, int dy, const wxRect* WXUNUSED(rect) )
4577 {
4578 wxCHECK_RET( m_widget != NULL, wxT("invalid window") );
4579
4580 wxCHECK_RET( m_wxwindow != NULL, wxT("window needs client area for scrolling") );
4581
4582 // No scrolling requested.
4583 if ((dx == 0) && (dy == 0)) return;
4584
4585 #ifndef __WXGTK20__
4586 if (!m_updateRegion.IsEmpty())
4587 {
4588 m_updateRegion.Offset( dx, dy );
4589
4590 int cw = 0;
4591 int ch = 0;
4592 GetClientSize( &cw, &ch );
4593 m_updateRegion.Intersect( 0, 0, cw, ch );
4594 }
4595
4596 if (!m_clearRegion.IsEmpty())
4597 {
4598 m_clearRegion.Offset( dx, dy );
4599
4600 int cw = 0;
4601 int ch = 0;
4602 GetClientSize( &cw, &ch );
4603 m_clearRegion.Intersect( 0, 0, cw, ch );
4604 }
4605 #endif
4606
4607 m_clipPaintRegion = TRUE;
4608
4609 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow), -dx, -dy );
4610
4611 m_clipPaintRegion = FALSE;
4612 }
4613
4614
4615 // Find the wxWindow at the current mouse position, also returning the mouse
4616 // position.
4617 wxWindow* wxFindWindowAtPointer(wxPoint& pt)
4618 {
4619 pt = wxGetMousePosition();
4620 wxWindow* found = wxFindWindowAtPoint(pt);
4621 return found;
4622 }
4623
4624 // Get the current mouse position.
4625 wxPoint wxGetMousePosition()
4626 {
4627 /* This crashes when used within wxHelpContext,
4628 so we have to use the X-specific implementation below.
4629 gint x, y;
4630 GdkModifierType *mask;
4631 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4632
4633 return wxPoint(x, y);
4634 */
4635
4636 int x, y;
4637 GdkWindow* windowAtPtr = gdk_window_at_pointer(& x, & y);
4638
4639 Display *display = windowAtPtr ? GDK_WINDOW_XDISPLAY(windowAtPtr) : GDK_DISPLAY();
4640 Window rootWindow = RootWindowOfScreen (DefaultScreenOfDisplay(display));
4641 Window rootReturn, childReturn;
4642 int rootX, rootY, winX, winY;
4643 unsigned int maskReturn;
4644
4645 XQueryPointer (display,
4646 rootWindow,
4647 &rootReturn,
4648 &childReturn,
4649 &rootX, &rootY, &winX, &winY, &maskReturn);
4650 return wxPoint(rootX, rootY);
4651
4652 }
4653
4654 // ----------------------------------------------------------------------------
4655 // wxDCModule
4656 // ----------------------------------------------------------------------------
4657
4658 class wxWinModule : public wxModule
4659 {
4660 public:
4661 bool OnInit();
4662 void OnExit();
4663
4664 private:
4665 DECLARE_DYNAMIC_CLASS(wxWinModule)
4666 };
4667
4668 IMPLEMENT_DYNAMIC_CLASS(wxWinModule, wxModule)
4669
4670 bool wxWinModule::OnInit()
4671 {
4672 // g_eraseGC = gdk_gc_new( GDK_ROOT_PARENT() );
4673 // gdk_gc_set_fill( g_eraseGC, GDK_SOLID );
4674
4675 return TRUE;
4676 }
4677
4678 void wxWinModule::OnExit()
4679 {
4680 if (g_eraseGC)
4681 gdk_gc_unref( g_eraseGC );
4682 }
4683
4684 // vi:sts=4:sw=4:et