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