1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/window.cpp
3 // Purpose: wxWindowGTK implementation
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling, Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
14 #define XWarpPointer XWARPPOINTER
17 #include "wx/window.h"
22 #include "wx/toplevel.h"
23 #include "wx/dcclient.h"
25 #include "wx/settings.h"
26 #include "wx/msgdlg.h"
31 #include "wx/tooltip.h"
33 #include "wx/fontutil.h"
34 #include "wx/sysopt.h"
38 #include "wx/gtk/private.h"
39 #include "wx/gtk/private/win_gtk.h"
40 #include "wx/gtk/private/event.h"
41 using namespace wxGTKImpl
;
43 #ifdef GDK_WINDOWING_X11
45 #include "wx/x11/private/wrapxkb.h"
50 #include <gdk/gdkkeysyms.h>
51 #if GTK_CHECK_VERSION(3,0,0)
52 #include <gdk/gdkkeysyms-compat.h>
55 // gdk_window_set_composited() is only supported since 2.12
56 #define wxGTK_VERSION_REQUIRED_FOR_COMPOSITING 2,12,0
57 #define wxGTK_HAS_COMPOSITING_SUPPORT GTK_CHECK_VERSION(2,12,0)
59 //-----------------------------------------------------------------------------
60 // documentation on internals
61 //-----------------------------------------------------------------------------
64 I have been asked several times about writing some documentation about
65 the GTK port of wxWidgets, especially its internal structures. Obviously,
66 you cannot understand wxGTK without knowing a little about the GTK, but
67 some more information about what the wxWindow, which is the base class
68 for all other window classes, does seems required as well.
72 What does wxWindow do? It contains the common interface for the following
73 jobs of its descendants:
75 1) Define the rudimentary behaviour common to all window classes, such as
76 resizing, intercepting user input (so as to make it possible to use these
77 events for special purposes in a derived class), window names etc.
79 2) Provide the possibility to contain and manage children, if the derived
80 class is allowed to contain children, which holds true for those window
81 classes which do not display a native GTK widget. To name them, these
82 classes are wxPanel, wxScrolledWindow, wxDialog, wxFrame. The MDI frame-
83 work classes are a special case and are handled a bit differently from
84 the rest. The same holds true for the wxNotebook class.
86 3) Provide the possibility to draw into a client area of a window. This,
87 too, only holds true for classes that do not display a native GTK widget
90 4) Provide the entire mechanism for scrolling widgets. This actual inter-
91 face for this is usually in wxScrolledWindow, but the GTK implementation
94 5) A multitude of helper or extra methods for special purposes, such as
95 Drag'n'Drop, managing validators etc.
97 6) Display a border (sunken, raised, simple or none).
99 Normally one might expect, that one wxWidgets window would always correspond
100 to one GTK widget. Under GTK, there is no such all-round widget that has all
101 the functionality. Moreover, the GTK defines a client area as a different
102 widget from the actual widget you are handling. Last but not least some
103 special classes (e.g. wxFrame) handle different categories of widgets and
104 still have the possibility to draw something in the client area.
105 It was therefore required to write a special purpose GTK widget, that would
106 represent a client area in the sense of wxWidgets capable to do the jobs
107 2), 3) and 4). I have written this class and it resides in win_gtk.c of
110 All windows must have a widget, with which they interact with other under-
111 lying GTK widgets. It is this widget, e.g. that has to be resized etc and
112 the wxWindow class has a member variable called m_widget which holds a
113 pointer to this widget. When the window class represents a GTK native widget,
114 this is (in most cases) the only GTK widget the class manages. E.g. the
115 wxStaticText class handles only a GtkLabel widget a pointer to which you
116 can find in m_widget (defined in wxWindow)
118 When the class has a client area for drawing into and for containing children
119 it has to handle the client area widget (of the type wxPizza, defined in
120 win_gtk.cpp), but there could be any number of widgets, handled by a class.
121 The common rule for all windows is only, that the widget that interacts with
122 the rest of GTK must be referenced in m_widget and all other widgets must be
123 children of this widget on the GTK level. The top-most widget, which also
124 represents the client area, must be in the m_wxwindow field and must be of
127 As I said, the window classes that display a GTK native widget only have
128 one widget, so in the case of e.g. the wxButton class m_widget holds a
129 pointer to a GtkButton widget. But windows with client areas (for drawing
130 and children) have a m_widget field that is a pointer to a GtkScrolled-
131 Window and a m_wxwindow field that is pointer to a wxPizza and this
132 one is (in the GTK sense) a child of the GtkScrolledWindow.
134 If the m_wxwindow field is set, then all input to this widget is inter-
135 cepted and sent to the wxWidgets class. If not, all input to the widget
136 that gets pointed to by m_widget gets intercepted and sent to the class.
140 The design of scrolling in wxWidgets is markedly different from that offered
141 by the GTK itself and therefore we cannot simply take it as it is. In GTK,
142 clicking on a scrollbar belonging to scrolled window will inevitably move
143 the window. In wxWidgets, the scrollbar will only emit an event, send this
144 to (normally) a wxScrolledWindow and that class will call ScrollWindow()
145 which actually moves the window and its sub-windows. Note that wxPizza
146 memorizes how much it has been scrolled but that wxWidgets forgets this
147 so that the two coordinates systems have to be kept in synch. This is done
148 in various places using the pizza->m_scroll_x and pizza->m_scroll_y values.
152 Singularly the most broken code in GTK is the code that is supposed to
153 inform subwindows (child windows) about new positions. Very often, duplicate
154 events are sent without changes in size or position, equally often no
155 events are sent at all (All this is due to a bug in the GtkContainer code
156 which got fixed in GTK 1.2.6). For that reason, wxGTK completely ignores
157 GTK's own system and it simply waits for size events for toplevel windows
158 and then iterates down the respective size events to all window. This has
159 the disadvantage that windows might get size events before the GTK widget
160 actually has the reported size. This doesn't normally pose any problem, but
161 the OpenGL drawing routines rely on correct behaviour. Therefore, I have
162 added the m_nativeSizeEvents flag, which is true only for the OpenGL canvas,
163 i.e. the wxGLCanvas will emit a size event, when (and not before) the X11
164 window that is used for OpenGL output really has that size (as reported by
169 If someone at some point of time feels the immense desire to have a look at,
170 change or attempt to optimise the Refresh() logic, this person will need an
171 intimate understanding of what "draw" and "expose" events are and what
172 they are used for, in particular when used in connection with GTK's
173 own windowless widgets. Beware.
177 Cursors, too, have been a constant source of pleasure. The main difficulty
178 is that a GdkWindow inherits a cursor if the programmer sets a new cursor
179 for the parent. To prevent this from doing too much harm, SetCursor calls
180 GTKUpdateCursor, which will recursively re-set the cursors of all child windows.
181 Also don't forget that cursors (like much else) are connected to GdkWindows,
182 not GtkWidgets and that the "window" field of a GtkWidget might very well
183 point to the GdkWindow of the parent widget (-> "window-less widget") and
184 that the two obviously have very different meanings.
187 //-----------------------------------------------------------------------------
189 //-----------------------------------------------------------------------------
191 // Don't allow event propagation during drag
192 bool g_blockEventsOnDrag
;
193 // Don't allow mouse event propagation during scroll
194 bool g_blockEventsOnScroll
;
195 extern wxCursor g_globalCursor
;
197 // mouse capture state: the window which has it and if the mouse is currently
199 static wxWindowGTK
*g_captureWindow
= NULL
;
200 static bool g_captureWindowHasMouse
= false;
202 // The window that currently has focus:
203 static wxWindowGTK
*gs_currentFocus
= NULL
;
204 // The window that is scheduled to get focus in the next event loop iteration
205 // or NULL if there's no pending focus change:
206 static wxWindowGTK
*gs_pendingFocus
= NULL
;
208 // the window that has deferred focus-out event pending, if any (see
209 // GTKAddDeferredFocusOut() for details)
210 static wxWindowGTK
*gs_deferredFocusOut
= NULL
;
212 // global variables because GTK+ DnD want to have the
213 // mouse event that caused it
214 GdkEvent
*g_lastMouseEvent
= NULL
;
215 int g_lastButtonNumber
= 0;
217 //-----------------------------------------------------------------------------
219 //-----------------------------------------------------------------------------
221 // the trace mask used for the focus debugging messages
222 #define TRACE_FOCUS wxT("focus")
224 //-----------------------------------------------------------------------------
225 // "size_request" of m_widget
226 //-----------------------------------------------------------------------------
230 wxgtk_window_size_request_callback(GtkWidget
* WXUNUSED(widget
),
231 GtkRequisition
*requisition
,
235 win
->GetSize( &w
, &h
);
241 requisition
->height
= h
;
242 requisition
->width
= w
;
246 //-----------------------------------------------------------------------------
247 // "expose_event" of m_wxwindow
248 //-----------------------------------------------------------------------------
252 gtk_window_expose_callback( GtkWidget
*,
253 GdkEventExpose
*gdk_event
,
256 if (gdk_event
->window
== win
->GTKGetDrawingWindow())
258 win
->GetUpdateRegion() = wxRegion( gdk_event
->region
);
259 win
->GtkSendPaintEvents();
261 // Let parent window draw window-less widgets
266 #ifndef __WXUNIVERSAL__
267 //-----------------------------------------------------------------------------
268 // "expose_event" from m_wxwindow->parent, for drawing border
269 //-----------------------------------------------------------------------------
273 expose_event_border(GtkWidget
* widget
, GdkEventExpose
* gdk_event
, wxWindow
* win
)
275 if (gdk_event
->window
!= gtk_widget_get_parent_window(win
->m_wxwindow
))
282 gtk_widget_get_allocation(win
->m_wxwindow
, &alloc
);
283 const int x
= alloc
.x
;
284 const int y
= alloc
.y
;
285 const int w
= alloc
.width
;
286 const int h
= alloc
.height
;
288 if (w
<= 0 || h
<= 0)
291 if (win
->HasFlag(wxBORDER_SIMPLE
))
293 gdk_draw_rectangle(gdk_event
->window
,
294 gtk_widget_get_style(widget
)->black_gc
, false, x
, y
, w
- 1, h
- 1);
298 GtkShadowType shadow
= GTK_SHADOW_IN
;
299 if (win
->HasFlag(wxBORDER_RAISED
))
300 shadow
= GTK_SHADOW_OUT
;
302 // Style detail to use
304 if (win
->m_widget
== win
->m_wxwindow
)
305 // for non-scrollable wxWindows
308 // for scrollable ones
311 // clip rect is required to avoid painting background
312 // over upper left (w,h) of parent window
313 GdkRectangle clipRect
= { x
, y
, w
, h
};
315 gtk_widget_get_style(win
->m_wxwindow
), gdk_event
->window
, GTK_STATE_NORMAL
,
316 shadow
, &clipRect
, wxGTKPrivate::GetEntryWidget(), detail
, x
, y
, w
, h
);
322 //-----------------------------------------------------------------------------
323 // "parent_set" from m_wxwindow
324 //-----------------------------------------------------------------------------
328 parent_set(GtkWidget
* widget
, GtkWidget
* old_parent
, wxWindow
* win
)
332 g_signal_handlers_disconnect_by_func(
333 old_parent
, (void*)expose_event_border
, win
);
335 GtkWidget
* parent
= gtk_widget_get_parent(widget
);
338 g_signal_connect_after(parent
, "expose_event",
339 G_CALLBACK(expose_event_border
), win
);
343 #endif // !__WXUNIVERSAL__
345 //-----------------------------------------------------------------------------
346 // "key_press_event" from any window
347 //-----------------------------------------------------------------------------
349 // set WXTRACE to this to see the key event codes on the console
350 #define TRACE_KEYS wxT("keyevent")
352 // translates an X key symbol to WXK_XXX value
354 // if isChar is true it means that the value returned will be used for EVT_CHAR
355 // event and then we choose the logical WXK_XXX, i.e. '/' for GDK_KP_Divide,
356 // for example, while if it is false it means that the value is going to be
357 // used for KEY_DOWN/UP events and then we translate GDK_KP_Divide to
359 static long wxTranslateKeySymToWXKey(KeySym keysym
, bool isChar
)
365 // Shift, Control and Alt don't generate the CHAR events at all
368 key_code
= isChar
? 0 : WXK_SHIFT
;
372 key_code
= isChar
? 0 : WXK_CONTROL
;
380 key_code
= isChar
? 0 : WXK_ALT
;
383 // neither do the toggle modifies
384 case GDK_Scroll_Lock
:
385 key_code
= isChar
? 0 : WXK_SCROLL
;
389 key_code
= isChar
? 0 : WXK_CAPITAL
;
393 key_code
= isChar
? 0 : WXK_NUMLOCK
;
397 // various other special keys
410 case GDK_ISO_Left_Tab
:
417 key_code
= WXK_RETURN
;
421 key_code
= WXK_CLEAR
;
425 key_code
= WXK_PAUSE
;
429 key_code
= WXK_SELECT
;
433 key_code
= WXK_PRINT
;
437 key_code
= WXK_EXECUTE
;
441 key_code
= WXK_ESCAPE
;
444 // cursor and other extended keyboard keys
446 key_code
= WXK_DELETE
;
462 key_code
= WXK_RIGHT
;
469 case GDK_Prior
: // == GDK_Page_Up
470 key_code
= WXK_PAGEUP
;
473 case GDK_Next
: // == GDK_Page_Down
474 key_code
= WXK_PAGEDOWN
;
486 key_code
= WXK_INSERT
;
501 key_code
= (isChar
? '0' : int(WXK_NUMPAD0
)) + keysym
- GDK_KP_0
;
505 key_code
= isChar
? ' ' : int(WXK_NUMPAD_SPACE
);
509 key_code
= isChar
? WXK_TAB
: WXK_NUMPAD_TAB
;
513 key_code
= isChar
? WXK_RETURN
: WXK_NUMPAD_ENTER
;
517 key_code
= isChar
? WXK_F1
: WXK_NUMPAD_F1
;
521 key_code
= isChar
? WXK_F2
: WXK_NUMPAD_F2
;
525 key_code
= isChar
? WXK_F3
: WXK_NUMPAD_F3
;
529 key_code
= isChar
? WXK_F4
: WXK_NUMPAD_F4
;
533 key_code
= isChar
? WXK_HOME
: WXK_NUMPAD_HOME
;
537 key_code
= isChar
? WXK_LEFT
: WXK_NUMPAD_LEFT
;
541 key_code
= isChar
? WXK_UP
: WXK_NUMPAD_UP
;
545 key_code
= isChar
? WXK_RIGHT
: WXK_NUMPAD_RIGHT
;
549 key_code
= isChar
? WXK_DOWN
: WXK_NUMPAD_DOWN
;
552 case GDK_KP_Prior
: // == GDK_KP_Page_Up
553 key_code
= isChar
? WXK_PAGEUP
: WXK_NUMPAD_PAGEUP
;
556 case GDK_KP_Next
: // == GDK_KP_Page_Down
557 key_code
= isChar
? WXK_PAGEDOWN
: WXK_NUMPAD_PAGEDOWN
;
561 key_code
= isChar
? WXK_END
: WXK_NUMPAD_END
;
565 key_code
= isChar
? WXK_HOME
: WXK_NUMPAD_BEGIN
;
569 key_code
= isChar
? WXK_INSERT
: WXK_NUMPAD_INSERT
;
573 key_code
= isChar
? WXK_DELETE
: WXK_NUMPAD_DELETE
;
577 key_code
= isChar
? '=' : int(WXK_NUMPAD_EQUAL
);
580 case GDK_KP_Multiply
:
581 key_code
= isChar
? '*' : int(WXK_NUMPAD_MULTIPLY
);
585 key_code
= isChar
? '+' : int(WXK_NUMPAD_ADD
);
588 case GDK_KP_Separator
:
589 // FIXME: what is this?
590 key_code
= isChar
? '.' : int(WXK_NUMPAD_SEPARATOR
);
593 case GDK_KP_Subtract
:
594 key_code
= isChar
? '-' : int(WXK_NUMPAD_SUBTRACT
);
598 key_code
= isChar
? '.' : int(WXK_NUMPAD_DECIMAL
);
602 key_code
= isChar
? '/' : int(WXK_NUMPAD_DIVIDE
);
619 key_code
= WXK_F1
+ keysym
- GDK_F1
;
629 static inline bool wxIsAsciiKeysym(KeySym ks
)
634 static void wxFillOtherKeyEventFields(wxKeyEvent
& event
,
636 GdkEventKey
*gdk_event
)
638 event
.SetTimestamp( gdk_event
->time
);
639 event
.SetId(win
->GetId());
641 event
.m_shiftDown
= (gdk_event
->state
& GDK_SHIFT_MASK
) != 0;
642 event
.m_controlDown
= (gdk_event
->state
& GDK_CONTROL_MASK
) != 0;
643 event
.m_altDown
= (gdk_event
->state
& GDK_MOD1_MASK
) != 0;
644 event
.m_metaDown
= (gdk_event
->state
& GDK_META_MASK
) != 0;
646 // Normally we take the state of modifiers directly from the low level GDK
647 // event but unfortunately GDK uses a different convention from MSW for the
648 // key events corresponding to the modifier keys themselves: in it, when
649 // e.g. Shift key is pressed, GDK_SHIFT_MASK is not set while it is set
650 // when Shift is released. Under MSW the situation is exactly reversed and
651 // the modifier corresponding to the key is set when it is pressed and
652 // unset when it is released. To ensure consistent behaviour between
653 // platforms (and because it seems to make slightly more sense, although
654 // arguably both behaviours are reasonable) we follow MSW here.
656 // Final notice: we set the flags to the desired value instead of just
657 // inverting them because they are not set correctly (i.e. in the same way
658 // as for the real events generated by the user) for wxUIActionSimulator-
659 // produced events and it seems better to keep that class code the same
660 // among all platforms and fix the discrepancy here instead of adding
661 // wxGTK-specific code to wxUIActionSimulator.
662 const bool isPress
= gdk_event
->type
== GDK_KEY_PRESS
;
663 switch ( gdk_event
->keyval
)
667 event
.m_shiftDown
= isPress
;
672 event
.m_controlDown
= isPress
;
677 event
.m_altDown
= isPress
;
684 event
.m_metaDown
= isPress
;
688 event
.m_rawCode
= (wxUint32
) gdk_event
->keyval
;
689 event
.m_rawFlags
= gdk_event
->hardware_keycode
;
691 wxGetMousePosition(&event
.m_x
, &event
.m_y
);
692 win
->ScreenToClient(&event
.m_x
, &event
.m_y
);
693 event
.SetEventObject( win
);
698 wxTranslateGTKKeyEventToWx(wxKeyEvent
& event
,
700 GdkEventKey
*gdk_event
)
702 // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string
703 // but only event->keyval which is quite useless to us, so remember
704 // the last character from GDK_KEY_PRESS and reuse it as last resort
706 // NB: should be MT-safe as we're always called from the main thread only
711 } s_lastKeyPress
= { 0, 0 };
713 KeySym keysym
= gdk_event
->keyval
;
715 wxLogTrace(TRACE_KEYS
, wxT("Key %s event: keysym = %ld"),
716 event
.GetEventType() == wxEVT_KEY_UP
? wxT("release")
720 long key_code
= wxTranslateKeySymToWXKey(keysym
, false /* !isChar */);
724 // do we have the translation or is it a plain ASCII character?
725 if ( (gdk_event
->length
== 1) || wxIsAsciiKeysym(keysym
) )
727 // we should use keysym if it is ASCII as X does some translations
728 // like "I pressed while Control is down" => "Ctrl-I" == "TAB"
729 // which we don't want here (but which we do use for OnChar())
730 if ( !wxIsAsciiKeysym(keysym
) )
732 keysym
= (KeySym
)gdk_event
->string
[0];
735 #ifdef GDK_WINDOWING_X11
736 // we want to always get the same key code when the same key is
737 // pressed regardless of the state of the modifiers, i.e. on a
738 // standard US keyboard pressing '5' or '%' ('5' key with
739 // Shift) should result in the same key code in OnKeyDown():
740 // '5' (although OnChar() will get either '5' or '%').
742 // to do it we first translate keysym to keycode (== scan code)
743 // and then back but always using the lower register
744 Display
*dpy
= (Display
*)wxGetDisplay();
745 KeyCode keycode
= XKeysymToKeycode(dpy
, keysym
);
747 wxLogTrace(TRACE_KEYS
, wxT("\t-> keycode %d"), keycode
);
749 #ifdef HAVE_X11_XKBLIB_H
750 KeySym keysymNormalized
= XkbKeycodeToKeysym(dpy
, keycode
, 0, 0);
752 KeySym keysymNormalized
= XKeycodeToKeysym(dpy
, keycode
, 0);
755 // use the normalized, i.e. lower register, keysym if we've
757 key_code
= keysymNormalized
? keysymNormalized
: keysym
;
762 // as explained above, we want to have lower register key codes
763 // normally but for the letter keys we want to have the upper ones
765 // NB: don't use XConvertCase() here, we want to do it for letters
767 key_code
= toupper(key_code
);
769 else // non ASCII key, what to do?
771 // by default, ignore it
774 // but if we have cached information from the last KEY_PRESS
775 if ( gdk_event
->type
== GDK_KEY_RELEASE
)
778 if ( keysym
== s_lastKeyPress
.keysym
)
780 key_code
= s_lastKeyPress
.keycode
;
785 if ( gdk_event
->type
== GDK_KEY_PRESS
)
787 // remember it to be reused for KEY_UP event later
788 s_lastKeyPress
.keysym
= keysym
;
789 s_lastKeyPress
.keycode
= key_code
;
793 wxLogTrace(TRACE_KEYS
, wxT("\t-> wxKeyCode %ld"), key_code
);
795 // sending unknown key events doesn't really make sense
799 event
.m_keyCode
= key_code
;
802 event
.m_uniChar
= gdk_keyval_to_unicode(key_code
? key_code
: keysym
);
803 if ( !event
.m_uniChar
&& event
.m_keyCode
<= WXK_DELETE
)
805 // Set Unicode key code to the ASCII equivalent for compatibility. E.g.
806 // let RETURN generate the key event with both key and Unicode key
808 event
.m_uniChar
= event
.m_keyCode
;
810 #endif // wxUSE_UNICODE
812 // now fill all the other fields
813 wxFillOtherKeyEventFields(event
, win
, gdk_event
);
821 GtkIMContext
*context
;
822 GdkEventKey
*lastKeyEvent
;
826 context
= gtk_im_multicontext_new();
831 g_object_unref (context
);
838 // Send wxEVT_CHAR_HOOK event to the parent of the window and return true only
839 // if it was processed (and not skipped).
840 bool SendCharHookEvent(const wxKeyEvent
& event
, wxWindow
*win
)
842 // wxEVT_CHAR_HOOK must be sent to allow the parent windows (e.g. a dialog
843 // which typically closes when Esc key is pressed in any of its controls)
844 // to handle key events in all of its children unless the mouse is captured
845 // in which case we consider that the keyboard should be "captured" too.
846 if ( !g_captureWindow
)
848 wxKeyEvent
eventCharHook(wxEVT_CHAR_HOOK
, event
);
849 if ( win
->HandleWindowEvent(eventCharHook
)
850 && !event
.IsNextEventAllowed() )
857 // Adjust wxEVT_CHAR event key code fields. This function takes care of two
859 // (a) Ctrl-letter key presses generate key codes in range 1..26
860 // (b) Unicode key codes are same as key codes for the codes in 1..255 range
861 void AdjustCharEventKeyCodes(wxKeyEvent
& event
)
863 const int code
= event
.m_keyCode
;
865 // Check for (a) above.
866 if ( event
.ControlDown() )
868 // We intentionally don't use isupper/lower() here, we really need
869 // ASCII letters only as it doesn't make sense to translate any other
870 // ones into this range which has only 26 slots.
871 if ( code
>= 'a' && code
<= 'z' )
872 event
.m_keyCode
= code
- 'a' + 1;
873 else if ( code
>= 'A' && code
<= 'Z' )
874 event
.m_keyCode
= code
- 'A' + 1;
877 // Adjust the Unicode equivalent in the same way too.
878 if ( event
.m_keyCode
!= code
)
879 event
.m_uniChar
= event
.m_keyCode
;
880 #endif // wxUSE_UNICODE
884 // Check for (b) from above.
886 // FIXME: Should we do it for key codes up to 255?
887 if ( !event
.m_uniChar
&& code
< WXK_DELETE
)
888 event
.m_uniChar
= code
;
889 #endif // wxUSE_UNICODE
892 } // anonymous namespace
894 // If a widget does not handle a key or mouse event, GTK+ sends it up the
895 // parent chain until it is handled. These events are not supposed to propagate
896 // in wxWidgets, so this code avoids handling them in any parent wxWindow,
897 // while still allowing the event to propagate so things like native keyboard
898 // navigation will work.
899 #define wxPROCESS_EVENT_ONCE(EventType, event) \
900 static EventType eventPrev; \
901 if (memcmp(&eventPrev, event, sizeof(EventType)) == 0) \
907 gtk_window_key_press_callback( GtkWidget
*WXUNUSED(widget
),
908 GdkEventKey
*gdk_event
,
913 if (g_blockEventsOnDrag
)
916 wxPROCESS_EVENT_ONCE(GdkEventKey
, gdk_event
);
918 wxKeyEvent
event( wxEVT_KEY_DOWN
);
920 bool return_after_IM
= false;
922 if( wxTranslateGTKKeyEventToWx(event
, win
, gdk_event
) )
924 // Send the CHAR_HOOK event first
925 if ( SendCharHookEvent(event
, win
) )
927 // Don't do anything at all with this event any more.
931 // Emit KEY_DOWN event
932 ret
= win
->HandleWindowEvent( event
);
936 // Return after IM processing as we cannot do
937 // anything with it anyhow.
938 return_after_IM
= true;
941 if (!ret
&& win
->m_imData
)
943 win
->m_imData
->lastKeyEvent
= gdk_event
;
945 // We should let GTK+ IM filter key event first. According to GTK+ 2.0 API
946 // docs, if IM filter returns true, no further processing should be done.
947 // we should send the key_down event anyway.
948 bool intercepted_by_IM
= gtk_im_context_filter_keypress(win
->m_imData
->context
, gdk_event
);
949 win
->m_imData
->lastKeyEvent
= NULL
;
950 if (intercepted_by_IM
)
952 wxLogTrace(TRACE_KEYS
, wxT("Key event intercepted by IM"));
963 wxWindowGTK
*ancestor
= win
;
966 int command
= ancestor
->GetAcceleratorTable()->GetCommand( event
);
969 wxCommandEvent
menu_event( wxEVT_COMMAND_MENU_SELECTED
, command
);
970 ret
= ancestor
->HandleWindowEvent( menu_event
);
974 // if the accelerator wasn't handled as menu event, try
975 // it as button click (for compatibility with other
977 wxCommandEvent
button_event( wxEVT_COMMAND_BUTTON_CLICKED
, command
);
978 ret
= ancestor
->HandleWindowEvent( button_event
);
983 if (ancestor
->IsTopLevel())
985 ancestor
= ancestor
->GetParent();
988 #endif // wxUSE_ACCEL
990 // Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
991 // will only be sent if it is not in an accelerator table.
995 KeySym keysym
= gdk_event
->keyval
;
996 // Find key code for EVT_CHAR and EVT_CHAR_HOOK events
997 key_code
= wxTranslateKeySymToWXKey(keysym
, true /* isChar */);
1000 if ( wxIsAsciiKeysym(keysym
) )
1003 key_code
= (unsigned char)keysym
;
1005 // gdk_event->string is actually deprecated
1006 else if ( gdk_event
->length
== 1 )
1008 key_code
= (unsigned char)gdk_event
->string
[0];
1014 wxKeyEvent
eventChar(wxEVT_CHAR
, event
);
1016 wxLogTrace(TRACE_KEYS
, wxT("Char event: %ld"), key_code
);
1018 eventChar
.m_keyCode
= key_code
;
1020 AdjustCharEventKeyCodes(eventChar
);
1022 ret
= win
->HandleWindowEvent(eventChar
);
1032 gtk_wxwindow_commit_cb (GtkIMContext
* WXUNUSED(context
),
1036 wxKeyEvent
event( wxEVT_CHAR
);
1038 // take modifiers, cursor position, timestamp etc. from the last
1039 // key_press_event that was fed into Input Method:
1040 if (window
->m_imData
->lastKeyEvent
)
1042 wxFillOtherKeyEventFields(event
,
1043 window
, window
->m_imData
->lastKeyEvent
);
1047 event
.SetEventObject( window
);
1050 const wxString
data(wxGTK_CONV_BACK_SYS(str
));
1054 for( wxString::const_iterator pstr
= data
.begin(); pstr
!= data
.end(); ++pstr
)
1057 event
.m_uniChar
= *pstr
;
1058 // Backward compatible for ISO-8859-1
1059 event
.m_keyCode
= *pstr
< 256 ? event
.m_uniChar
: 0;
1060 wxLogTrace(TRACE_KEYS
, wxT("IM sent character '%c'"), event
.m_uniChar
);
1062 event
.m_keyCode
= (char)*pstr
;
1063 #endif // wxUSE_UNICODE
1065 AdjustCharEventKeyCodes(event
);
1067 window
->HandleWindowEvent(event
);
1073 //-----------------------------------------------------------------------------
1074 // "key_release_event" from any window
1075 //-----------------------------------------------------------------------------
1079 gtk_window_key_release_callback( GtkWidget
* WXUNUSED(widget
),
1080 GdkEventKey
*gdk_event
,
1086 if (g_blockEventsOnDrag
)
1089 wxPROCESS_EVENT_ONCE(GdkEventKey
, gdk_event
);
1091 wxKeyEvent
event( wxEVT_KEY_UP
);
1092 if ( !wxTranslateGTKKeyEventToWx(event
, win
, gdk_event
) )
1094 // unknown key pressed, ignore (the event would be useless anyhow)
1098 return win
->GTKProcessEvent(event
);
1102 // ============================================================================
1104 // ============================================================================
1106 // ----------------------------------------------------------------------------
1107 // mouse event processing helpers
1108 // ----------------------------------------------------------------------------
1110 static void AdjustEventButtonState(wxMouseEvent
& event
)
1112 // GDK reports the old state of the button for a button press event, but
1113 // for compatibility with MSW and common sense we want m_leftDown be TRUE
1114 // for a LEFT_DOWN event, not FALSE, so we will invert
1115 // left/right/middleDown for the corresponding click events
1117 if ((event
.GetEventType() == wxEVT_LEFT_DOWN
) ||
1118 (event
.GetEventType() == wxEVT_LEFT_DCLICK
) ||
1119 (event
.GetEventType() == wxEVT_LEFT_UP
))
1121 event
.m_leftDown
= !event
.m_leftDown
;
1125 if ((event
.GetEventType() == wxEVT_MIDDLE_DOWN
) ||
1126 (event
.GetEventType() == wxEVT_MIDDLE_DCLICK
) ||
1127 (event
.GetEventType() == wxEVT_MIDDLE_UP
))
1129 event
.m_middleDown
= !event
.m_middleDown
;
1133 if ((event
.GetEventType() == wxEVT_RIGHT_DOWN
) ||
1134 (event
.GetEventType() == wxEVT_RIGHT_DCLICK
) ||
1135 (event
.GetEventType() == wxEVT_RIGHT_UP
))
1137 event
.m_rightDown
= !event
.m_rightDown
;
1141 if ((event
.GetEventType() == wxEVT_AUX1_DOWN
) ||
1142 (event
.GetEventType() == wxEVT_AUX1_DCLICK
))
1144 event
.m_aux1Down
= true;
1148 if ((event
.GetEventType() == wxEVT_AUX2_DOWN
) ||
1149 (event
.GetEventType() == wxEVT_AUX2_DCLICK
))
1151 event
.m_aux2Down
= true;
1156 // find the window to send the mouse event to
1158 wxWindowGTK
*FindWindowForMouseEvent(wxWindowGTK
*win
, wxCoord
& x
, wxCoord
& y
)
1163 if (win
->m_wxwindow
)
1165 wxPizza
* pizza
= WX_PIZZA(win
->m_wxwindow
);
1166 xx
+= pizza
->m_scroll_x
;
1167 yy
+= pizza
->m_scroll_y
;
1170 wxWindowList::compatibility_iterator node
= win
->GetChildren().GetFirst();
1173 wxWindow
* child
= static_cast<wxWindow
*>(node
->GetData());
1175 node
= node
->GetNext();
1176 if (!child
->IsShown())
1179 if (child
->GTKIsTransparentForMouse())
1181 // wxStaticBox is transparent in the box itself
1182 int xx1
= child
->m_x
;
1183 int yy1
= child
->m_y
;
1184 int xx2
= child
->m_x
+ child
->m_width
;
1185 int yy2
= child
->m_y
+ child
->m_height
;
1188 if (((xx
>= xx1
) && (xx
<= xx1
+10) && (yy
>= yy1
) && (yy
<= yy2
)) ||
1190 ((xx
>= xx2
-10) && (xx
<= xx2
) && (yy
>= yy1
) && (yy
<= yy2
)) ||
1192 ((xx
>= xx1
) && (xx
<= xx2
) && (yy
>= yy1
) && (yy
<= yy1
+10)) ||
1194 ((xx
>= xx1
) && (xx
<= xx2
) && (yy
>= yy2
-1) && (yy
<= yy2
)))
1205 if ((child
->m_wxwindow
== NULL
) &&
1206 win
->IsClientAreaChild(child
) &&
1207 (child
->m_x
<= xx
) &&
1208 (child
->m_y
<= yy
) &&
1209 (child
->m_x
+child
->m_width
>= xx
) &&
1210 (child
->m_y
+child
->m_height
>= yy
))
1223 // ----------------------------------------------------------------------------
1224 // common event handlers helpers
1225 // ----------------------------------------------------------------------------
1227 bool wxWindowGTK::GTKProcessEvent(wxEvent
& event
) const
1229 // nothing special at this level
1230 return HandleWindowEvent(event
);
1233 bool wxWindowGTK::GTKShouldIgnoreEvent() const
1235 return !m_hasVMT
|| g_blockEventsOnDrag
;
1238 int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny
*event
) const
1242 if (g_blockEventsOnDrag
)
1244 if (g_blockEventsOnScroll
)
1247 if (!GTKIsOwnWindow(event
->window
))
1253 // overloads for all GDK event types we use here: we need to have this as
1254 // GdkEventXXX can't be implicitly cast to GdkEventAny even if it, in fact,
1255 // derives from it in the sense that the structs have the same layout
1256 #define wxDEFINE_COMMON_PROLOGUE_OVERLOAD(T) \
1257 static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win) \
1259 return win->GTKCallbackCommonPrologue((GdkEventAny *)event); \
1262 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventButton
)
1263 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion
)
1264 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing
)
1266 #undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD
1268 #define wxCOMMON_CALLBACK_PROLOGUE(event, win) \
1269 const int rc = wxGtkCallbackCommonPrologue(event, win); \
1273 // all event handlers must have C linkage as they're called from GTK+ C code
1277 //-----------------------------------------------------------------------------
1278 // "button_press_event"
1279 //-----------------------------------------------------------------------------
1282 gtk_window_button_press_callback( GtkWidget
*widget
,
1283 GdkEventButton
*gdk_event
,
1286 wxPROCESS_EVENT_ONCE(GdkEventButton
, gdk_event
);
1288 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1290 g_lastButtonNumber
= gdk_event
->button
;
1292 // GDK sends surplus button down events
1293 // before a double click event. We
1294 // need to filter these out.
1295 if ((gdk_event
->type
== GDK_BUTTON_PRESS
) && (win
->m_wxwindow
))
1297 GdkEvent
*peek_event
= gdk_event_peek();
1300 if ((peek_event
->type
== GDK_2BUTTON_PRESS
) ||
1301 (peek_event
->type
== GDK_3BUTTON_PRESS
))
1303 gdk_event_free( peek_event
);
1308 gdk_event_free( peek_event
);
1313 wxEventType event_type
= wxEVT_NULL
;
1315 if ( gdk_event
->type
== GDK_2BUTTON_PRESS
&&
1316 gdk_event
->button
>= 1 && gdk_event
->button
<= 3 )
1318 // Reset GDK internal timestamp variables in order to disable GDK
1319 // triple click events. GDK will then next time believe no button has
1320 // been clicked just before, and send a normal button click event.
1321 GdkDisplay
* display
= gtk_widget_get_display (widget
);
1322 display
->button_click_time
[1] = 0;
1323 display
->button_click_time
[0] = 0;
1326 if (gdk_event
->button
== 1)
1328 // note that GDK generates triple click events which are not supported
1329 // by wxWidgets but still have to be passed to the app as otherwise
1330 // clicks would simply go missing
1331 switch (gdk_event
->type
)
1333 // we shouldn't get triple clicks at all for GTK2 because we
1334 // suppress them artificially using the code above but we still
1335 // should map them to something for GTK1 and not just ignore them
1336 // as this would lose clicks
1337 case GDK_3BUTTON_PRESS
: // we could also map this to DCLICK...
1338 case GDK_BUTTON_PRESS
:
1339 event_type
= wxEVT_LEFT_DOWN
;
1342 case GDK_2BUTTON_PRESS
:
1343 event_type
= wxEVT_LEFT_DCLICK
;
1347 // just to silence gcc warnings
1351 else if (gdk_event
->button
== 2)
1353 switch (gdk_event
->type
)
1355 case GDK_3BUTTON_PRESS
:
1356 case GDK_BUTTON_PRESS
:
1357 event_type
= wxEVT_MIDDLE_DOWN
;
1360 case GDK_2BUTTON_PRESS
:
1361 event_type
= wxEVT_MIDDLE_DCLICK
;
1368 else if (gdk_event
->button
== 3)
1370 switch (gdk_event
->type
)
1372 case GDK_3BUTTON_PRESS
:
1373 case GDK_BUTTON_PRESS
:
1374 event_type
= wxEVT_RIGHT_DOWN
;
1377 case GDK_2BUTTON_PRESS
:
1378 event_type
= wxEVT_RIGHT_DCLICK
;
1386 else if (gdk_event
->button
== 8)
1388 switch (gdk_event
->type
)
1390 case GDK_3BUTTON_PRESS
:
1391 case GDK_BUTTON_PRESS
:
1392 event_type
= wxEVT_AUX1_DOWN
;
1395 case GDK_2BUTTON_PRESS
:
1396 event_type
= wxEVT_AUX1_DCLICK
;
1404 else if (gdk_event
->button
== 9)
1406 switch (gdk_event
->type
)
1408 case GDK_3BUTTON_PRESS
:
1409 case GDK_BUTTON_PRESS
:
1410 event_type
= wxEVT_AUX2_DOWN
;
1413 case GDK_2BUTTON_PRESS
:
1414 event_type
= wxEVT_AUX2_DCLICK
;
1422 if ( event_type
== wxEVT_NULL
)
1424 // unknown mouse button or click type
1428 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1430 wxMouseEvent
event( event_type
);
1431 InitMouseEvent( win
, event
, gdk_event
);
1433 AdjustEventButtonState(event
);
1435 // find the correct window to send the event to: it may be a different one
1436 // from the one which got it at GTK+ level because some controls don't have
1437 // their own X window and thus cannot get any events.
1438 if ( !g_captureWindow
)
1439 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1441 // reset the event object and id in case win changed.
1442 event
.SetEventObject( win
);
1443 event
.SetId( win
->GetId() );
1445 bool ret
= win
->GTKProcessEvent( event
);
1446 g_lastMouseEvent
= NULL
;
1450 if ((event_type
== wxEVT_LEFT_DOWN
) && !win
->IsOfStandardClass() &&
1451 (gs_currentFocus
!= win
) /* && win->IsFocusable() */)
1456 if (event_type
== wxEVT_RIGHT_DOWN
)
1458 // generate a "context menu" event: this is similar to right mouse
1459 // click under many GUIs except that it is generated differently
1460 // (right up under MSW, ctrl-click under Mac, right down here) and
1462 // (a) it's a command event and so is propagated to the parent
1463 // (b) under some ports it can be generated from kbd too
1464 // (c) it uses screen coords (because of (a))
1465 wxContextMenuEvent
evtCtx(
1468 win
->ClientToScreen(event
.GetPosition()));
1469 evtCtx
.SetEventObject(win
);
1470 return win
->GTKProcessEvent(evtCtx
);
1476 //-----------------------------------------------------------------------------
1477 // "button_release_event"
1478 //-----------------------------------------------------------------------------
1481 gtk_window_button_release_callback( GtkWidget
*WXUNUSED(widget
),
1482 GdkEventButton
*gdk_event
,
1485 wxPROCESS_EVENT_ONCE(GdkEventButton
, gdk_event
);
1487 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1489 g_lastButtonNumber
= 0;
1491 wxEventType event_type
= wxEVT_NULL
;
1493 switch (gdk_event
->button
)
1496 event_type
= wxEVT_LEFT_UP
;
1500 event_type
= wxEVT_MIDDLE_UP
;
1504 event_type
= wxEVT_RIGHT_UP
;
1508 event_type
= wxEVT_AUX1_UP
;
1512 event_type
= wxEVT_AUX2_UP
;
1516 // unknown button, don't process
1520 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1522 wxMouseEvent
event( event_type
);
1523 InitMouseEvent( win
, event
, gdk_event
);
1525 AdjustEventButtonState(event
);
1527 if ( !g_captureWindow
)
1528 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1530 // reset the event object and id in case win changed.
1531 event
.SetEventObject( win
);
1532 event
.SetId( win
->GetId() );
1534 bool ret
= win
->GTKProcessEvent(event
);
1536 g_lastMouseEvent
= NULL
;
1541 //-----------------------------------------------------------------------------
1542 // "motion_notify_event"
1543 //-----------------------------------------------------------------------------
1546 gtk_window_motion_notify_callback( GtkWidget
* WXUNUSED(widget
),
1547 GdkEventMotion
*gdk_event
,
1550 wxPROCESS_EVENT_ONCE(GdkEventMotion
, gdk_event
);
1552 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1554 if (gdk_event
->is_hint
)
1558 GdkModifierType state
;
1559 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, &state
);
1564 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1566 wxMouseEvent
event( wxEVT_MOTION
);
1567 InitMouseEvent(win
, event
, gdk_event
);
1569 if ( g_captureWindow
)
1571 // synthesise a mouse enter or leave event if needed
1572 GdkWindow
*winUnderMouse
= gdk_window_at_pointer(NULL
, NULL
);
1573 // This seems to be necessary and actually been added to
1574 // GDK itself in version 2.0.X
1577 bool hasMouse
= winUnderMouse
== gdk_event
->window
;
1578 if ( hasMouse
!= g_captureWindowHasMouse
)
1580 // the mouse changed window
1581 g_captureWindowHasMouse
= hasMouse
;
1583 wxMouseEvent
eventM(g_captureWindowHasMouse
? wxEVT_ENTER_WINDOW
1584 : wxEVT_LEAVE_WINDOW
);
1585 InitMouseEvent(win
, eventM
, gdk_event
);
1586 eventM
.SetEventObject(win
);
1587 win
->GTKProcessEvent(eventM
);
1592 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1594 // reset the event object and id in case win changed.
1595 event
.SetEventObject( win
);
1596 event
.SetId( win
->GetId() );
1599 if ( !g_captureWindow
)
1601 wxSetCursorEvent
cevent( event
.m_x
, event
.m_y
);
1602 if (win
->GTKProcessEvent( cevent
))
1604 win
->SetCursor( cevent
.GetCursor() );
1608 bool ret
= win
->GTKProcessEvent(event
);
1610 g_lastMouseEvent
= NULL
;
1615 //-----------------------------------------------------------------------------
1616 // "scroll_event" (mouse wheel event)
1617 //-----------------------------------------------------------------------------
1620 window_scroll_event_hscrollbar(GtkWidget
*, GdkEventScroll
* gdk_event
, wxWindow
* win
)
1622 if (gdk_event
->direction
!= GDK_SCROLL_LEFT
&&
1623 gdk_event
->direction
!= GDK_SCROLL_RIGHT
)
1628 GtkRange
*range
= win
->m_scrollBar
[wxWindow::ScrollDir_Horz
];
1630 if (range
&& gtk_widget_get_visible(GTK_WIDGET(range
)))
1632 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
1633 double delta
= gtk_adjustment_get_step_increment(adj
) * 3;
1634 if (gdk_event
->direction
== GDK_SCROLL_LEFT
)
1637 gtk_range_set_value(range
, gtk_adjustment_get_value(adj
) + delta
);
1646 window_scroll_event(GtkWidget
*, GdkEventScroll
* gdk_event
, wxWindow
* win
)
1648 wxMouseEvent
event(wxEVT_MOUSEWHEEL
);
1649 InitMouseEvent(win
, event
, gdk_event
);
1651 // FIXME: Get these values from GTK or GDK
1652 event
.m_linesPerAction
= 3;
1653 event
.m_wheelDelta
= 120;
1655 // Determine the scroll direction.
1656 switch (gdk_event
->direction
)
1659 case GDK_SCROLL_RIGHT
:
1660 event
.m_wheelRotation
= 120;
1663 case GDK_SCROLL_DOWN
:
1664 case GDK_SCROLL_LEFT
:
1665 event
.m_wheelRotation
= -120;
1669 return false; // Unknown/unhandled direction
1672 // And the scroll axis.
1673 switch (gdk_event
->direction
)
1676 case GDK_SCROLL_DOWN
:
1677 event
.m_wheelAxis
= wxMOUSE_WHEEL_VERTICAL
;
1680 case GDK_SCROLL_LEFT
:
1681 case GDK_SCROLL_RIGHT
:
1682 event
.m_wheelAxis
= wxMOUSE_WHEEL_HORIZONTAL
;
1686 if (win
->GTKProcessEvent(event
))
1689 GtkRange
*range
= win
->m_scrollBar
[wxWindow::ScrollDir_Vert
];
1691 if (range
&& gtk_widget_get_visible(GTK_WIDGET(range
)))
1693 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
1694 double delta
= gtk_adjustment_get_step_increment(adj
) * 3;
1695 if (gdk_event
->direction
== GDK_SCROLL_UP
)
1698 gtk_range_set_value(range
, gtk_adjustment_get_value(adj
) + delta
);
1706 //-----------------------------------------------------------------------------
1708 //-----------------------------------------------------------------------------
1710 static gboolean
wxgtk_window_popup_menu_callback(GtkWidget
*, wxWindowGTK
* win
)
1712 wxContextMenuEvent
event(wxEVT_CONTEXT_MENU
, win
->GetId(), wxPoint(-1, -1));
1713 event
.SetEventObject(win
);
1714 return win
->GTKProcessEvent(event
);
1717 //-----------------------------------------------------------------------------
1719 //-----------------------------------------------------------------------------
1722 gtk_window_focus_in_callback( GtkWidget
* WXUNUSED(widget
),
1723 GdkEventFocus
*WXUNUSED(event
),
1726 return win
->GTKHandleFocusIn();
1729 //-----------------------------------------------------------------------------
1730 // "focus_out_event"
1731 //-----------------------------------------------------------------------------
1734 gtk_window_focus_out_callback( GtkWidget
* WXUNUSED(widget
),
1735 GdkEventFocus
* WXUNUSED(gdk_event
),
1738 return win
->GTKHandleFocusOut();
1741 //-----------------------------------------------------------------------------
1743 //-----------------------------------------------------------------------------
1746 wx_window_focus_callback(GtkWidget
*widget
,
1747 GtkDirectionType
WXUNUSED(direction
),
1750 // the default handler for focus signal in GtkScrolledWindow sets
1751 // focus to the window itself even if it doesn't accept focus, i.e. has no
1752 // GTK_CAN_FOCUS in its style -- work around this by forcibly preventing
1753 // the signal from reaching gtk_scrolled_window_focus() if we don't have
1754 // any children which might accept focus (we know we don't accept the focus
1755 // ourselves as this signal is only connected in this case)
1756 if ( win
->GetChildren().empty() )
1757 g_signal_stop_emission_by_name(widget
, "focus");
1759 // we didn't change the focus
1763 //-----------------------------------------------------------------------------
1764 // "enter_notify_event"
1765 //-----------------------------------------------------------------------------
1768 gtk_window_enter_callback( GtkWidget
*widget
,
1769 GdkEventCrossing
*gdk_event
,
1772 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1774 // Event was emitted after a grab
1775 if (gdk_event
->mode
!= GDK_CROSSING_NORMAL
) return FALSE
;
1779 GdkModifierType state
= (GdkModifierType
)0;
1781 gdk_window_get_pointer(gtk_widget_get_window(widget
), &x
, &y
, &state
);
1783 wxMouseEvent
event( wxEVT_ENTER_WINDOW
);
1784 InitMouseEvent(win
, event
, gdk_event
);
1785 wxPoint pt
= win
->GetClientAreaOrigin();
1786 event
.m_x
= x
+ pt
.x
;
1787 event
.m_y
= y
+ pt
.y
;
1789 if ( !g_captureWindow
)
1791 wxSetCursorEvent
cevent( event
.m_x
, event
.m_y
);
1792 if (win
->GTKProcessEvent( cevent
))
1794 win
->SetCursor( cevent
.GetCursor() );
1798 return win
->GTKProcessEvent(event
);
1801 //-----------------------------------------------------------------------------
1802 // "leave_notify_event"
1803 //-----------------------------------------------------------------------------
1806 gtk_window_leave_callback( GtkWidget
*widget
,
1807 GdkEventCrossing
*gdk_event
,
1810 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1812 // Event was emitted after an ungrab
1813 if (gdk_event
->mode
!= GDK_CROSSING_NORMAL
) return FALSE
;
1815 wxMouseEvent
event( wxEVT_LEAVE_WINDOW
);
1819 GdkModifierType state
= (GdkModifierType
)0;
1821 gdk_window_get_pointer(gtk_widget_get_window(widget
), &x
, &y
, &state
);
1823 InitMouseEvent(win
, event
, gdk_event
);
1825 return win
->GTKProcessEvent(event
);
1828 //-----------------------------------------------------------------------------
1829 // "value_changed" from scrollbar
1830 //-----------------------------------------------------------------------------
1833 gtk_scrollbar_value_changed(GtkRange
* range
, wxWindow
* win
)
1835 wxEventType eventType
= win
->GTKGetScrollEventType(range
);
1836 if (eventType
!= wxEVT_NULL
)
1838 // Convert scroll event type to scrollwin event type
1839 eventType
+= wxEVT_SCROLLWIN_TOP
- wxEVT_SCROLL_TOP
;
1841 // find the scrollbar which generated the event
1842 wxWindowGTK::ScrollDir dir
= win
->ScrollDirFromRange(range
);
1844 // generate the corresponding wx event
1845 const int orient
= wxWindow::OrientFromScrollDir(dir
);
1846 wxScrollWinEvent
event(eventType
, win
->GetScrollPos(orient
), orient
);
1847 event
.SetEventObject(win
);
1849 win
->GTKProcessEvent(event
);
1853 //-----------------------------------------------------------------------------
1854 // "button_press_event" from scrollbar
1855 //-----------------------------------------------------------------------------
1858 gtk_scrollbar_button_press_event(GtkRange
*, GdkEventButton
*, wxWindow
* win
)
1860 g_blockEventsOnScroll
= true;
1861 win
->m_mouseButtonDown
= true;
1866 //-----------------------------------------------------------------------------
1867 // "event_after" from scrollbar
1868 //-----------------------------------------------------------------------------
1871 gtk_scrollbar_event_after(GtkRange
* range
, GdkEvent
* event
, wxWindow
* win
)
1873 if (event
->type
== GDK_BUTTON_RELEASE
)
1875 g_signal_handlers_block_by_func(range
, (void*)gtk_scrollbar_event_after
, win
);
1877 const int orient
= wxWindow::OrientFromScrollDir(
1878 win
->ScrollDirFromRange(range
));
1879 wxScrollWinEvent
evt(wxEVT_SCROLLWIN_THUMBRELEASE
,
1880 win
->GetScrollPos(orient
), orient
);
1881 evt
.SetEventObject(win
);
1882 win
->GTKProcessEvent(evt
);
1886 //-----------------------------------------------------------------------------
1887 // "button_release_event" from scrollbar
1888 //-----------------------------------------------------------------------------
1891 gtk_scrollbar_button_release_event(GtkRange
* range
, GdkEventButton
*, wxWindow
* win
)
1893 g_blockEventsOnScroll
= false;
1894 win
->m_mouseButtonDown
= false;
1895 // If thumb tracking
1896 if (win
->m_isScrolling
)
1898 win
->m_isScrolling
= false;
1899 // Hook up handler to send thumb release event after this emission is finished.
1900 // To allow setting scroll position from event handler, sending event must
1901 // be deferred until after the GtkRange handler for this signal has run
1902 g_signal_handlers_unblock_by_func(range
, (void*)gtk_scrollbar_event_after
, win
);
1908 //-----------------------------------------------------------------------------
1909 // "realize" from m_widget
1910 //-----------------------------------------------------------------------------
1913 gtk_window_realized_callback(GtkWidget
* WXUNUSED(widget
), wxWindowGTK
* win
)
1915 win
->GTKHandleRealized();
1918 //-----------------------------------------------------------------------------
1919 // "unrealize" from m_wxwindow
1920 //-----------------------------------------------------------------------------
1922 static void unrealize(GtkWidget
*, wxWindowGTK
* win
)
1925 gtk_im_context_set_client_window(win
->m_imData
->context
, NULL
);
1928 //-----------------------------------------------------------------------------
1929 // "size_allocate" from m_wxwindow or m_widget
1930 //-----------------------------------------------------------------------------
1933 size_allocate(GtkWidget
*, GtkAllocation
* alloc
, wxWindow
* win
)
1935 int w
= alloc
->width
;
1936 int h
= alloc
->height
;
1937 if (win
->m_wxwindow
)
1939 int border_x
, border_y
;
1940 WX_PIZZA(win
->m_wxwindow
)->get_border_widths(border_x
, border_y
);
1946 if (win
->m_oldClientWidth
!= w
|| win
->m_oldClientHeight
!= h
)
1948 win
->m_oldClientWidth
= w
;
1949 win
->m_oldClientHeight
= h
;
1950 // this callback can be connected to m_wxwindow,
1951 // so always get size from m_widget->allocation
1953 gtk_widget_get_allocation(win
->m_widget
, &a
);
1954 win
->m_width
= a
.width
;
1955 win
->m_height
= a
.height
;
1956 if (!win
->m_nativeSizeEvent
)
1958 wxSizeEvent
event(win
->GetSize(), win
->GetId());
1959 event
.SetEventObject(win
);
1960 win
->GTKProcessEvent(event
);
1965 //-----------------------------------------------------------------------------
1967 //-----------------------------------------------------------------------------
1969 #if GTK_CHECK_VERSION(2, 8, 0)
1971 gtk_window_grab_broken( GtkWidget
*,
1972 GdkEventGrabBroken
*event
,
1975 // Mouse capture has been lost involuntarily, notify the application
1976 if(!event
->keyboard
&& wxWindow::GetCapture() == win
)
1978 wxMouseCaptureLostEvent
evt( win
->GetId() );
1979 evt
.SetEventObject( win
);
1980 win
->HandleWindowEvent( evt
);
1986 //-----------------------------------------------------------------------------
1988 //-----------------------------------------------------------------------------
1991 void gtk_window_style_set_callback( GtkWidget
*WXUNUSED(widget
),
1992 GtkStyle
*previous_style
,
1995 if (win
&& previous_style
)
1997 if (win
->IsTopLevel())
1999 wxSysColourChangedEvent event
;
2000 event
.SetEventObject(win
);
2001 win
->GTKProcessEvent(event
);
2005 // Border width could change, which will change client size.
2006 // Make sure size event occurs for this
2007 win
->m_oldClientWidth
= 0;
2014 void wxWindowGTK::GTKHandleRealized()
2018 gtk_im_context_set_client_window
2021 m_wxwindow
? GTKGetDrawingWindow()
2022 : gtk_widget_get_window(m_widget
)
2026 // Use composited window if background is transparent, if supported.
2027 if (m_backgroundStyle
== wxBG_STYLE_TRANSPARENT
)
2029 #if wxGTK_HAS_COMPOSITING_SUPPORT
2030 if (IsTransparentBackgroundSupported())
2032 GdkWindow
* const window
= GTKGetDrawingWindow();
2034 gdk_window_set_composited(window
, true);
2037 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
2039 // We revert to erase mode if transparency is not supported
2040 m_backgroundStyle
= wxBG_STYLE_ERASE
;
2045 // We cannot set colours and fonts before the widget
2046 // been realized, so we do this directly after realization
2047 // or otherwise in idle time
2049 if (m_needsStyleChange
)
2051 SetBackgroundStyle(GetBackgroundStyle());
2052 m_needsStyleChange
= false;
2055 wxWindowCreateEvent
event(static_cast<wxWindow
*>(this));
2056 event
.SetEventObject( this );
2057 GTKProcessEvent( event
);
2059 GTKUpdateCursor(true, false);
2062 // ----------------------------------------------------------------------------
2063 // this wxWindowBase function is implemented here (in platform-specific file)
2064 // because it is static and so couldn't be made virtual
2065 // ----------------------------------------------------------------------------
2067 wxWindow
*wxWindowBase::DoFindFocus()
2069 // For compatibility with wxMSW, pretend that showing a popup menu doesn't
2070 // change the focus and that it remains on the window showing it, even
2071 // though the real focus does change in GTK.
2072 extern wxMenu
*wxCurrentPopupMenu
;
2073 if ( wxCurrentPopupMenu
)
2074 return wxCurrentPopupMenu
->GetInvokingWindow();
2076 wxWindowGTK
*focus
= gs_pendingFocus
? gs_pendingFocus
: gs_currentFocus
;
2077 // the cast is necessary when we compile in wxUniversal mode
2078 return static_cast<wxWindow
*>(focus
);
2081 void wxWindowGTK::AddChildGTK(wxWindowGTK
* child
)
2083 wxASSERT_MSG(m_wxwindow
, "Cannot add a child to a window without a client area");
2085 // the window might have been scrolled already, we
2086 // have to adapt the position
2087 wxPizza
* pizza
= WX_PIZZA(m_wxwindow
);
2088 child
->m_x
+= pizza
->m_scroll_x
;
2089 child
->m_y
+= pizza
->m_scroll_y
;
2091 gtk_widget_set_size_request(
2092 child
->m_widget
, child
->m_width
, child
->m_height
);
2093 pizza
->put(child
->m_widget
, child
->m_x
, child
->m_y
);
2096 //-----------------------------------------------------------------------------
2098 //-----------------------------------------------------------------------------
2100 wxWindow
*wxGetActiveWindow()
2102 return wxWindow::FindFocus();
2106 wxMouseState
wxGetMouseState()
2112 GdkModifierType mask
;
2114 gdk_window_get_pointer(NULL
, &x
, &y
, &mask
);
2118 ms
.SetLeftDown((mask
& GDK_BUTTON1_MASK
) != 0);
2119 ms
.SetMiddleDown((mask
& GDK_BUTTON2_MASK
) != 0);
2120 ms
.SetRightDown((mask
& GDK_BUTTON3_MASK
) != 0);
2121 // see the comment in InitMouseEvent()
2122 ms
.SetAux1Down((mask
& GDK_BUTTON4_MASK
) != 0);
2123 ms
.SetAux2Down((mask
& GDK_BUTTON5_MASK
) != 0);
2125 ms
.SetControlDown((mask
& GDK_CONTROL_MASK
) != 0);
2126 ms
.SetShiftDown((mask
& GDK_SHIFT_MASK
) != 0);
2127 ms
.SetAltDown((mask
& GDK_MOD1_MASK
) != 0);
2128 ms
.SetMetaDown((mask
& GDK_META_MASK
) != 0);
2133 //-----------------------------------------------------------------------------
2135 //-----------------------------------------------------------------------------
2137 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2139 #ifdef __WXUNIVERSAL__
2140 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK
, wxWindowBase
)
2141 #endif // __WXUNIVERSAL__
2143 void wxWindowGTK::Init()
2148 m_focusWidget
= NULL
;
2158 m_showOnIdle
= false;
2161 m_nativeSizeEvent
= false;
2163 m_isScrolling
= false;
2164 m_mouseButtonDown
= false;
2166 // initialize scrolling stuff
2167 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
2169 m_scrollBar
[dir
] = NULL
;
2170 m_scrollPos
[dir
] = 0;
2174 m_oldClientHeight
= 0;
2176 m_clipPaintRegion
= false;
2178 m_needsStyleChange
= false;
2180 m_cursor
= *wxSTANDARD_CURSOR
;
2183 m_dirtyTabOrder
= false;
2186 wxWindowGTK::wxWindowGTK()
2191 wxWindowGTK::wxWindowGTK( wxWindow
*parent
,
2196 const wxString
&name
)
2200 Create( parent
, id
, pos
, size
, style
, name
);
2203 bool wxWindowGTK::Create( wxWindow
*parent
,
2208 const wxString
&name
)
2210 // Get default border
2211 wxBorder border
= GetBorder(style
);
2213 style
&= ~wxBORDER_MASK
;
2216 if (!PreCreation( parent
, pos
, size
) ||
2217 !CreateBase( parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
))
2219 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2223 // We should accept the native look
2225 GtkScrolledWindowClass
*scroll_class
= GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget
) );
2226 scroll_class
->scrollbar_spacing
= 0;
2230 m_wxwindow
= wxPizza::New(m_windowStyle
);
2231 #ifndef __WXUNIVERSAL__
2232 if (HasFlag(wxPizza::BORDER_STYLES
))
2234 g_signal_connect(m_wxwindow
, "parent_set",
2235 G_CALLBACK(parent_set
), this);
2238 if (!HasFlag(wxHSCROLL
) && !HasFlag(wxVSCROLL
))
2239 m_widget
= m_wxwindow
;
2242 m_widget
= gtk_scrolled_window_new( NULL
, NULL
);
2244 GtkScrolledWindow
*scrolledWindow
= GTK_SCROLLED_WINDOW(m_widget
);
2246 // There is a conflict with default bindings at GTK+
2247 // level between scrolled windows and notebooks both of which want to use
2248 // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
2249 // direction and notebooks for changing pages -- we decide that if we don't
2250 // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
2251 // means we can get working keyboard navigation in notebooks
2252 if ( !HasFlag(wxHSCROLL
) )
2255 bindings
= gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget
));
2258 gtk_binding_entry_remove(bindings
, GDK_Page_Up
, GDK_CONTROL_MASK
);
2259 gtk_binding_entry_remove(bindings
, GDK_Page_Down
, GDK_CONTROL_MASK
);
2263 if (HasFlag(wxALWAYS_SHOW_SB
))
2265 gtk_scrolled_window_set_policy( scrolledWindow
, GTK_POLICY_ALWAYS
, GTK_POLICY_ALWAYS
);
2269 gtk_scrolled_window_set_policy( scrolledWindow
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2272 m_scrollBar
[ScrollDir_Horz
] = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(scrolledWindow
));
2273 m_scrollBar
[ScrollDir_Vert
] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(scrolledWindow
));
2274 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2275 gtk_range_set_inverted( m_scrollBar
[ScrollDir_Horz
], TRUE
);
2277 gtk_container_add( GTK_CONTAINER(m_widget
), m_wxwindow
);
2279 // connect various scroll-related events
2280 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
2282 // these handlers block mouse events to any window during scrolling
2283 // such as motion events and prevent GTK and wxWidgets from fighting
2284 // over where the slider should be
2285 g_signal_connect(m_scrollBar
[dir
], "button_press_event",
2286 G_CALLBACK(gtk_scrollbar_button_press_event
), this);
2287 g_signal_connect(m_scrollBar
[dir
], "button_release_event",
2288 G_CALLBACK(gtk_scrollbar_button_release_event
), this);
2290 gulong handler_id
= g_signal_connect(m_scrollBar
[dir
], "event_after",
2291 G_CALLBACK(gtk_scrollbar_event_after
), this);
2292 g_signal_handler_block(m_scrollBar
[dir
], handler_id
);
2294 // these handlers get notified when scrollbar slider moves
2295 g_signal_connect_after(m_scrollBar
[dir
], "value_changed",
2296 G_CALLBACK(gtk_scrollbar_value_changed
), this);
2299 gtk_widget_show( m_wxwindow
);
2301 g_object_ref(m_widget
);
2304 m_parent
->DoAddChild( this );
2306 m_focusWidget
= m_wxwindow
;
2308 SetCanFocus(AcceptsFocus());
2315 wxWindowGTK::~wxWindowGTK()
2319 if (gs_currentFocus
== this)
2320 gs_currentFocus
= NULL
;
2321 if (gs_pendingFocus
== this)
2322 gs_pendingFocus
= NULL
;
2324 if ( gs_deferredFocusOut
== this )
2325 gs_deferredFocusOut
= NULL
;
2329 // destroy children before destroying this window itself
2332 // unhook focus handlers to prevent stray events being
2333 // propagated to this (soon to be) dead object
2334 if (m_focusWidget
!= NULL
)
2336 g_signal_handlers_disconnect_by_func (m_focusWidget
,
2337 (gpointer
) gtk_window_focus_in_callback
,
2339 g_signal_handlers_disconnect_by_func (m_focusWidget
,
2340 (gpointer
) gtk_window_focus_out_callback
,
2347 // delete before the widgets to avoid a crash on solaris
2351 // avoid problem with GTK+ 2.18 where a frozen window causes the whole
2352 // TLW to be frozen, and if the window is then destroyed, nothing ever
2353 // gets painted again
2359 // Note that gtk_widget_destroy() does not destroy the widget, it just
2360 // emits the "destroy" signal. The widget is not actually destroyed
2361 // until its reference count drops to zero.
2362 gtk_widget_destroy(m_widget
);
2363 // Release our reference, should be the last one
2364 g_object_unref(m_widget
);
2370 bool wxWindowGTK::PreCreation( wxWindowGTK
*parent
, const wxPoint
&pos
, const wxSize
&size
)
2372 if ( GTKNeedsParent() )
2374 wxCHECK_MSG( parent
, false, wxT("Must have non-NULL parent") );
2377 // Use either the given size, or the default if -1 is given.
2378 // See wxWindowBase for these functions.
2379 m_width
= WidthDefault(size
.x
) ;
2380 m_height
= HeightDefault(size
.y
);
2382 if (pos
!= wxDefaultPosition
)
2391 void wxWindowGTK::PostCreation()
2393 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
2395 #if wxGTK_HAS_COMPOSITING_SUPPORT
2396 // Set RGBA visual as soon as possible to minimize the possibility that
2397 // somebody uses the wrong one.
2398 if ( m_backgroundStyle
== wxBG_STYLE_TRANSPARENT
&&
2399 IsTransparentBackgroundSupported() )
2401 GdkScreen
*screen
= gtk_widget_get_screen (m_widget
);
2403 GdkColormap
*rgba_colormap
= gdk_screen_get_rgba_colormap (screen
);
2406 gtk_widget_set_colormap(m_widget
, rgba_colormap
);
2408 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
2414 // these get reported to wxWidgets -> wxPaintEvent
2416 g_signal_connect (m_wxwindow
, "expose_event",
2417 G_CALLBACK (gtk_window_expose_callback
), this);
2419 if (GetLayoutDirection() == wxLayout_LeftToRight
)
2420 gtk_widget_set_redraw_on_allocate(m_wxwindow
, HasFlag(wxFULL_REPAINT_ON_RESIZE
));
2423 // Create input method handler
2424 m_imData
= new wxGtkIMData
;
2426 // Cannot handle drawing preedited text yet
2427 gtk_im_context_set_use_preedit( m_imData
->context
, FALSE
);
2429 g_signal_connect (m_imData
->context
, "commit",
2430 G_CALLBACK (gtk_wxwindow_commit_cb
), this);
2431 g_signal_connect(m_wxwindow
, "unrealize", G_CALLBACK(unrealize
), this);
2436 if (!GTK_IS_WINDOW(m_widget
))
2438 if (m_focusWidget
== NULL
)
2439 m_focusWidget
= m_widget
;
2443 g_signal_connect (m_focusWidget
, "focus_in_event",
2444 G_CALLBACK (gtk_window_focus_in_callback
), this);
2445 g_signal_connect (m_focusWidget
, "focus_out_event",
2446 G_CALLBACK (gtk_window_focus_out_callback
), this);
2450 g_signal_connect_after (m_focusWidget
, "focus_in_event",
2451 G_CALLBACK (gtk_window_focus_in_callback
), this);
2452 g_signal_connect_after (m_focusWidget
, "focus_out_event",
2453 G_CALLBACK (gtk_window_focus_out_callback
), this);
2457 if ( !AcceptsFocusFromKeyboard() )
2461 g_signal_connect(m_widget
, "focus",
2462 G_CALLBACK(wx_window_focus_callback
), this);
2465 // connect to the various key and mouse handlers
2467 GtkWidget
*connect_widget
= GetConnectWidget();
2469 ConnectWidget( connect_widget
);
2471 // We cannot set colours, fonts and cursors before the widget has been
2472 // realized, so we do this directly after realization -- unless the widget
2473 // was in fact realized already.
2474 if ( gtk_widget_get_realized(connect_widget
) )
2476 gtk_window_realized_callback(connect_widget
, this);
2480 g_signal_connect (connect_widget
, "realize",
2481 G_CALLBACK (gtk_window_realized_callback
), this);
2486 g_signal_connect(m_wxwindow
? m_wxwindow
: m_widget
, "size_allocate",
2487 G_CALLBACK(size_allocate
), this);
2490 #if GTK_CHECK_VERSION(2, 8, 0)
2491 if ( gtk_check_version(2,8,0) == NULL
)
2493 // Make sure we can notify the app when mouse capture is lost
2496 g_signal_connect (m_wxwindow
, "grab_broken_event",
2497 G_CALLBACK (gtk_window_grab_broken
), this);
2500 if ( connect_widget
!= m_wxwindow
)
2502 g_signal_connect (connect_widget
, "grab_broken_event",
2503 G_CALLBACK (gtk_window_grab_broken
), this);
2506 #endif // GTK+ >= 2.8
2508 if ( GTKShouldConnectSizeRequest() )
2510 // This is needed if we want to add our windows into native
2511 // GTK controls, such as the toolbar. With this callback, the
2512 // toolbar gets to know the correct size (the one set by the
2513 // programmer). Sadly, it misbehaves for wxComboBox.
2514 g_signal_connect (m_widget
, "size_request",
2515 G_CALLBACK (wxgtk_window_size_request_callback
),
2519 InheritAttributes();
2523 SetLayoutDirection(wxLayout_Default
);
2525 // unless the window was created initially hidden (i.e. Hide() had been
2526 // called before Create()), we should show it at GTK+ level as well
2528 gtk_widget_show( m_widget
);
2532 wxWindowGTK::GTKConnectWidget(const char *signal
, wxGTKCallback callback
)
2534 return g_signal_connect(m_widget
, signal
, callback
, this);
2537 void wxWindowGTK::ConnectWidget( GtkWidget
*widget
)
2539 g_signal_connect (widget
, "key_press_event",
2540 G_CALLBACK (gtk_window_key_press_callback
), this);
2541 g_signal_connect (widget
, "key_release_event",
2542 G_CALLBACK (gtk_window_key_release_callback
), this);
2543 g_signal_connect (widget
, "button_press_event",
2544 G_CALLBACK (gtk_window_button_press_callback
), this);
2545 g_signal_connect (widget
, "button_release_event",
2546 G_CALLBACK (gtk_window_button_release_callback
), this);
2547 g_signal_connect (widget
, "motion_notify_event",
2548 G_CALLBACK (gtk_window_motion_notify_callback
), this);
2550 g_signal_connect (widget
, "scroll_event",
2551 G_CALLBACK (window_scroll_event
), this);
2552 if (m_scrollBar
[ScrollDir_Horz
])
2553 g_signal_connect (m_scrollBar
[ScrollDir_Horz
], "scroll_event",
2554 G_CALLBACK (window_scroll_event_hscrollbar
), this);
2555 if (m_scrollBar
[ScrollDir_Vert
])
2556 g_signal_connect (m_scrollBar
[ScrollDir_Vert
], "scroll_event",
2557 G_CALLBACK (window_scroll_event
), this);
2559 g_signal_connect (widget
, "popup_menu",
2560 G_CALLBACK (wxgtk_window_popup_menu_callback
), this);
2561 g_signal_connect (widget
, "enter_notify_event",
2562 G_CALLBACK (gtk_window_enter_callback
), this);
2563 g_signal_connect (widget
, "leave_notify_event",
2564 G_CALLBACK (gtk_window_leave_callback
), this);
2566 if (m_wxwindow
&& (IsTopLevel() || HasFlag(wxBORDER_RAISED
| wxBORDER_SUNKEN
| wxBORDER_THEME
)))
2567 g_signal_connect (m_wxwindow
, "style_set",
2568 G_CALLBACK (gtk_window_style_set_callback
), this);
2571 bool wxWindowGTK::Destroy()
2575 return wxWindowBase::Destroy();
2578 static GSList
* gs_queueResizeList
;
2581 static gboolean
queue_resize(void*)
2583 gdk_threads_enter();
2584 for (GSList
* p
= gs_queueResizeList
; p
; p
= p
->next
)
2588 gtk_widget_queue_resize(GTK_WIDGET(p
->data
));
2589 g_object_remove_weak_pointer(G_OBJECT(p
->data
), &p
->data
);
2592 g_slist_free(gs_queueResizeList
);
2593 gs_queueResizeList
= NULL
;
2594 gdk_threads_leave();
2599 void wxWindowGTK::DoMoveWindow(int x
, int y
, int width
, int height
)
2601 GtkWidget
* parent
= gtk_widget_get_parent(m_widget
);
2602 if (WX_IS_PIZZA(parent
))
2604 WX_PIZZA(parent
)->move(m_widget
, x
, y
);
2605 gtk_widget_set_size_request(m_widget
, width
, height
);
2608 // With GTK3, gtk_widget_queue_resize() is ignored while a size-allocate
2609 // is in progress. This situation is common in wxWidgets, since
2610 // size-allocate can generate wxSizeEvent and size event handlers often
2611 // call SetSize(), directly or indirectly. Work around this by deferring
2612 // the queue-resize until after size-allocate processing is finished.
2613 if (g_slist_find(gs_queueResizeList
, m_widget
) == NULL
)
2615 if (gs_queueResizeList
== NULL
)
2616 g_idle_add_full(GTK_PRIORITY_RESIZE
, queue_resize
, NULL
, NULL
);
2617 gs_queueResizeList
= g_slist_prepend(gs_queueResizeList
, m_widget
);
2618 g_object_add_weak_pointer(G_OBJECT(m_widget
), &gs_queueResizeList
->data
);
2622 void wxWindowGTK::ConstrainSize()
2625 // GPE's window manager doesn't like size hints at all, esp. when the user
2626 // has to use the virtual keyboard, so don't constrain size there
2630 const wxSize minSize
= GetMinSize();
2631 const wxSize maxSize
= GetMaxSize();
2632 if (minSize
.x
> 0 && m_width
< minSize
.x
) m_width
= minSize
.x
;
2633 if (minSize
.y
> 0 && m_height
< minSize
.y
) m_height
= minSize
.y
;
2634 if (maxSize
.x
> 0 && m_width
> maxSize
.x
) m_width
= maxSize
.x
;
2635 if (maxSize
.y
> 0 && m_height
> maxSize
.y
) m_height
= maxSize
.y
;
2639 void wxWindowGTK::DoSetSize( int x
, int y
, int width
, int height
, int sizeFlags
)
2641 wxCHECK_RET(m_widget
, "invalid window");
2643 int scrollX
= 0, scrollY
= 0;
2644 GtkWidget
* parent
= gtk_widget_get_parent(m_widget
);
2645 if (WX_IS_PIZZA(parent
))
2647 wxPizza
* pizza
= WX_PIZZA(parent
);
2648 scrollX
= pizza
->m_scroll_x
;
2649 scrollY
= pizza
->m_scroll_y
;
2651 if (x
!= -1 || (sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
2655 if (y
!= -1 || (sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
2660 // calculate the best size if we should auto size the window
2661 if ( ((sizeFlags
& wxSIZE_AUTO_WIDTH
) && width
== -1) ||
2662 ((sizeFlags
& wxSIZE_AUTO_HEIGHT
) && height
== -1) )
2664 const wxSize sizeBest
= GetBestSize();
2665 if ( (sizeFlags
& wxSIZE_AUTO_WIDTH
) && width
== -1 )
2667 if ( (sizeFlags
& wxSIZE_AUTO_HEIGHT
) && height
== -1 )
2668 height
= sizeBest
.y
;
2676 const bool sizeChange
= m_width
!= width
|| m_height
!= height
;
2677 if (sizeChange
|| m_x
!= x
|| m_y
!= y
)
2684 /* the default button has a border around it */
2685 if (gtk_widget_get_can_default(m_widget
))
2687 GtkBorder
*default_border
= NULL
;
2688 gtk_widget_style_get( m_widget
, "default_border", &default_border
, NULL
);
2691 x
-= default_border
->left
;
2692 y
-= default_border
->top
;
2693 width
+= default_border
->left
+ default_border
->right
;
2694 height
+= default_border
->top
+ default_border
->bottom
;
2695 gtk_border_free( default_border
);
2699 DoMoveWindow(x
, y
, width
, height
);
2702 if ((sizeChange
&& !m_nativeSizeEvent
) || (sizeFlags
& wxSIZE_FORCE_EVENT
))
2704 // update these variables to keep size_allocate handler
2705 // from sending another size event for this change
2706 GetClientSize( &m_oldClientWidth
, &m_oldClientHeight
);
2708 wxSizeEvent
event( wxSize(m_width
,m_height
), GetId() );
2709 event
.SetEventObject( this );
2710 HandleWindowEvent( event
);
2714 bool wxWindowGTK::GTKShowFromOnIdle()
2716 if (IsShown() && m_showOnIdle
&& !gtk_widget_get_visible (m_widget
))
2718 GtkAllocation alloc
;
2721 alloc
.width
= m_width
;
2722 alloc
.height
= m_height
;
2723 gtk_widget_size_allocate( m_widget
, &alloc
);
2724 gtk_widget_show( m_widget
);
2725 wxShowEvent
eventShow(GetId(), true);
2726 eventShow
.SetEventObject(this);
2727 HandleWindowEvent(eventShow
);
2728 m_showOnIdle
= false;
2735 void wxWindowGTK::OnInternalIdle()
2737 if ( gs_deferredFocusOut
)
2738 GTKHandleDeferredFocusOut();
2740 // Check if we have to show window now
2741 if (GTKShowFromOnIdle()) return;
2743 if ( m_dirtyTabOrder
)
2745 m_dirtyTabOrder
= false;
2749 wxWindowBase::OnInternalIdle();
2752 void wxWindowGTK::DoGetSize( int *width
, int *height
) const
2754 if (width
) (*width
) = m_width
;
2755 if (height
) (*height
) = m_height
;
2758 void wxWindowGTK::DoSetClientSize( int width
, int height
)
2760 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2762 const wxSize size
= GetSize();
2763 const wxSize clientSize
= GetClientSize();
2764 SetSize(width
+ (size
.x
- clientSize
.x
), height
+ (size
.y
- clientSize
.y
));
2767 void wxWindowGTK::DoGetClientSize( int *width
, int *height
) const
2769 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2776 // if window is scrollable, account for scrollbars
2777 if ( GTK_IS_SCROLLED_WINDOW(m_widget
) )
2779 GtkPolicyType policy
[ScrollDir_Max
];
2780 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(m_widget
),
2781 &policy
[ScrollDir_Horz
],
2782 &policy
[ScrollDir_Vert
]);
2784 for ( int i
= 0; i
< ScrollDir_Max
; i
++ )
2786 // don't account for the scrollbars we don't have
2787 GtkRange
* const range
= m_scrollBar
[i
];
2791 // nor for the ones we have but don't current show
2792 switch ( policy
[i
] )
2794 case GTK_POLICY_NEVER
:
2795 // never shown so doesn't take any place
2798 case GTK_POLICY_ALWAYS
:
2799 // no checks necessary
2802 case GTK_POLICY_AUTOMATIC
:
2803 // may be shown or not, check
2804 GtkAdjustment
*adj
= gtk_range_get_adjustment(range
);
2805 if (gtk_adjustment_get_upper(adj
) <= gtk_adjustment_get_page_size(adj
))
2809 GtkScrolledWindowClass
*scroll_class
=
2810 GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget
) );
2813 gtk_widget_size_request(GTK_WIDGET(range
), &req
);
2814 if (i
== ScrollDir_Horz
)
2815 h
-= req
.height
+ scroll_class
->scrollbar_spacing
;
2817 w
-= req
.width
+ scroll_class
->scrollbar_spacing
;
2821 const wxSize sizeBorders
= DoGetBorderSize();
2831 if (width
) *width
= w
;
2832 if (height
) *height
= h
;
2835 wxSize
wxWindowGTK::DoGetBorderSize() const
2838 return wxWindowBase::DoGetBorderSize();
2841 WX_PIZZA(m_wxwindow
)->get_border_widths(x
, y
);
2843 return 2*wxSize(x
, y
);
2846 void wxWindowGTK::DoGetPosition( int *x
, int *y
) const
2850 GtkWidget
* parent
= NULL
;
2852 parent
= gtk_widget_get_parent(m_widget
);
2853 if (WX_IS_PIZZA(parent
))
2855 wxPizza
* pizza
= WX_PIZZA(parent
);
2856 dx
= pizza
->m_scroll_x
;
2857 dy
= pizza
->m_scroll_y
;
2859 if (x
) (*x
) = m_x
- dx
;
2860 if (y
) (*y
) = m_y
- dy
;
2863 void wxWindowGTK::DoClientToScreen( int *x
, int *y
) const
2865 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2867 if (gtk_widget_get_window(m_widget
) == NULL
) return;
2869 GdkWindow
*source
= NULL
;
2871 source
= gtk_widget_get_window(m_wxwindow
);
2873 source
= gtk_widget_get_window(m_widget
);
2877 gdk_window_get_origin( source
, &org_x
, &org_y
);
2881 if (!gtk_widget_get_has_window(m_widget
))
2884 gtk_widget_get_allocation(m_widget
, &a
);
2893 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2894 *x
= (GetClientSize().x
- *x
) + org_x
;
2902 void wxWindowGTK::DoScreenToClient( int *x
, int *y
) const
2904 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2906 if (!gtk_widget_get_realized(m_widget
)) return;
2908 GdkWindow
*source
= NULL
;
2910 source
= gtk_widget_get_window(m_wxwindow
);
2912 source
= gtk_widget_get_window(m_widget
);
2916 gdk_window_get_origin( source
, &org_x
, &org_y
);
2920 if (!gtk_widget_get_has_window(m_widget
))
2923 gtk_widget_get_allocation(m_widget
, &a
);
2931 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2932 *x
= (GetClientSize().x
- *x
) - org_x
;
2939 bool wxWindowGTK::Show( bool show
)
2941 if ( !wxWindowBase::Show(show
) )
2947 // notice that we may call Hide() before the window is created and this is
2948 // actually useful to create it hidden initially -- but we can't call
2949 // Show() before it is created
2952 wxASSERT_MSG( !show
, "can't show invalid window" );
2960 // defer until later
2964 gtk_widget_show(m_widget
);
2968 gtk_widget_hide(m_widget
);
2971 wxShowEvent
eventShow(GetId(), show
);
2972 eventShow
.SetEventObject(this);
2973 HandleWindowEvent(eventShow
);
2978 void wxWindowGTK::DoEnable( bool enable
)
2980 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2982 gtk_widget_set_sensitive( m_widget
, enable
);
2983 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
2984 gtk_widget_set_sensitive( m_wxwindow
, enable
);
2987 int wxWindowGTK::GetCharHeight() const
2989 wxCHECK_MSG( (m_widget
!= NULL
), 12, wxT("invalid window") );
2991 wxFont font
= GetFont();
2992 wxCHECK_MSG( font
.IsOk(), 12, wxT("invalid font") );
2994 PangoContext
* context
= gtk_widget_get_pango_context(m_widget
);
2999 PangoFontDescription
*desc
= font
.GetNativeFontInfo()->description
;
3000 PangoLayout
*layout
= pango_layout_new(context
);
3001 pango_layout_set_font_description(layout
, desc
);
3002 pango_layout_set_text(layout
, "H", 1);
3003 PangoLayoutLine
*line
= (PangoLayoutLine
*)pango_layout_get_lines(layout
)->data
;
3005 PangoRectangle rect
;
3006 pango_layout_line_get_extents(line
, NULL
, &rect
);
3008 g_object_unref (layout
);
3010 return (int) PANGO_PIXELS(rect
.height
);
3013 int wxWindowGTK::GetCharWidth() const
3015 wxCHECK_MSG( (m_widget
!= NULL
), 8, wxT("invalid window") );
3017 wxFont font
= GetFont();
3018 wxCHECK_MSG( font
.IsOk(), 8, wxT("invalid font") );
3020 PangoContext
* context
= gtk_widget_get_pango_context(m_widget
);
3025 PangoFontDescription
*desc
= font
.GetNativeFontInfo()->description
;
3026 PangoLayout
*layout
= pango_layout_new(context
);
3027 pango_layout_set_font_description(layout
, desc
);
3028 pango_layout_set_text(layout
, "g", 1);
3029 PangoLayoutLine
*line
= (PangoLayoutLine
*)pango_layout_get_lines(layout
)->data
;
3031 PangoRectangle rect
;
3032 pango_layout_line_get_extents(line
, NULL
, &rect
);
3034 g_object_unref (layout
);
3036 return (int) PANGO_PIXELS(rect
.width
);
3039 void wxWindowGTK::DoGetTextExtent( const wxString
& string
,
3043 int *externalLeading
,
3044 const wxFont
*theFont
) const
3046 wxFont fontToUse
= theFont
? *theFont
: GetFont();
3048 wxCHECK_RET( fontToUse
.IsOk(), wxT("invalid font") );
3057 PangoContext
*context
= NULL
;
3059 context
= gtk_widget_get_pango_context( m_widget
);
3068 PangoFontDescription
*desc
= fontToUse
.GetNativeFontInfo()->description
;
3069 PangoLayout
*layout
= pango_layout_new(context
);
3070 pango_layout_set_font_description(layout
, desc
);
3072 const wxCharBuffer data
= wxGTK_CONV( string
);
3074 pango_layout_set_text(layout
, data
, strlen(data
));
3077 PangoRectangle rect
;
3078 pango_layout_get_extents(layout
, NULL
, &rect
);
3080 if (x
) (*x
) = (wxCoord
) PANGO_PIXELS(rect
.width
);
3081 if (y
) (*y
) = (wxCoord
) PANGO_PIXELS(rect
.height
);
3084 PangoLayoutIter
*iter
= pango_layout_get_iter(layout
);
3085 int baseline
= pango_layout_iter_get_baseline(iter
);
3086 pango_layout_iter_free(iter
);
3087 *descent
= *y
- PANGO_PIXELS(baseline
);
3089 if (externalLeading
) (*externalLeading
) = 0; // ??
3091 g_object_unref (layout
);
3094 void wxWindowGTK::GTKDisableFocusOutEvent()
3096 g_signal_handlers_block_by_func( m_focusWidget
,
3097 (gpointer
) gtk_window_focus_out_callback
, this);
3100 void wxWindowGTK::GTKEnableFocusOutEvent()
3102 g_signal_handlers_unblock_by_func( m_focusWidget
,
3103 (gpointer
) gtk_window_focus_out_callback
, this);
3106 bool wxWindowGTK::GTKHandleFocusIn()
3108 // Disable default focus handling for custom windows since the default GTK+
3109 // handler issues a repaint
3110 const bool retval
= m_wxwindow
? true : false;
3113 // NB: if there's still unprocessed deferred focus-out event (see
3114 // GTKHandleFocusOut() for explanation), we need to process it first so
3115 // that the order of focus events -- focus-out first, then focus-in
3116 // elsewhere -- is preserved
3117 if ( gs_deferredFocusOut
)
3119 if ( GTKNeedsToFilterSameWindowFocus() &&
3120 gs_deferredFocusOut
== this )
3122 // GTK+ focus changed from this wxWindow back to itself, so don't
3123 // emit any events at all
3124 wxLogTrace(TRACE_FOCUS
,
3125 "filtered out spurious focus change within %s(%p, %s)",
3126 GetClassInfo()->GetClassName(), this, GetLabel());
3127 gs_deferredFocusOut
= NULL
;
3131 // otherwise we need to send focus-out first
3132 wxASSERT_MSG ( gs_deferredFocusOut
!= this,
3133 "GTKHandleFocusIn(GTKFocus_Normal) called even though focus changed back to itself - derived class should handle this" );
3134 GTKHandleDeferredFocusOut();
3138 wxLogTrace(TRACE_FOCUS
,
3139 "handling focus_in event for %s(%p, %s)",
3140 GetClassInfo()->GetClassName(), this, GetLabel());
3143 gtk_im_context_focus_in(m_imData
->context
);
3145 gs_currentFocus
= this;
3146 gs_pendingFocus
= NULL
;
3149 // caret needs to be informed about focus change
3150 wxCaret
*caret
= GetCaret();
3153 caret
->OnSetFocus();
3155 #endif // wxUSE_CARET
3157 // Notify the parent keeping track of focus for the kbd navigation
3158 // purposes that we got it.
3159 wxChildFocusEvent
eventChildFocus(static_cast<wxWindow
*>(this));
3160 GTKProcessEvent(eventChildFocus
);
3162 wxFocusEvent
eventFocus(wxEVT_SET_FOCUS
, GetId());
3163 eventFocus
.SetEventObject(this);
3164 GTKProcessEvent(eventFocus
);
3169 bool wxWindowGTK::GTKHandleFocusOut()
3171 // Disable default focus handling for custom windows since the default GTK+
3172 // handler issues a repaint
3173 const bool retval
= m_wxwindow
? true : false;
3176 // NB: If a control is composed of several GtkWidgets and when focus
3177 // changes from one of them to another within the same wxWindow, we get
3178 // a focus-out event followed by focus-in for another GtkWidget owned
3179 // by the same wx control. We don't want to generate two spurious
3180 // wxEVT_SET_FOCUS events in this case, so we defer sending wx events
3181 // from GTKHandleFocusOut() until we know for sure it's not coming back
3182 // (i.e. in GTKHandleFocusIn() or at idle time).
3183 if ( GTKNeedsToFilterSameWindowFocus() )
3185 wxASSERT_MSG( gs_deferredFocusOut
== NULL
,
3186 "deferred focus out event already pending" );
3187 wxLogTrace(TRACE_FOCUS
,
3188 "deferring focus_out event for %s(%p, %s)",
3189 GetClassInfo()->GetClassName(), this, GetLabel());
3190 gs_deferredFocusOut
= this;
3194 GTKHandleFocusOutNoDeferring();
3199 void wxWindowGTK::GTKHandleFocusOutNoDeferring()
3201 wxLogTrace(TRACE_FOCUS
,
3202 "handling focus_out event for %s(%p, %s)",
3203 GetClassInfo()->GetClassName(), this, GetLabel());
3206 gtk_im_context_focus_out(m_imData
->context
);
3208 if ( gs_currentFocus
!= this )
3210 // Something is terribly wrong, gs_currentFocus is out of sync with the
3211 // real focus. We will reset it to NULL anyway, because after this
3212 // focus-out event is handled, one of the following with happen:
3214 // * either focus will go out of the app altogether, in which case
3215 // gs_currentFocus _should_ be NULL
3217 // * or it goes to another control, in which case focus-in event will
3218 // follow immediately and it will set gs_currentFocus to the right
3220 wxLogDebug("window %s(%p, %s) lost focus even though it didn't have it",
3221 GetClassInfo()->GetClassName(), this, GetLabel());
3223 gs_currentFocus
= NULL
;
3226 // caret needs to be informed about focus change
3227 wxCaret
*caret
= GetCaret();
3230 caret
->OnKillFocus();
3232 #endif // wxUSE_CARET
3234 wxFocusEvent
event( wxEVT_KILL_FOCUS
, GetId() );
3235 event
.SetEventObject( this );
3236 event
.SetWindow( FindFocus() );
3237 GTKProcessEvent( event
);
3241 void wxWindowGTK::GTKHandleDeferredFocusOut()
3243 // NB: See GTKHandleFocusOut() for explanation. This function is called
3244 // from either GTKHandleFocusIn() or OnInternalIdle() to process
3246 if ( gs_deferredFocusOut
)
3248 wxWindowGTK
*win
= gs_deferredFocusOut
;
3249 gs_deferredFocusOut
= NULL
;
3251 wxLogTrace(TRACE_FOCUS
,
3252 "processing deferred focus_out event for %s(%p, %s)",
3253 win
->GetClassInfo()->GetClassName(), win
, win
->GetLabel());
3255 win
->GTKHandleFocusOutNoDeferring();
3259 void wxWindowGTK::SetFocus()
3261 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
3263 // Setting "physical" focus is not immediate in GTK+ and while
3264 // gtk_widget_is_focus ("determines if the widget is the focus widget
3265 // within its toplevel", i.e. returns true for one widget per TLW, not
3266 // globally) returns true immediately after grabbing focus,
3267 // GTK_WIDGET_HAS_FOCUS (which returns true only for the one widget that
3268 // has focus at the moment) takes effect only after the window is shown
3269 // (if it was hidden at the moment of the call) or at the next event loop
3272 // Because we want to FindFocus() call immediately following
3273 // foo->SetFocus() to return foo, we have to keep track of "pending" focus
3275 gs_pendingFocus
= this;
3277 GtkWidget
*widget
= m_wxwindow
? m_wxwindow
: m_focusWidget
;
3279 if ( GTK_IS_CONTAINER(widget
) &&
3280 !gtk_widget_get_can_focus(widget
) )
3282 wxLogTrace(TRACE_FOCUS
,
3283 wxT("Setting focus to a child of %s(%p, %s)"),
3284 GetClassInfo()->GetClassName(), this, GetLabel().c_str());
3285 gtk_widget_child_focus(widget
, GTK_DIR_TAB_FORWARD
);
3289 wxLogTrace(TRACE_FOCUS
,
3290 wxT("Setting focus to %s(%p, %s)"),
3291 GetClassInfo()->GetClassName(), this, GetLabel().c_str());
3292 gtk_widget_grab_focus(widget
);
3296 void wxWindowGTK::SetCanFocus(bool canFocus
)
3298 gtk_widget_set_can_focus(m_widget
, canFocus
);
3300 if ( m_wxwindow
&& (m_widget
!= m_wxwindow
) )
3302 gtk_widget_set_can_focus(m_wxwindow
, canFocus
);
3306 bool wxWindowGTK::Reparent( wxWindowBase
*newParentBase
)
3308 wxCHECK_MSG( (m_widget
!= NULL
), false, wxT("invalid window") );
3310 wxWindowGTK
* const newParent
= (wxWindowGTK
*)newParentBase
;
3312 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3314 if ( !wxWindowBase::Reparent(newParent
) )
3317 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3319 // Notice that old m_parent pointer might be non-NULL here but the widget
3320 // still not have any parent at GTK level if it's a notebook page that had
3321 // been removed from the notebook so test this at GTK level and not wx one.
3322 if ( GtkWidget
*parentGTK
= gtk_widget_get_parent(m_widget
) )
3323 gtk_container_remove(GTK_CONTAINER(parentGTK
), m_widget
);
3325 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3329 if (gtk_widget_get_visible (newParent
->m_widget
))
3331 m_showOnIdle
= true;
3332 gtk_widget_hide( m_widget
);
3334 /* insert GTK representation */
3335 newParent
->AddChildGTK(this);
3338 SetLayoutDirection(wxLayout_Default
);
3343 void wxWindowGTK::DoAddChild(wxWindowGTK
*child
)
3345 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
3346 wxASSERT_MSG( (child
!= NULL
), wxT("invalid child window") );
3351 /* insert GTK representation */
3355 void wxWindowGTK::AddChild(wxWindowBase
*child
)
3357 wxWindowBase::AddChild(child
);
3358 m_dirtyTabOrder
= true;
3359 wxTheApp
->WakeUpIdle();
3362 void wxWindowGTK::RemoveChild(wxWindowBase
*child
)
3364 wxWindowBase::RemoveChild(child
);
3365 m_dirtyTabOrder
= true;
3366 wxTheApp
->WakeUpIdle();
3370 wxLayoutDirection
wxWindowGTK::GTKGetLayout(GtkWidget
*widget
)
3372 return gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
3373 ? wxLayout_RightToLeft
3374 : wxLayout_LeftToRight
;
3378 void wxWindowGTK::GTKSetLayout(GtkWidget
*widget
, wxLayoutDirection dir
)
3380 wxASSERT_MSG( dir
!= wxLayout_Default
, wxT("invalid layout direction") );
3382 gtk_widget_set_direction(widget
,
3383 dir
== wxLayout_RightToLeft
? GTK_TEXT_DIR_RTL
3384 : GTK_TEXT_DIR_LTR
);
3387 wxLayoutDirection
wxWindowGTK::GetLayoutDirection() const
3389 return GTKGetLayout(m_widget
);
3392 void wxWindowGTK::SetLayoutDirection(wxLayoutDirection dir
)
3394 if ( dir
== wxLayout_Default
)
3396 const wxWindow
*const parent
= GetParent();
3399 // inherit layout from parent.
3400 dir
= parent
->GetLayoutDirection();
3402 else // no parent, use global default layout
3404 dir
= wxTheApp
->GetLayoutDirection();
3408 if ( dir
== wxLayout_Default
)
3411 GTKSetLayout(m_widget
, dir
);
3413 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
3414 GTKSetLayout(m_wxwindow
, dir
);
3418 wxWindowGTK::AdjustForLayoutDirection(wxCoord x
,
3419 wxCoord
WXUNUSED(width
),
3420 wxCoord
WXUNUSED(widthTotal
)) const
3422 // We now mirror the coordinates of RTL windows in wxPizza
3426 void wxWindowGTK::DoMoveInTabOrder(wxWindow
*win
, WindowOrder move
)
3428 wxWindowBase::DoMoveInTabOrder(win
, move
);
3429 m_dirtyTabOrder
= true;
3430 wxTheApp
->WakeUpIdle();
3433 bool wxWindowGTK::DoNavigateIn(int flags
)
3435 if ( flags
& wxNavigationKeyEvent::WinChange
)
3437 wxFAIL_MSG( wxT("not implemented") );
3441 else // navigate inside the container
3443 wxWindow
*parent
= wxGetTopLevelParent((wxWindow
*)this);
3444 wxCHECK_MSG( parent
, false, wxT("every window must have a TLW parent") );
3446 GtkDirectionType dir
;
3447 dir
= flags
& wxNavigationKeyEvent::IsForward
? GTK_DIR_TAB_FORWARD
3448 : GTK_DIR_TAB_BACKWARD
;
3451 g_signal_emit_by_name(parent
->m_widget
, "focus", dir
, &rc
);
3457 bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3459 // none needed by default
3463 void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget
* WXUNUSED(w
))
3465 // nothing to do by default since none is needed
3468 void wxWindowGTK::RealizeTabOrder()
3472 if ( !m_children
.empty() )
3474 // we don't only construct the correct focus chain but also use
3475 // this opportunity to update the mnemonic widgets for the widgets
3478 GList
*chain
= NULL
;
3479 wxWindowGTK
* mnemonicWindow
= NULL
;
3481 for ( wxWindowList::const_iterator i
= m_children
.begin();
3482 i
!= m_children
.end();
3485 wxWindowGTK
*win
= *i
;
3487 bool focusableFromKeyboard
= win
->AcceptsFocusFromKeyboard();
3489 if ( mnemonicWindow
)
3491 if ( focusableFromKeyboard
)
3493 // wxComboBox et al. needs to focus on on a different
3494 // widget than m_widget, so if the main widget isn't
3495 // focusable try the connect widget
3496 GtkWidget
* w
= win
->m_widget
;
3497 if ( !gtk_widget_get_can_focus(w
) )
3499 w
= win
->GetConnectWidget();
3500 if ( !gtk_widget_get_can_focus(w
) )
3506 mnemonicWindow
->GTKWidgetDoSetMnemonic(w
);
3507 mnemonicWindow
= NULL
;
3511 else if ( win
->GTKWidgetNeedsMnemonic() )
3513 mnemonicWindow
= win
;
3516 if ( focusableFromKeyboard
)
3517 chain
= g_list_prepend(chain
, win
->m_widget
);
3520 chain
= g_list_reverse(chain
);
3522 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow
), chain
);
3527 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow
));
3532 void wxWindowGTK::Raise()
3534 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3536 if (m_wxwindow
&& gtk_widget_get_window(m_wxwindow
))
3538 gdk_window_raise(gtk_widget_get_window(m_wxwindow
));
3540 else if (gtk_widget_get_window(m_widget
))
3542 gdk_window_raise(gtk_widget_get_window(m_widget
));
3546 void wxWindowGTK::Lower()
3548 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3550 if (m_wxwindow
&& gtk_widget_get_window(m_wxwindow
))
3552 gdk_window_lower(gtk_widget_get_window(m_wxwindow
));
3554 else if (gtk_widget_get_window(m_widget
))
3556 gdk_window_lower(gtk_widget_get_window(m_widget
));
3560 bool wxWindowGTK::SetCursor( const wxCursor
&cursor
)
3562 if ( !wxWindowBase::SetCursor(cursor
.IsOk() ? cursor
: *wxSTANDARD_CURSOR
) )
3570 void wxWindowGTK::GTKUpdateCursor(bool update_self
/*=true*/, bool recurse
/*=true*/)
3574 wxCursor
cursor(g_globalCursor
.IsOk() ? g_globalCursor
: GetCursor());
3575 if ( cursor
.IsOk() )
3577 wxArrayGdkWindows windowsThis
;
3578 GdkWindow
* window
= GTKGetWindow(windowsThis
);
3580 gdk_window_set_cursor( window
, cursor
.GetCursor() );
3583 const size_t count
= windowsThis
.size();
3584 for ( size_t n
= 0; n
< count
; n
++ )
3586 GdkWindow
*win
= windowsThis
[n
];
3587 // It can be zero if the window has not been realized yet.
3590 gdk_window_set_cursor(win
, cursor
.GetCursor());
3599 for (wxWindowList::iterator it
= GetChildren().begin(); it
!= GetChildren().end(); ++it
)
3601 (*it
)->GTKUpdateCursor( true );
3606 void wxWindowGTK::WarpPointer( int x
, int y
)
3608 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3610 ClientToScreen(&x
, &y
);
3611 GdkDisplay
* display
= gtk_widget_get_display(m_widget
);
3612 GdkScreen
* screen
= gtk_widget_get_screen(m_widget
);
3614 GdkDeviceManager
* manager
= gdk_display_get_device_manager(display
);
3615 gdk_device_warp(gdk_device_manager_get_client_pointer(manager
), screen
, x
, y
);
3617 #ifdef GDK_WINDOWING_X11
3618 XWarpPointer(GDK_DISPLAY_XDISPLAY(display
),
3620 GDK_WINDOW_XID(gdk_screen_get_root_window(screen
)),
3626 wxWindowGTK::ScrollDir
wxWindowGTK::ScrollDirFromRange(GtkRange
*range
) const
3628 // find the scrollbar which generated the event
3629 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
3631 if ( range
== m_scrollBar
[dir
] )
3632 return (ScrollDir
)dir
;
3635 wxFAIL_MSG( wxT("event from unknown scrollbar received") );
3637 return ScrollDir_Max
;
3640 bool wxWindowGTK::DoScrollByUnits(ScrollDir dir
, ScrollUnit unit
, int units
)
3642 bool changed
= false;
3643 GtkRange
* range
= m_scrollBar
[dir
];
3644 if ( range
&& units
)
3646 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
3647 double inc
= unit
== ScrollUnit_Line
? gtk_adjustment_get_step_increment(adj
)
3648 : gtk_adjustment_get_page_increment(adj
);
3650 const int posOld
= wxRound(gtk_adjustment_get_value(adj
));
3651 gtk_range_set_value(range
, posOld
+ units
*inc
);
3653 changed
= wxRound(gtk_adjustment_get_value(adj
)) != posOld
;
3659 bool wxWindowGTK::ScrollLines(int lines
)
3661 return DoScrollByUnits(ScrollDir_Vert
, ScrollUnit_Line
, lines
);
3664 bool wxWindowGTK::ScrollPages(int pages
)
3666 return DoScrollByUnits(ScrollDir_Vert
, ScrollUnit_Page
, pages
);
3669 void wxWindowGTK::Refresh(bool WXUNUSED(eraseBackground
),
3674 if (gtk_widget_get_mapped(m_wxwindow
))
3676 GdkWindow
* window
= gtk_widget_get_window(m_wxwindow
);
3679 GdkRectangle r
= { rect
->x
, rect
->y
, rect
->width
, rect
->height
};
3680 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3681 r
.x
= gdk_window_get_width(window
) - r
.x
- rect
->width
;
3682 gdk_window_invalidate_rect(window
, &r
, true);
3685 gdk_window_invalidate_rect(window
, NULL
, true);
3690 if (gtk_widget_get_mapped(m_widget
))
3693 gtk_widget_queue_draw_area(m_widget
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
3695 gtk_widget_queue_draw(m_widget
);
3700 void wxWindowGTK::Update()
3702 if (m_widget
&& gtk_widget_get_mapped(m_widget
))
3704 GdkDisplay
* display
= gtk_widget_get_display(m_widget
);
3705 // Flush everything out to the server, and wait for it to finish.
3706 // This ensures nothing will overwrite the drawing we are about to do.
3707 gdk_display_sync(display
);
3709 GdkWindow
* window
= GTKGetDrawingWindow();
3711 window
= gtk_widget_get_window(m_widget
);
3712 gdk_window_process_updates(window
, true);
3714 // Flush again, but no need to wait for it to finish
3715 gdk_display_flush(display
);
3719 bool wxWindowGTK::DoIsExposed( int x
, int y
) const
3721 return m_updateRegion
.Contains(x
, y
) != wxOutRegion
;
3724 bool wxWindowGTK::DoIsExposed( int x
, int y
, int w
, int h
) const
3726 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3727 return m_updateRegion
.Contains(x
-w
, y
, w
, h
) != wxOutRegion
;
3729 return m_updateRegion
.Contains(x
, y
, w
, h
) != wxOutRegion
;
3732 void wxWindowGTK::GtkSendPaintEvents()
3736 m_updateRegion
.Clear();
3739 #if wxGTK_HAS_COMPOSITING_SUPPORT
3742 // Clip to paint region in wxClientDC
3743 m_clipPaintRegion
= true;
3745 m_nativeUpdateRegion
= m_updateRegion
;
3747 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3749 // Transform m_updateRegion under RTL
3750 m_updateRegion
.Clear();
3753 gdk_drawable_get_size(gtk_widget_get_window(m_wxwindow
), &width
, NULL
);
3755 wxRegionIterator
upd( m_nativeUpdateRegion
);
3759 rect
.x
= upd
.GetX();
3760 rect
.y
= upd
.GetY();
3761 rect
.width
= upd
.GetWidth();
3762 rect
.height
= upd
.GetHeight();
3764 rect
.x
= width
- rect
.x
- rect
.width
;
3765 m_updateRegion
.Union( rect
);
3771 switch ( GetBackgroundStyle() )
3773 case wxBG_STYLE_TRANSPARENT
:
3774 #if wxGTK_HAS_COMPOSITING_SUPPORT
3775 if (IsTransparentBackgroundSupported())
3777 // Set a transparent background, so that overlaying in parent
3778 // might indeed let see through where this child did not
3779 // explicitly paint.
3780 // NB: it works also for top level windows (but this is the
3781 // windows manager which then does the compositing job)
3782 cr
= gdk_cairo_create(m_wxwindow
->window
);
3783 gdk_cairo_region(cr
, m_nativeUpdateRegion
.GetRegion());
3786 cairo_set_operator(cr
, CAIRO_OPERATOR_CLEAR
);
3788 cairo_set_operator(cr
, CAIRO_OPERATOR_OVER
);
3789 cairo_surface_flush(cairo_get_target(cr
));
3791 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
3794 case wxBG_STYLE_ERASE
:
3796 wxWindowDC
dc( (wxWindow
*)this );
3797 dc
.SetDeviceClippingRegion( m_updateRegion
);
3799 // Work around gtk-qt <= 0.60 bug whereby the window colour
3803 GetOptionInt("gtk.window.force-background-colour") )
3805 dc
.SetBackground(GetBackgroundColour());
3809 wxEraseEvent
erase_event( GetId(), &dc
);
3810 erase_event
.SetEventObject( this );
3812 if ( HandleWindowEvent(erase_event
) )
3814 // background erased, don't do it again
3820 case wxBG_STYLE_SYSTEM
:
3821 if ( GetThemeEnabled() )
3823 // find ancestor from which to steal background
3824 wxWindow
*parent
= wxGetTopLevelParent((wxWindow
*)this);
3826 parent
= (wxWindow
*)this;
3828 if (gtk_widget_get_mapped(parent
->m_widget
))
3830 wxRegionIterator
upd( m_nativeUpdateRegion
);
3834 rect
.x
= upd
.GetX();
3835 rect
.y
= upd
.GetY();
3836 rect
.width
= upd
.GetWidth();
3837 rect
.height
= upd
.GetHeight();
3839 gtk_paint_flat_box(gtk_widget_get_style(parent
->m_widget
),
3840 GTKGetDrawingWindow(),
3841 gtk_widget_get_state(m_wxwindow
),
3854 case wxBG_STYLE_PAINT
:
3855 // nothing to do: window will be painted over in EVT_PAINT
3859 wxFAIL_MSG( "unsupported background style" );
3862 wxNcPaintEvent
nc_paint_event( GetId() );
3863 nc_paint_event
.SetEventObject( this );
3864 HandleWindowEvent( nc_paint_event
);
3866 wxPaintEvent
paint_event( GetId() );
3867 paint_event
.SetEventObject( this );
3868 HandleWindowEvent( paint_event
);
3870 #if wxGTK_HAS_COMPOSITING_SUPPORT
3871 if (IsTransparentBackgroundSupported())
3872 { // now composite children which need it
3873 // Overlay all our composite children on top of the painted area
3874 wxWindowList::compatibility_iterator node
;
3875 for ( node
= m_children
.GetFirst(); node
; node
= node
->GetNext() )
3877 wxWindow
*compositeChild
= node
->GetData();
3878 if (compositeChild
->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT
)
3882 cr
= gdk_cairo_create(m_wxwindow
->window
);
3883 gdk_cairo_region(cr
, m_nativeUpdateRegion
.GetRegion());
3887 GtkWidget
*child
= compositeChild
->m_wxwindow
;
3888 GtkAllocation alloc
;
3889 gtk_widget_get_allocation(child
, &alloc
);
3891 // The source data is the (composited) child
3892 gdk_cairo_set_source_window(
3893 cr
, gtk_widget_get_window(child
), alloc
.x
, alloc
.y
);
3901 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
3903 m_clipPaintRegion
= false;
3905 m_updateRegion
.Clear();
3906 m_nativeUpdateRegion
.Clear();
3909 void wxWindowGTK::SetDoubleBuffered( bool on
)
3911 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3914 gtk_widget_set_double_buffered( m_wxwindow
, on
);
3917 bool wxWindowGTK::IsDoubleBuffered() const
3919 return gtk_widget_get_double_buffered( m_wxwindow
);
3922 void wxWindowGTK::ClearBackground()
3924 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
3928 void wxWindowGTK::DoSetToolTip( wxToolTip
*tip
)
3930 if (m_tooltip
!= tip
)
3932 wxWindowBase::DoSetToolTip(tip
);
3935 m_tooltip
->GTKSetWindow(static_cast<wxWindow
*>(this));
3937 GTKApplyToolTip(NULL
);
3941 void wxWindowGTK::GTKApplyToolTip(const char* tip
)
3943 wxToolTip::GTKApply(GetConnectWidget(), tip
);
3945 #endif // wxUSE_TOOLTIPS
3947 bool wxWindowGTK::SetBackgroundColour( const wxColour
&colour
)
3949 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
3951 if (!wxWindowBase::SetBackgroundColour(colour
))
3956 // We need the pixel value e.g. for background clearing.
3957 m_backgroundColour
.CalcPixel(gtk_widget_get_colormap(m_widget
));
3960 // apply style change (forceStyle=true so that new style is applied
3961 // even if the bg colour changed from valid to wxNullColour)
3962 GTKApplyWidgetStyle(true);
3967 bool wxWindowGTK::SetForegroundColour( const wxColour
&colour
)
3969 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
3971 if (!wxWindowBase::SetForegroundColour(colour
))
3978 // We need the pixel value e.g. for background clearing.
3979 m_foregroundColour
.CalcPixel(gtk_widget_get_colormap(m_widget
));
3982 // apply style change (forceStyle=true so that new style is applied
3983 // even if the bg colour changed from valid to wxNullColour):
3984 GTKApplyWidgetStyle(true);
3989 PangoContext
*wxWindowGTK::GTKGetPangoDefaultContext()
3991 return gtk_widget_get_pango_context( m_widget
);
3994 GtkRcStyle
*wxWindowGTK::GTKCreateWidgetStyle(bool forceStyle
)
3996 // do we need to apply any changes at all?
3999 !m_foregroundColour
.IsOk() && !m_backgroundColour
.IsOk() )
4004 GtkRcStyle
*style
= gtk_rc_style_new();
4006 if ( m_font
.IsOk() )
4009 pango_font_description_copy( m_font
.GetNativeFontInfo()->description
);
4012 int flagsNormal
= 0,
4015 flagsInsensitive
= 0;
4017 if ( m_foregroundColour
.IsOk() )
4019 const GdkColor
*fg
= m_foregroundColour
.GetColor();
4021 style
->fg
[GTK_STATE_NORMAL
] =
4022 style
->text
[GTK_STATE_NORMAL
] = *fg
;
4023 flagsNormal
|= GTK_RC_FG
| GTK_RC_TEXT
;
4025 style
->fg
[GTK_STATE_PRELIGHT
] =
4026 style
->text
[GTK_STATE_PRELIGHT
] = *fg
;
4027 flagsPrelight
|= GTK_RC_FG
| GTK_RC_TEXT
;
4029 style
->fg
[GTK_STATE_ACTIVE
] =
4030 style
->text
[GTK_STATE_ACTIVE
] = *fg
;
4031 flagsActive
|= GTK_RC_FG
| GTK_RC_TEXT
;
4034 if ( m_backgroundColour
.IsOk() )
4036 const GdkColor
*bg
= m_backgroundColour
.GetColor();
4038 style
->bg
[GTK_STATE_NORMAL
] =
4039 style
->base
[GTK_STATE_NORMAL
] = *bg
;
4040 flagsNormal
|= GTK_RC_BG
| GTK_RC_BASE
;
4042 style
->bg
[GTK_STATE_PRELIGHT
] =
4043 style
->base
[GTK_STATE_PRELIGHT
] = *bg
;
4044 flagsPrelight
|= GTK_RC_BG
| GTK_RC_BASE
;
4046 style
->bg
[GTK_STATE_ACTIVE
] =
4047 style
->base
[GTK_STATE_ACTIVE
] = *bg
;
4048 flagsActive
|= GTK_RC_BG
| GTK_RC_BASE
;
4050 style
->bg
[GTK_STATE_INSENSITIVE
] =
4051 style
->base
[GTK_STATE_INSENSITIVE
] = *bg
;
4052 flagsInsensitive
|= GTK_RC_BG
| GTK_RC_BASE
;
4055 style
->color_flags
[GTK_STATE_NORMAL
] = (GtkRcFlags
)flagsNormal
;
4056 style
->color_flags
[GTK_STATE_PRELIGHT
] = (GtkRcFlags
)flagsPrelight
;
4057 style
->color_flags
[GTK_STATE_ACTIVE
] = (GtkRcFlags
)flagsActive
;
4058 style
->color_flags
[GTK_STATE_INSENSITIVE
] = (GtkRcFlags
)flagsInsensitive
;
4063 void wxWindowGTK::GTKApplyWidgetStyle(bool forceStyle
)
4065 GtkRcStyle
*style
= GTKCreateWidgetStyle(forceStyle
);
4068 DoApplyWidgetStyle(style
);
4069 g_object_unref(style
);
4072 // Style change may affect GTK+'s size calculation:
4073 InvalidateBestSize();
4076 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle
*style
)
4080 // block the signal temporarily to avoid sending
4081 // wxSysColourChangedEvents when we change the colours ourselves
4082 bool unblock
= false;
4086 g_signal_handlers_block_by_func(
4087 m_wxwindow
, (void *)gtk_window_style_set_callback
, this);
4090 gtk_widget_modify_style(m_wxwindow
, style
);
4094 g_signal_handlers_unblock_by_func(
4095 m_wxwindow
, (void *)gtk_window_style_set_callback
, this);
4100 gtk_widget_modify_style(m_widget
, style
);
4104 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style
)
4106 if (!wxWindowBase::SetBackgroundStyle(style
))
4112 window
= GTKGetDrawingWindow();
4116 GtkWidget
* const w
= GetConnectWidget();
4117 window
= w
? gtk_widget_get_window(w
) : NULL
;
4120 bool wantNoBackPixmap
= style
== wxBG_STYLE_PAINT
|| style
== wxBG_STYLE_TRANSPARENT
;
4122 if ( wantNoBackPixmap
)
4126 // Make sure GDK/X11 doesn't refresh the window
4128 gdk_window_set_back_pixmap( window
, NULL
, FALSE
);
4129 m_needsStyleChange
= false;
4131 else // window not realized yet
4133 // Do when window is realized
4134 m_needsStyleChange
= true;
4137 // Don't apply widget style, or we get a grey background
4141 // apply style change (forceStyle=true so that new style is applied
4142 // even if the bg colour changed from valid to wxNullColour):
4143 GTKApplyWidgetStyle(true);
4149 bool wxWindowGTK::IsTransparentBackgroundSupported(wxString
* reason
) const
4151 #if wxGTK_HAS_COMPOSITING_SUPPORT
4152 if (gtk_check_version(wxGTK_VERSION_REQUIRED_FOR_COMPOSITING
) != NULL
)
4156 *reason
= _("GTK+ installed on this machine is too old to "
4157 "support screen compositing, please install "
4158 "GTK+ 2.12 or later.");
4164 // NB: We don't check here if the particular kind of widget supports
4165 // transparency, we check only if it would be possible for a generic window
4167 wxCHECK_MSG ( m_widget
, false, "Window must be created first" );
4169 if (!gdk_screen_is_composited(gtk_widget_get_screen(m_widget
)))
4173 *reason
= _("Compositing not supported by this system, "
4174 "please enable it in your Window Manager.");
4184 *reason
= _("This program was compiled with a too old version of GTK+, "
4185 "please rebuild with GTK+ 2.12 or newer.");
4187 #endif // wxGTK_HAS_COMPOSITING_SUPPORT/!wxGTK_HAS_COMPOSITING_SUPPORT
4192 // ----------------------------------------------------------------------------
4193 // Pop-up menu stuff
4194 // ----------------------------------------------------------------------------
4196 #if wxUSE_MENUS_NATIVE
4200 void wxPopupMenuPositionCallback( GtkMenu
*menu
,
4202 gboolean
* WXUNUSED(whatever
),
4203 gpointer user_data
)
4205 // ensure that the menu appears entirely on screen
4207 gtk_widget_get_child_requisition(GTK_WIDGET(menu
), &req
);
4209 wxSize sizeScreen
= wxGetDisplaySize();
4210 wxPoint
*pos
= (wxPoint
*)user_data
;
4212 gint xmax
= sizeScreen
.x
- req
.width
,
4213 ymax
= sizeScreen
.y
- req
.height
;
4215 *x
= pos
->x
< xmax
? pos
->x
: xmax
;
4216 *y
= pos
->y
< ymax
? pos
->y
: ymax
;
4220 bool wxWindowGTK::DoPopupMenu( wxMenu
*menu
, int x
, int y
)
4222 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
4228 GtkMenuPositionFunc posfunc
;
4229 if ( x
== -1 && y
== -1 )
4231 // use GTK's default positioning algorithm
4237 pos
= ClientToScreen(wxPoint(x
, y
));
4239 posfunc
= wxPopupMenuPositionCallback
;
4242 menu
->m_popupShown
= true;
4244 GTK_MENU(menu
->m_menu
),
4245 NULL
, // parent menu shell
4246 NULL
, // parent menu item
4247 posfunc
, // function to position it
4248 userdata
, // client data
4249 0, // button used to activate it
4250 gtk_get_current_event_time()
4253 while (menu
->m_popupShown
)
4255 gtk_main_iteration();
4261 #endif // wxUSE_MENUS_NATIVE
4263 #if wxUSE_DRAG_AND_DROP
4265 void wxWindowGTK::SetDropTarget( wxDropTarget
*dropTarget
)
4267 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4269 GtkWidget
*dnd_widget
= GetConnectWidget();
4271 if (m_dropTarget
) m_dropTarget
->GtkUnregisterWidget( dnd_widget
);
4273 if (m_dropTarget
) delete m_dropTarget
;
4274 m_dropTarget
= dropTarget
;
4276 if (m_dropTarget
) m_dropTarget
->GtkRegisterWidget( dnd_widget
);
4279 #endif // wxUSE_DRAG_AND_DROP
4281 GtkWidget
* wxWindowGTK::GetConnectWidget()
4283 GtkWidget
*connect_widget
= m_widget
;
4284 if (m_wxwindow
) connect_widget
= m_wxwindow
;
4286 return connect_widget
;
4289 bool wxWindowGTK::GTKIsOwnWindow(GdkWindow
*window
) const
4291 wxArrayGdkWindows windowsThis
;
4292 GdkWindow
* const winThis
= GTKGetWindow(windowsThis
);
4294 return winThis
? window
== winThis
4295 : windowsThis
.Index(window
) != wxNOT_FOUND
;
4298 GdkWindow
*wxWindowGTK::GTKGetWindow(wxArrayGdkWindows
& WXUNUSED(windows
)) const
4300 return m_wxwindow
? GTKGetDrawingWindow() : gtk_widget_get_window(m_widget
);
4303 bool wxWindowGTK::SetFont( const wxFont
&font
)
4305 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
4307 if (!wxWindowBase::SetFont(font
))
4310 // apply style change (forceStyle=true so that new style is applied
4311 // even if the font changed from valid to wxNullFont):
4312 GTKApplyWidgetStyle(true);
4317 void wxWindowGTK::DoCaptureMouse()
4319 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4321 GdkWindow
*window
= NULL
;
4323 window
= GTKGetDrawingWindow();
4325 window
= gtk_widget_get_window(GetConnectWidget());
4327 wxCHECK_RET( window
, wxT("CaptureMouse() failed") );
4329 const wxCursor
* cursor
= &m_cursor
;
4330 if (!cursor
->IsOk())
4331 cursor
= wxSTANDARD_CURSOR
;
4333 gdk_pointer_grab( window
, FALSE
,
4335 (GDK_BUTTON_PRESS_MASK
|
4336 GDK_BUTTON_RELEASE_MASK
|
4337 GDK_POINTER_MOTION_HINT_MASK
|
4338 GDK_POINTER_MOTION_MASK
),
4340 cursor
->GetCursor(),
4341 (guint32
)GDK_CURRENT_TIME
);
4342 g_captureWindow
= this;
4343 g_captureWindowHasMouse
= true;
4346 void wxWindowGTK::DoReleaseMouse()
4348 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4350 wxCHECK_RET( g_captureWindow
, wxT("can't release mouse - not captured") );
4352 g_captureWindow
= NULL
;
4354 GdkWindow
*window
= NULL
;
4356 window
= GTKGetDrawingWindow();
4358 window
= gtk_widget_get_window(GetConnectWidget());
4363 gdk_pointer_ungrab ( (guint32
)GDK_CURRENT_TIME
);
4366 void wxWindowGTK::GTKReleaseMouseAndNotify()
4369 wxMouseCaptureLostEvent
evt(GetId());
4370 evt
.SetEventObject( this );
4371 HandleWindowEvent( evt
);
4375 wxWindow
*wxWindowBase::GetCapture()
4377 return (wxWindow
*)g_captureWindow
;
4380 bool wxWindowGTK::IsRetained() const
4385 void wxWindowGTK::SetScrollbar(int orient
,
4389 bool WXUNUSED(update
))
4391 const int dir
= ScrollDirFromOrient(orient
);
4392 GtkRange
* const sb
= m_scrollBar
[dir
];
4393 wxCHECK_RET( sb
, wxT("this window is not scrollable") );
4397 // GtkRange requires upper > lower
4402 g_signal_handlers_block_by_func(
4403 sb
, (void*)gtk_scrollbar_value_changed
, this);
4405 gtk_range_set_increments(sb
, 1, thumbVisible
);
4406 gtk_adjustment_set_page_size(gtk_range_get_adjustment(sb
), thumbVisible
);
4407 gtk_range_set_range(sb
, 0, range
);
4408 gtk_range_set_value(sb
, pos
);
4409 m_scrollPos
[dir
] = gtk_range_get_value(sb
);
4411 g_signal_handlers_unblock_by_func(
4412 sb
, (void*)gtk_scrollbar_value_changed
, this);
4415 void wxWindowGTK::SetScrollPos(int orient
, int pos
, bool WXUNUSED(refresh
))
4417 const int dir
= ScrollDirFromOrient(orient
);
4418 GtkRange
* const sb
= m_scrollBar
[dir
];
4419 wxCHECK_RET( sb
, wxT("this window is not scrollable") );
4421 // This check is more than an optimization. Without it, the slider
4422 // will not move smoothly while tracking when using wxScrollHelper.
4423 if (GetScrollPos(orient
) != pos
)
4425 g_signal_handlers_block_by_func(
4426 sb
, (void*)gtk_scrollbar_value_changed
, this);
4428 gtk_range_set_value(sb
, pos
);
4429 m_scrollPos
[dir
] = gtk_range_get_value(sb
);
4431 g_signal_handlers_unblock_by_func(
4432 sb
, (void*)gtk_scrollbar_value_changed
, this);
4436 int wxWindowGTK::GetScrollThumb(int orient
) const
4438 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4439 wxCHECK_MSG( sb
, 0, wxT("this window is not scrollable") );
4441 return wxRound(gtk_adjustment_get_page_size(gtk_range_get_adjustment(sb
)));
4444 int wxWindowGTK::GetScrollPos( int orient
) const
4446 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4447 wxCHECK_MSG( sb
, 0, wxT("this window is not scrollable") );
4449 return wxRound(gtk_range_get_value(sb
));
4452 int wxWindowGTK::GetScrollRange( int orient
) const
4454 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4455 wxCHECK_MSG( sb
, 0, wxT("this window is not scrollable") );
4457 return wxRound(gtk_adjustment_get_upper(gtk_range_get_adjustment(sb
)));
4460 // Determine if increment is the same as +/-x, allowing for some small
4461 // difference due to possible inexactness in floating point arithmetic
4462 static inline bool IsScrollIncrement(double increment
, double x
)
4464 wxASSERT(increment
> 0);
4465 const double tolerance
= 1.0 / 1024;
4466 return fabs(increment
- fabs(x
)) < tolerance
;
4469 wxEventType
wxWindowGTK::GTKGetScrollEventType(GtkRange
* range
)
4471 wxASSERT(range
== m_scrollBar
[0] || range
== m_scrollBar
[1]);
4473 const int barIndex
= range
== m_scrollBar
[1];
4475 const double value
= gtk_range_get_value(range
);
4477 // save previous position
4478 const double oldPos
= m_scrollPos
[barIndex
];
4479 // update current position
4480 m_scrollPos
[barIndex
] = value
;
4481 // If event should be ignored, or integral position has not changed
4482 if (!m_hasVMT
|| g_blockEventsOnDrag
|| wxRound(value
) == wxRound(oldPos
))
4487 wxEventType eventType
= wxEVT_SCROLL_THUMBTRACK
;
4490 // Difference from last change event
4491 const double diff
= value
- oldPos
;
4492 const bool isDown
= diff
> 0;
4494 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
4495 if (IsScrollIncrement(gtk_adjustment_get_step_increment(adj
), diff
))
4497 eventType
= isDown
? wxEVT_SCROLL_LINEDOWN
: wxEVT_SCROLL_LINEUP
;
4499 else if (IsScrollIncrement(gtk_adjustment_get_page_increment(adj
), diff
))
4501 eventType
= isDown
? wxEVT_SCROLL_PAGEDOWN
: wxEVT_SCROLL_PAGEUP
;
4503 else if (m_mouseButtonDown
)
4505 // Assume track event
4506 m_isScrolling
= true;
4512 void wxWindowGTK::ScrollWindow( int dx
, int dy
, const wxRect
* WXUNUSED(rect
) )
4514 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4516 wxCHECK_RET( m_wxwindow
!= NULL
, wxT("window needs client area for scrolling") );
4518 // No scrolling requested.
4519 if ((dx
== 0) && (dy
== 0)) return;
4521 m_clipPaintRegion
= true;
4523 WX_PIZZA(m_wxwindow
)->scroll(dx
, dy
);
4525 m_clipPaintRegion
= false;
4528 bool restoreCaret
= (GetCaret() != NULL
&& GetCaret()->IsVisible());
4531 wxRect
caretRect(GetCaret()->GetPosition(), GetCaret()->GetSize());
4533 caretRect
.width
+= dx
;
4536 caretRect
.x
+= dx
; caretRect
.width
-= dx
;
4539 caretRect
.height
+= dy
;
4542 caretRect
.y
+= dy
; caretRect
.height
-= dy
;
4545 RefreshRect(caretRect
);
4547 #endif // wxUSE_CARET
4550 void wxWindowGTK::GTKScrolledWindowSetBorder(GtkWidget
* w
, int wxstyle
)
4552 //RN: Note that static controls usually have no border on gtk, so maybe
4553 //it makes sense to treat that as simply no border at the wx level
4555 if (!(wxstyle
& wxNO_BORDER
) && !(wxstyle
& wxBORDER_STATIC
))
4557 GtkShadowType gtkstyle
;
4559 if(wxstyle
& wxBORDER_RAISED
)
4560 gtkstyle
= GTK_SHADOW_OUT
;
4561 else if ((wxstyle
& wxBORDER_SUNKEN
) || (wxstyle
& wxBORDER_THEME
))
4562 gtkstyle
= GTK_SHADOW_IN
;
4565 else if (wxstyle
& wxBORDER_DOUBLE
)
4566 gtkstyle
= GTK_SHADOW_ETCHED_IN
;
4569 gtkstyle
= GTK_SHADOW_IN
;
4571 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w
),
4576 // Find the wxWindow at the current mouse position, also returning the mouse
4578 wxWindow
* wxFindWindowAtPointer(wxPoint
& pt
)
4580 pt
= wxGetMousePosition();
4581 wxWindow
* found
= wxFindWindowAtPoint(pt
);
4585 // Get the current mouse position.
4586 wxPoint
wxGetMousePosition()
4588 wxWindow
* tlw
= NULL
;
4589 if (!wxTopLevelWindows
.empty())
4590 tlw
= wxTopLevelWindows
.front();
4591 GdkDisplay
* display
;
4592 if (tlw
&& tlw
->m_widget
)
4593 display
= gtk_widget_get_display(tlw
->m_widget
);
4595 display
= gdk_display_get_default();
4598 gdk_display_get_pointer(display
, NULL
, &x
, &y
, NULL
);
4599 return wxPoint(x
, y
);
4602 GdkWindow
* wxWindowGTK::GTKGetDrawingWindow() const
4604 GdkWindow
* window
= NULL
;
4606 window
= gtk_widget_get_window(m_wxwindow
);
4610 // ----------------------------------------------------------------------------
4612 // ----------------------------------------------------------------------------
4617 // this is called if we attempted to freeze unrealized widget when it finally
4618 // is realized (and so can be frozen):
4619 static void wx_frozen_widget_realize(GtkWidget
* w
, wxWindowGTK
* win
)
4621 wxASSERT( w
&& gtk_widget_get_has_window(w
) );
4622 wxASSERT( gtk_widget_get_realized(w
) );
4624 g_signal_handlers_disconnect_by_func
4627 (void*)wx_frozen_widget_realize
,
4632 if (w
== win
->m_wxwindow
)
4633 window
= win
->GTKGetDrawingWindow();
4635 window
= gtk_widget_get_window(w
);
4636 gdk_window_freeze_updates(window
);
4641 void wxWindowGTK::GTKFreezeWidget(GtkWidget
*w
)
4643 if ( !w
|| !gtk_widget_get_has_window(w
) )
4644 return; // window-less widget, cannot be frozen
4646 GdkWindow
* window
= gtk_widget_get_window(w
);
4649 // we can't thaw unrealized widgets because they don't have GdkWindow,
4650 // so set it up to be done immediately after realization:
4651 g_signal_connect_after
4655 G_CALLBACK(wx_frozen_widget_realize
),
4661 if (w
== m_wxwindow
)
4662 window
= GTKGetDrawingWindow();
4663 gdk_window_freeze_updates(window
);
4666 void wxWindowGTK::GTKThawWidget(GtkWidget
*w
)
4668 if ( !w
|| !gtk_widget_get_has_window(w
) )
4669 return; // window-less widget, cannot be frozen
4671 GdkWindow
* window
= gtk_widget_get_window(w
);
4674 // the widget wasn't realized yet, no need to thaw
4675 g_signal_handlers_disconnect_by_func
4678 (void*)wx_frozen_widget_realize
,
4684 if (w
== m_wxwindow
)
4685 window
= GTKGetDrawingWindow();
4686 gdk_window_thaw_updates(window
);
4689 void wxWindowGTK::DoFreeze()
4691 GTKFreezeWidget(m_widget
);
4692 if ( m_wxwindow
&& m_widget
!= m_wxwindow
)
4693 GTKFreezeWidget(m_wxwindow
);
4696 void wxWindowGTK::DoThaw()
4698 GTKThawWidget(m_widget
);
4699 if ( m_wxwindow
&& m_widget
!= m_wxwindow
)
4700 GTKThawWidget(m_wxwindow
);