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