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