1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/window.cpp
3 // Purpose: wxWindowGTK implementation
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling, Julian Smart
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
13 #define XWarpPointer XWARPPOINTER
16 #include "wx/window.h"
21 #include "wx/toplevel.h"
22 #include "wx/dcclient.h"
24 #include "wx/settings.h"
25 #include "wx/msgdlg.h"
30 #include "wx/tooltip.h"
32 #include "wx/fontutil.h"
33 #include "wx/sysopt.h"
35 #include "wx/gtk/dc.h"
41 #include "wx/gtk/private.h"
42 #include "wx/gtk/private/gtk2-compat.h"
43 #include "wx/gtk/private/event.h"
44 #include "wx/gtk/private/win_gtk.h"
45 #include "wx/private/textmeasure.h"
46 using namespace wxGTKImpl
;
48 #ifdef GDK_WINDOWING_X11
50 #include "wx/x11/private/wrapxkb.h"
55 #include <gdk/gdkkeysyms.h>
57 #include <gdk/gdkkeysyms-compat.h>
60 // gdk_window_set_composited() is only supported since 2.12
61 #define wxGTK_VERSION_REQUIRED_FOR_COMPOSITING 2,12,0
62 #define wxGTK_HAS_COMPOSITING_SUPPORT GTK_CHECK_VERSION(2,12,0)
64 //-----------------------------------------------------------------------------
65 // documentation on internals
66 //-----------------------------------------------------------------------------
69 I have been asked several times about writing some documentation about
70 the GTK port of wxWidgets, especially its internal structures. Obviously,
71 you cannot understand wxGTK without knowing a little about the GTK, but
72 some more information about what the wxWindow, which is the base class
73 for all other window classes, does seems required as well.
77 What does wxWindow do? It contains the common interface for the following
78 jobs of its descendants:
80 1) Define the rudimentary behaviour common to all window classes, such as
81 resizing, intercepting user input (so as to make it possible to use these
82 events for special purposes in a derived class), window names etc.
84 2) Provide the possibility to contain and manage children, if the derived
85 class is allowed to contain children, which holds true for those window
86 classes which do not display a native GTK widget. To name them, these
87 classes are wxPanel, wxScrolledWindow, wxDialog, wxFrame. The MDI frame-
88 work classes are a special case and are handled a bit differently from
89 the rest. The same holds true for the wxNotebook class.
91 3) Provide the possibility to draw into a client area of a window. This,
92 too, only holds true for classes that do not display a native GTK widget
95 4) Provide the entire mechanism for scrolling widgets. This actual inter-
96 face for this is usually in wxScrolledWindow, but the GTK implementation
99 5) A multitude of helper or extra methods for special purposes, such as
100 Drag'n'Drop, managing validators etc.
102 6) Display a border (sunken, raised, simple or none).
104 Normally one might expect, that one wxWidgets window would always correspond
105 to one GTK widget. Under GTK, there is no such all-round widget that has all
106 the functionality. Moreover, the GTK defines a client area as a different
107 widget from the actual widget you are handling. Last but not least some
108 special classes (e.g. wxFrame) handle different categories of widgets and
109 still have the possibility to draw something in the client area.
110 It was therefore required to write a special purpose GTK widget, that would
111 represent a client area in the sense of wxWidgets capable to do the jobs
112 2), 3) and 4). I have written this class and it resides in win_gtk.c of
115 All windows must have a widget, with which they interact with other under-
116 lying GTK widgets. It is this widget, e.g. that has to be resized etc and
117 the wxWindow class has a member variable called m_widget which holds a
118 pointer to this widget. When the window class represents a GTK native widget,
119 this is (in most cases) the only GTK widget the class manages. E.g. the
120 wxStaticText class handles only a GtkLabel widget a pointer to which you
121 can find in m_widget (defined in wxWindow)
123 When the class has a client area for drawing into and for containing children
124 it has to handle the client area widget (of the type wxPizza, defined in
125 win_gtk.cpp), but there could be any number of widgets, handled by a class.
126 The common rule for all windows is only, that the widget that interacts with
127 the rest of GTK must be referenced in m_widget and all other widgets must be
128 children of this widget on the GTK level. The top-most widget, which also
129 represents the client area, must be in the m_wxwindow field and must be of
132 As I said, the window classes that display a GTK native widget only have
133 one widget, so in the case of e.g. the wxButton class m_widget holds a
134 pointer to a GtkButton widget. But windows with client areas (for drawing
135 and children) have a m_widget field that is a pointer to a GtkScrolled-
136 Window and a m_wxwindow field that is pointer to a wxPizza and this
137 one is (in the GTK sense) a child of the GtkScrolledWindow.
139 If the m_wxwindow field is set, then all input to this widget is inter-
140 cepted and sent to the wxWidgets class. If not, all input to the widget
141 that gets pointed to by m_widget gets intercepted and sent to the class.
145 The design of scrolling in wxWidgets is markedly different from that offered
146 by the GTK itself and therefore we cannot simply take it as it is. In GTK,
147 clicking on a scrollbar belonging to scrolled window will inevitably move
148 the window. In wxWidgets, the scrollbar will only emit an event, send this
149 to (normally) a wxScrolledWindow and that class will call ScrollWindow()
150 which actually moves the window and its sub-windows. Note that wxPizza
151 memorizes how much it has been scrolled but that wxWidgets forgets this
152 so that the two coordinates systems have to be kept in synch. This is done
153 in various places using the pizza->m_scroll_x and pizza->m_scroll_y values.
157 Singularly the most broken code in GTK is the code that is supposed to
158 inform subwindows (child windows) about new positions. Very often, duplicate
159 events are sent without changes in size or position, equally often no
160 events are sent at all (All this is due to a bug in the GtkContainer code
161 which got fixed in GTK 1.2.6). For that reason, wxGTK completely ignores
162 GTK's own system and it simply waits for size events for toplevel windows
163 and then iterates down the respective size events to all window. This has
164 the disadvantage that windows might get size events before the GTK widget
165 actually has the reported size. This doesn't normally pose any problem, but
166 the OpenGL drawing routines rely on correct behaviour. Therefore, I have
167 added the m_nativeSizeEvents flag, which is true only for the OpenGL canvas,
168 i.e. the wxGLCanvas will emit a size event, when (and not before) the X11
169 window that is used for OpenGL output really has that size (as reported by
174 If someone at some point of time feels the immense desire to have a look at,
175 change or attempt to optimise the Refresh() logic, this person will need an
176 intimate understanding of what "draw" and "expose" events are and what
177 they are used for, in particular when used in connection with GTK's
178 own windowless widgets. Beware.
182 Cursors, too, have been a constant source of pleasure. The main difficulty
183 is that a GdkWindow inherits a cursor if the programmer sets a new cursor
184 for the parent. To prevent this from doing too much harm, SetCursor calls
185 GTKUpdateCursor, which will recursively re-set the cursors of all child windows.
186 Also don't forget that cursors (like much else) are connected to GdkWindows,
187 not GtkWidgets and that the "window" field of a GtkWidget might very well
188 point to the GdkWindow of the parent widget (-> "window-less widget") and
189 that the two obviously have very different meanings.
192 //-----------------------------------------------------------------------------
194 //-----------------------------------------------------------------------------
196 // Don't allow event propagation during drag
197 bool g_blockEventsOnDrag
;
198 // Don't allow mouse event propagation during scroll
199 bool g_blockEventsOnScroll
;
200 extern wxCursor g_globalCursor
;
202 // mouse capture state: the window which has it and if the mouse is currently
204 static wxWindowGTK
*g_captureWindow
= NULL
;
205 static bool g_captureWindowHasMouse
= false;
207 // The window that currently has focus:
208 static wxWindowGTK
*gs_currentFocus
= NULL
;
209 // The window that is scheduled to get focus in the next event loop iteration
210 // or NULL if there's no pending focus change:
211 static wxWindowGTK
*gs_pendingFocus
= NULL
;
213 // the window that has deferred focus-out event pending, if any (see
214 // GTKAddDeferredFocusOut() for details)
215 static wxWindowGTK
*gs_deferredFocusOut
= NULL
;
217 // global variables because GTK+ DnD want to have the
218 // mouse event that caused it
219 GdkEvent
*g_lastMouseEvent
= NULL
;
220 int g_lastButtonNumber
= 0;
222 //-----------------------------------------------------------------------------
224 //-----------------------------------------------------------------------------
226 // the trace mask used for the focus debugging messages
227 #define TRACE_FOCUS wxT("focus")
229 // A handy function to run from under gdb to show information about the given
230 // GtkWidget. Right now it only shows its type, we could enhance it to show
231 // more information later but this is already pretty useful.
232 const char* wxDumpGtkWidget(GtkWidget
* w
)
235 s
.Printf("GtkWidget %p, type \"%s\"", w
, G_OBJECT_TYPE_NAME(w
));
240 //-----------------------------------------------------------------------------
241 // "expose_event"/"draw" from m_wxwindow
242 //-----------------------------------------------------------------------------
246 static gboolean
draw(GtkWidget
*, cairo_t
* cr
, wxWindow
* win
)
248 if (gtk_cairo_should_draw_window(cr
, win
->GTKGetDrawingWindow()))
249 win
->GTKSendPaintEvents(cr
);
254 static gboolean
expose_event(GtkWidget
*, GdkEventExpose
* gdk_event
, wxWindow
* win
)
256 if (gdk_event
->window
== win
->GTKGetDrawingWindow())
257 win
->GTKSendPaintEvents(gdk_event
->region
);
261 #endif // !__WXGTK3__
264 #ifndef __WXUNIVERSAL__
265 //-----------------------------------------------------------------------------
266 // "expose_event"/"draw" from m_wxwindow->parent, for drawing border
267 //-----------------------------------------------------------------------------
272 draw_border(GtkWidget
*, cairo_t
* cr
, wxWindow
* win
)
274 draw_border(GtkWidget
* widget
, GdkEventExpose
* gdk_event
, wxWindow
* win
)
278 if (!gtk_cairo_should_draw_window(cr
, gtk_widget_get_parent_window(win
->m_wxwindow
)))
280 if (gdk_event
->window
!= gtk_widget_get_parent_window(win
->m_wxwindow
))
288 gtk_widget_get_allocation(win
->m_wxwindow
, &alloc
);
289 const int x
= alloc
.x
;
290 const int y
= alloc
.y
;
291 const int w
= alloc
.width
;
292 const int h
= alloc
.height
;
294 if (w
<= 0 || h
<= 0)
297 if (win
->HasFlag(wxBORDER_SIMPLE
))
300 GtkStyleContext
* sc
= gtk_widget_get_style_context(win
->m_wxwindow
);
302 gtk_style_context_get_border_color(sc
, GTK_STATE_FLAG_NORMAL
, &c
);
303 gdk_cairo_set_source_rgba(cr
, &c
);
304 cairo_set_line_width(cr
, 1);
305 cairo_rectangle(cr
, x
+ 0.5, y
+ 0.5, w
- 1, h
- 1);
308 gdk_draw_rectangle(gdk_event
->window
,
309 gtk_widget_get_style(widget
)->black_gc
, false, x
, y
, w
- 1, h
- 1);
312 else if (win
->HasFlag(wxBORDER_RAISED
| wxBORDER_SUNKEN
| wxBORDER_THEME
))
315 //TODO: wxBORDER_RAISED/wxBORDER_SUNKEN
317 if (win
->HasFlag(wxHSCROLL
| wxVSCROLL
))
318 sc
= gtk_widget_get_style_context(wxGTKPrivate::GetTreeWidget());
320 sc
= gtk_widget_get_style_context(wxGTKPrivate::GetEntryWidget());
322 gtk_render_frame(sc
, cr
, x
, y
, w
, h
);
324 GtkShadowType shadow
= GTK_SHADOW_IN
;
325 if (win
->HasFlag(wxBORDER_RAISED
))
326 shadow
= GTK_SHADOW_OUT
;
330 if (win
->HasFlag(wxHSCROLL
| wxVSCROLL
))
332 style
= gtk_widget_get_style(wxGTKPrivate::GetTreeWidget());
337 style
= gtk_widget_get_style(wxGTKPrivate::GetEntryWidget());
341 // clip rect is required to avoid painting background
342 // over upper left (w,h) of parent window
343 GdkRectangle clipRect
= { x
, y
, w
, h
};
345 style
, gdk_event
->window
, GTK_STATE_NORMAL
,
346 shadow
, &clipRect
, widget
, detail
, x
, y
, w
, h
);
347 #endif // !__WXGTK3__
353 //-----------------------------------------------------------------------------
354 // "parent_set" from m_wxwindow
355 //-----------------------------------------------------------------------------
359 parent_set(GtkWidget
* widget
, GtkWidget
* old_parent
, wxWindow
* win
)
363 g_signal_handlers_disconnect_by_func(
364 old_parent
, (void*)draw_border
, win
);
366 GtkWidget
* parent
= gtk_widget_get_parent(widget
);
370 g_signal_connect_after(parent
, "draw", G_CALLBACK(draw_border
), win
);
372 g_signal_connect_after(parent
, "expose_event", G_CALLBACK(draw_border
), win
);
377 #endif // !__WXUNIVERSAL__
379 //-----------------------------------------------------------------------------
380 // "key_press_event" from any window
381 //-----------------------------------------------------------------------------
383 // set WXTRACE to this to see the key event codes on the console
384 #define TRACE_KEYS wxT("keyevent")
386 // translates an X key symbol to WXK_XXX value
388 // if isChar is true it means that the value returned will be used for EVT_CHAR
389 // event and then we choose the logical WXK_XXX, i.e. '/' for GDK_KP_Divide,
390 // for example, while if it is false it means that the value is going to be
391 // used for KEY_DOWN/UP events and then we translate GDK_KP_Divide to
393 static long wxTranslateKeySymToWXKey(KeySym keysym
, bool isChar
)
399 // Shift, Control and Alt don't generate the CHAR events at all
402 key_code
= isChar
? 0 : WXK_SHIFT
;
406 key_code
= isChar
? 0 : WXK_CONTROL
;
414 key_code
= isChar
? 0 : WXK_ALT
;
417 // neither do the toggle modifies
418 case GDK_Scroll_Lock
:
419 key_code
= isChar
? 0 : WXK_SCROLL
;
423 key_code
= isChar
? 0 : WXK_CAPITAL
;
427 key_code
= isChar
? 0 : WXK_NUMLOCK
;
431 // various other special keys
444 case GDK_ISO_Left_Tab
:
451 key_code
= WXK_RETURN
;
455 key_code
= WXK_CLEAR
;
459 key_code
= WXK_PAUSE
;
463 key_code
= WXK_SELECT
;
467 key_code
= WXK_PRINT
;
471 key_code
= WXK_EXECUTE
;
475 key_code
= WXK_ESCAPE
;
478 // cursor and other extended keyboard keys
480 key_code
= WXK_DELETE
;
496 key_code
= WXK_RIGHT
;
503 case GDK_Prior
: // == GDK_Page_Up
504 key_code
= WXK_PAGEUP
;
507 case GDK_Next
: // == GDK_Page_Down
508 key_code
= WXK_PAGEDOWN
;
520 key_code
= WXK_INSERT
;
535 key_code
= (isChar
? '0' : int(WXK_NUMPAD0
)) + keysym
- GDK_KP_0
;
539 key_code
= isChar
? ' ' : int(WXK_NUMPAD_SPACE
);
543 key_code
= isChar
? WXK_TAB
: WXK_NUMPAD_TAB
;
547 key_code
= isChar
? WXK_RETURN
: WXK_NUMPAD_ENTER
;
551 key_code
= isChar
? WXK_F1
: WXK_NUMPAD_F1
;
555 key_code
= isChar
? WXK_F2
: WXK_NUMPAD_F2
;
559 key_code
= isChar
? WXK_F3
: WXK_NUMPAD_F3
;
563 key_code
= isChar
? WXK_F4
: WXK_NUMPAD_F4
;
567 key_code
= isChar
? WXK_HOME
: WXK_NUMPAD_HOME
;
571 key_code
= isChar
? WXK_LEFT
: WXK_NUMPAD_LEFT
;
575 key_code
= isChar
? WXK_UP
: WXK_NUMPAD_UP
;
579 key_code
= isChar
? WXK_RIGHT
: WXK_NUMPAD_RIGHT
;
583 key_code
= isChar
? WXK_DOWN
: WXK_NUMPAD_DOWN
;
586 case GDK_KP_Prior
: // == GDK_KP_Page_Up
587 key_code
= isChar
? WXK_PAGEUP
: WXK_NUMPAD_PAGEUP
;
590 case GDK_KP_Next
: // == GDK_KP_Page_Down
591 key_code
= isChar
? WXK_PAGEDOWN
: WXK_NUMPAD_PAGEDOWN
;
595 key_code
= isChar
? WXK_END
: WXK_NUMPAD_END
;
599 key_code
= isChar
? WXK_HOME
: WXK_NUMPAD_BEGIN
;
603 key_code
= isChar
? WXK_INSERT
: WXK_NUMPAD_INSERT
;
607 key_code
= isChar
? WXK_DELETE
: WXK_NUMPAD_DELETE
;
611 key_code
= isChar
? '=' : int(WXK_NUMPAD_EQUAL
);
614 case GDK_KP_Multiply
:
615 key_code
= isChar
? '*' : int(WXK_NUMPAD_MULTIPLY
);
619 key_code
= isChar
? '+' : int(WXK_NUMPAD_ADD
);
622 case GDK_KP_Separator
:
623 // FIXME: what is this?
624 key_code
= isChar
? '.' : int(WXK_NUMPAD_SEPARATOR
);
627 case GDK_KP_Subtract
:
628 key_code
= isChar
? '-' : int(WXK_NUMPAD_SUBTRACT
);
632 key_code
= isChar
? '.' : int(WXK_NUMPAD_DECIMAL
);
636 key_code
= isChar
? '/' : int(WXK_NUMPAD_DIVIDE
);
653 key_code
= WXK_F1
+ keysym
- GDK_F1
;
663 static inline bool wxIsAsciiKeysym(KeySym ks
)
668 static void wxFillOtherKeyEventFields(wxKeyEvent
& event
,
670 GdkEventKey
*gdk_event
)
672 event
.SetTimestamp( gdk_event
->time
);
673 event
.SetId(win
->GetId());
675 event
.m_shiftDown
= (gdk_event
->state
& GDK_SHIFT_MASK
) != 0;
676 event
.m_controlDown
= (gdk_event
->state
& GDK_CONTROL_MASK
) != 0;
677 event
.m_altDown
= (gdk_event
->state
& GDK_MOD1_MASK
) != 0;
678 event
.m_metaDown
= (gdk_event
->state
& GDK_META_MASK
) != 0;
680 // At least with current Linux systems, MOD5 corresponds to AltGr key and
681 // we represent it, for consistency with Windows, which really allows to
682 // use Ctrl+Alt as a replacement for AltGr if this key is not present, as a
683 // combination of these two modifiers.
684 if ( gdk_event
->state
& GDK_MOD5_MASK
)
686 event
.m_controlDown
=
687 event
.m_altDown
= true;
690 // Normally we take the state of modifiers directly from the low level GDK
691 // event but unfortunately GDK uses a different convention from MSW for the
692 // key events corresponding to the modifier keys themselves: in it, when
693 // e.g. Shift key is pressed, GDK_SHIFT_MASK is not set while it is set
694 // when Shift is released. Under MSW the situation is exactly reversed and
695 // the modifier corresponding to the key is set when it is pressed and
696 // unset when it is released. To ensure consistent behaviour between
697 // platforms (and because it seems to make slightly more sense, although
698 // arguably both behaviours are reasonable) we follow MSW here.
700 // Final notice: we set the flags to the desired value instead of just
701 // inverting them because they are not set correctly (i.e. in the same way
702 // as for the real events generated by the user) for wxUIActionSimulator-
703 // produced events and it seems better to keep that class code the same
704 // among all platforms and fix the discrepancy here instead of adding
705 // wxGTK-specific code to wxUIActionSimulator.
706 const bool isPress
= gdk_event
->type
== GDK_KEY_PRESS
;
707 switch ( gdk_event
->keyval
)
711 event
.m_shiftDown
= isPress
;
716 event
.m_controlDown
= isPress
;
721 event
.m_altDown
= isPress
;
728 event
.m_metaDown
= isPress
;
732 event
.m_rawCode
= (wxUint32
) gdk_event
->keyval
;
733 event
.m_rawFlags
= gdk_event
->hardware_keycode
;
735 event
.SetEventObject( win
);
740 wxTranslateGTKKeyEventToWx(wxKeyEvent
& event
,
742 GdkEventKey
*gdk_event
)
744 // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string
745 // but only event->keyval which is quite useless to us, so remember
746 // the last character from GDK_KEY_PRESS and reuse it as last resort
748 // NB: should be MT-safe as we're always called from the main thread only
753 } s_lastKeyPress
= { 0, 0 };
755 KeySym keysym
= gdk_event
->keyval
;
757 wxLogTrace(TRACE_KEYS
, wxT("Key %s event: keysym = %lu"),
758 event
.GetEventType() == wxEVT_KEY_UP
? wxT("release")
760 static_cast<unsigned long>(keysym
));
762 long key_code
= wxTranslateKeySymToWXKey(keysym
, false /* !isChar */);
766 // do we have the translation or is it a plain ASCII character?
767 if ( (gdk_event
->length
== 1) || wxIsAsciiKeysym(keysym
) )
769 // we should use keysym if it is ASCII as X does some translations
770 // like "I pressed while Control is down" => "Ctrl-I" == "TAB"
771 // which we don't want here (but which we do use for OnChar())
772 if ( !wxIsAsciiKeysym(keysym
) )
774 keysym
= (KeySym
)gdk_event
->string
[0];
777 #ifdef GDK_WINDOWING_X11
778 // we want to always get the same key code when the same key is
779 // pressed regardless of the state of the modifiers, i.e. on a
780 // standard US keyboard pressing '5' or '%' ('5' key with
781 // Shift) should result in the same key code in OnKeyDown():
782 // '5' (although OnChar() will get either '5' or '%').
784 // to do it we first translate keysym to keycode (== scan code)
785 // and then back but always using the lower register
786 Display
*dpy
= (Display
*)wxGetDisplay();
787 KeyCode keycode
= XKeysymToKeycode(dpy
, keysym
);
789 wxLogTrace(TRACE_KEYS
, wxT("\t-> keycode %d"), keycode
);
791 #ifdef HAVE_X11_XKBLIB_H
792 KeySym keysymNormalized
= XkbKeycodeToKeysym(dpy
, keycode
, 0, 0);
794 KeySym keysymNormalized
= XKeycodeToKeysym(dpy
, keycode
, 0);
797 // use the normalized, i.e. lower register, keysym if we've
799 key_code
= keysymNormalized
? keysymNormalized
: keysym
;
804 // as explained above, we want to have lower register key codes
805 // normally but for the letter keys we want to have the upper ones
807 // NB: don't use XConvertCase() here, we want to do it for letters
809 key_code
= toupper(key_code
);
811 else // non ASCII key, what to do?
813 // by default, ignore it
816 // but if we have cached information from the last KEY_PRESS
817 if ( gdk_event
->type
== GDK_KEY_RELEASE
)
820 if ( keysym
== s_lastKeyPress
.keysym
)
822 key_code
= s_lastKeyPress
.keycode
;
827 if ( gdk_event
->type
== GDK_KEY_PRESS
)
829 // remember it to be reused for KEY_UP event later
830 s_lastKeyPress
.keysym
= keysym
;
831 s_lastKeyPress
.keycode
= key_code
;
835 wxLogTrace(TRACE_KEYS
, wxT("\t-> wxKeyCode %ld"), key_code
);
837 // sending unknown key events doesn't really make sense
841 event
.m_keyCode
= key_code
;
844 event
.m_uniChar
= gdk_keyval_to_unicode(key_code
);
845 if ( !event
.m_uniChar
&& event
.m_keyCode
<= WXK_DELETE
)
847 // Set Unicode key code to the ASCII equivalent for compatibility. E.g.
848 // let RETURN generate the key event with both key and Unicode key
850 event
.m_uniChar
= event
.m_keyCode
;
852 #endif // wxUSE_UNICODE
854 // now fill all the other fields
855 wxFillOtherKeyEventFields(event
, win
, gdk_event
);
864 // Send wxEVT_CHAR_HOOK event to the parent of the window and return true only
865 // if it was processed (and not skipped).
866 bool SendCharHookEvent(const wxKeyEvent
& event
, wxWindow
*win
)
868 // wxEVT_CHAR_HOOK must be sent to allow the parent windows (e.g. a dialog
869 // which typically closes when Esc key is pressed in any of its controls)
870 // to handle key events in all of its children unless the mouse is captured
871 // in which case we consider that the keyboard should be "captured" too.
872 if ( !g_captureWindow
)
874 wxKeyEvent
eventCharHook(wxEVT_CHAR_HOOK
, event
);
875 if ( win
->HandleWindowEvent(eventCharHook
)
876 && !event
.IsNextEventAllowed() )
883 // Adjust wxEVT_CHAR event key code fields. This function takes care of two
885 // (a) Ctrl-letter key presses generate key codes in range 1..26
886 // (b) Unicode key codes are same as key codes for the codes in 1..255 range
887 void AdjustCharEventKeyCodes(wxKeyEvent
& event
)
889 const int code
= event
.m_keyCode
;
891 // Check for (a) above.
892 if ( event
.ControlDown() )
894 // We intentionally don't use isupper/lower() here, we really need
895 // ASCII letters only as it doesn't make sense to translate any other
896 // ones into this range which has only 26 slots.
897 if ( code
>= 'a' && code
<= 'z' )
898 event
.m_keyCode
= code
- 'a' + 1;
899 else if ( code
>= 'A' && code
<= 'Z' )
900 event
.m_keyCode
= code
- 'A' + 1;
903 // Adjust the Unicode equivalent in the same way too.
904 if ( event
.m_keyCode
!= code
)
905 event
.m_uniChar
= event
.m_keyCode
;
906 #endif // wxUSE_UNICODE
910 // Check for (b) from above.
912 // FIXME: Should we do it for key codes up to 255?
913 if ( !event
.m_uniChar
&& code
< WXK_DELETE
)
914 event
.m_uniChar
= code
;
915 #endif // wxUSE_UNICODE
918 } // anonymous namespace
920 // If a widget does not handle a key or mouse event, GTK+ sends it up the
921 // parent chain until it is handled. These events are not supposed to propagate
922 // in wxWidgets, so this code avoids handling them in any parent wxWindow,
923 // while still allowing the event to propagate so things like native keyboard
924 // navigation will work.
925 #define wxPROCESS_EVENT_ONCE(EventType, event) \
926 static EventType eventPrev; \
927 if (memcmp(&eventPrev, event, sizeof(EventType)) == 0) \
933 gtk_window_key_press_callback( GtkWidget
*WXUNUSED(widget
),
934 GdkEventKey
*gdk_event
,
937 if (g_blockEventsOnDrag
)
940 wxPROCESS_EVENT_ONCE(GdkEventKey
, gdk_event
);
942 wxKeyEvent
event( wxEVT_KEY_DOWN
);
944 bool return_after_IM
= false;
946 if( wxTranslateGTKKeyEventToWx(event
, win
, gdk_event
) )
948 // Send the CHAR_HOOK event first
949 if ( SendCharHookEvent(event
, win
) )
951 // Don't do anything at all with this event any more.
955 // Next check for accelerators.
957 wxWindowGTK
*ancestor
= win
;
960 int command
= ancestor
->GetAcceleratorTable()->GetCommand( event
);
963 wxCommandEvent
menu_event( wxEVT_MENU
, command
);
964 ret
= ancestor
->HandleWindowEvent( menu_event
);
968 // if the accelerator wasn't handled as menu event, try
969 // it as button click (for compatibility with other
971 wxCommandEvent
button_event( wxEVT_BUTTON
, command
);
972 ret
= ancestor
->HandleWindowEvent( button_event
);
977 if (ancestor
->IsTopLevel())
979 ancestor
= ancestor
->GetParent();
981 #endif // wxUSE_ACCEL
983 // If not an accelerator, then emit KEY_DOWN event
985 ret
= win
->HandleWindowEvent( event
);
989 // Return after IM processing as we cannot do
990 // anything with it anyhow.
991 return_after_IM
= true;
996 // Indicate that IM handling is in process by setting this pointer
997 // (which will remain valid for all the code called during IM key
999 win
->m_imKeyEvent
= gdk_event
;
1001 // We should let GTK+ IM filter key event first. According to GTK+ 2.0 API
1002 // docs, if IM filter returns true, no further processing should be done.
1003 // we should send the key_down event anyway.
1004 const int intercepted_by_IM
= win
->GTKIMFilterKeypress(gdk_event
);
1006 win
->m_imKeyEvent
= NULL
;
1008 if ( intercepted_by_IM
)
1010 wxLogTrace(TRACE_KEYS
, wxT("Key event intercepted by IM"));
1015 if (return_after_IM
)
1018 // Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
1019 // will only be sent if it is not in an accelerator table.
1022 KeySym keysym
= gdk_event
->keyval
;
1023 // Find key code for EVT_CHAR and EVT_CHAR_HOOK events
1024 long key_code
= wxTranslateKeySymToWXKey(keysym
, true /* isChar */);
1027 if ( wxIsAsciiKeysym(keysym
) )
1030 key_code
= (unsigned char)keysym
;
1032 // gdk_event->string is actually deprecated
1033 else if ( gdk_event
->length
== 1 )
1035 key_code
= (unsigned char)gdk_event
->string
[0];
1041 wxKeyEvent
eventChar(wxEVT_CHAR
, event
);
1043 wxLogTrace(TRACE_KEYS
, wxT("Char event: %ld"), key_code
);
1045 eventChar
.m_keyCode
= key_code
;
1047 eventChar
.m_uniChar
= gdk_keyval_to_unicode(key_code
);
1048 #endif // wxUSE_UNICODE
1050 AdjustCharEventKeyCodes(eventChar
);
1052 ret
= win
->HandleWindowEvent(eventChar
);
1060 int wxWindowGTK::GTKIMFilterKeypress(GdkEventKey
* event
) const
1062 return m_imContext
? gtk_im_context_filter_keypress(m_imContext
, event
)
1068 gtk_wxwindow_commit_cb (GtkIMContext
* WXUNUSED(context
),
1072 // Ignore the return value here, it doesn't matter for the "commit" signal.
1073 window
->GTKDoInsertTextFromIM(str
);
1077 bool wxWindowGTK::GTKDoInsertTextFromIM(const char* str
)
1079 wxKeyEvent
event( wxEVT_CHAR
);
1081 // take modifiers, cursor position, timestamp etc. from the last
1082 // key_press_event that was fed into Input Method:
1085 wxFillOtherKeyEventFields(event
, this, m_imKeyEvent
);
1089 event
.SetEventObject(this);
1092 const wxString
data(wxGTK_CONV_BACK_SYS(str
));
1096 bool processed
= false;
1097 for( wxString::const_iterator pstr
= data
.begin(); pstr
!= data
.end(); ++pstr
)
1100 event
.m_uniChar
= *pstr
;
1101 // Backward compatible for ISO-8859-1
1102 event
.m_keyCode
= *pstr
< 256 ? event
.m_uniChar
: 0;
1103 wxLogTrace(TRACE_KEYS
, wxT("IM sent character '%c'"), event
.m_uniChar
);
1105 event
.m_keyCode
= (char)*pstr
;
1106 #endif // wxUSE_UNICODE
1108 AdjustCharEventKeyCodes(event
);
1110 if ( HandleWindowEvent(event
) )
1117 bool wxWindowGTK::GTKOnInsertText(const char* text
)
1119 if ( !m_imKeyEvent
)
1121 // We're not inside IM key handling at all.
1125 return GTKDoInsertTextFromIM(text
);
1129 //-----------------------------------------------------------------------------
1130 // "key_release_event" from any window
1131 //-----------------------------------------------------------------------------
1135 gtk_window_key_release_callback( GtkWidget
* WXUNUSED(widget
),
1136 GdkEventKey
*gdk_event
,
1139 if (g_blockEventsOnDrag
)
1142 wxPROCESS_EVENT_ONCE(GdkEventKey
, gdk_event
);
1144 wxKeyEvent
event( wxEVT_KEY_UP
);
1145 if ( !wxTranslateGTKKeyEventToWx(event
, win
, gdk_event
) )
1147 // unknown key pressed, ignore (the event would be useless anyhow)
1151 return win
->GTKProcessEvent(event
);
1155 // ============================================================================
1157 // ============================================================================
1159 // ----------------------------------------------------------------------------
1160 // mouse event processing helpers
1161 // ----------------------------------------------------------------------------
1163 static void AdjustEventButtonState(wxMouseEvent
& event
)
1165 // GDK reports the old state of the button for a button press event, but
1166 // for compatibility with MSW and common sense we want m_leftDown be TRUE
1167 // for a LEFT_DOWN event, not FALSE, so we will invert
1168 // left/right/middleDown for the corresponding click events
1170 if ((event
.GetEventType() == wxEVT_LEFT_DOWN
) ||
1171 (event
.GetEventType() == wxEVT_LEFT_DCLICK
) ||
1172 (event
.GetEventType() == wxEVT_LEFT_UP
))
1174 event
.m_leftDown
= !event
.m_leftDown
;
1178 if ((event
.GetEventType() == wxEVT_MIDDLE_DOWN
) ||
1179 (event
.GetEventType() == wxEVT_MIDDLE_DCLICK
) ||
1180 (event
.GetEventType() == wxEVT_MIDDLE_UP
))
1182 event
.m_middleDown
= !event
.m_middleDown
;
1186 if ((event
.GetEventType() == wxEVT_RIGHT_DOWN
) ||
1187 (event
.GetEventType() == wxEVT_RIGHT_DCLICK
) ||
1188 (event
.GetEventType() == wxEVT_RIGHT_UP
))
1190 event
.m_rightDown
= !event
.m_rightDown
;
1194 if ((event
.GetEventType() == wxEVT_AUX1_DOWN
) ||
1195 (event
.GetEventType() == wxEVT_AUX1_DCLICK
))
1197 event
.m_aux1Down
= true;
1201 if ((event
.GetEventType() == wxEVT_AUX2_DOWN
) ||
1202 (event
.GetEventType() == wxEVT_AUX2_DCLICK
))
1204 event
.m_aux2Down
= true;
1209 // find the window to send the mouse event to
1211 wxWindowGTK
*FindWindowForMouseEvent(wxWindowGTK
*win
, wxCoord
& x
, wxCoord
& y
)
1216 if (win
->m_wxwindow
)
1218 wxPizza
* pizza
= WX_PIZZA(win
->m_wxwindow
);
1219 xx
+= pizza
->m_scroll_x
;
1220 yy
+= pizza
->m_scroll_y
;
1223 wxWindowList::compatibility_iterator node
= win
->GetChildren().GetFirst();
1226 wxWindow
* child
= static_cast<wxWindow
*>(node
->GetData());
1228 node
= node
->GetNext();
1229 if (!child
->IsShown())
1232 if (child
->GTKIsTransparentForMouse())
1234 // wxStaticBox is transparent in the box itself
1235 int xx1
= child
->m_x
;
1236 int yy1
= child
->m_y
;
1237 int xx2
= child
->m_x
+ child
->m_width
;
1238 int yy2
= child
->m_y
+ child
->m_height
;
1241 if (((xx
>= xx1
) && (xx
<= xx1
+10) && (yy
>= yy1
) && (yy
<= yy2
)) ||
1243 ((xx
>= xx2
-10) && (xx
<= xx2
) && (yy
>= yy1
) && (yy
<= yy2
)) ||
1245 ((xx
>= xx1
) && (xx
<= xx2
) && (yy
>= yy1
) && (yy
<= yy1
+10)) ||
1247 ((xx
>= xx1
) && (xx
<= xx2
) && (yy
>= yy2
-1) && (yy
<= yy2
)))
1258 if ((child
->m_wxwindow
== NULL
) &&
1259 win
->IsClientAreaChild(child
) &&
1260 (child
->m_x
<= xx
) &&
1261 (child
->m_y
<= yy
) &&
1262 (child
->m_x
+child
->m_width
>= xx
) &&
1263 (child
->m_y
+child
->m_height
>= yy
))
1276 // ----------------------------------------------------------------------------
1277 // common event handlers helpers
1278 // ----------------------------------------------------------------------------
1280 bool wxWindowGTK::GTKProcessEvent(wxEvent
& event
) const
1282 // nothing special at this level
1283 return HandleWindowEvent(event
);
1286 bool wxWindowGTK::GTKShouldIgnoreEvent() const
1288 return g_blockEventsOnDrag
;
1291 int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny
*event
) const
1293 if (g_blockEventsOnDrag
)
1295 if (g_blockEventsOnScroll
)
1298 if (!GTKIsOwnWindow(event
->window
))
1304 // overloads for all GDK event types we use here: we need to have this as
1305 // GdkEventXXX can't be implicitly cast to GdkEventAny even if it, in fact,
1306 // derives from it in the sense that the structs have the same layout
1307 #define wxDEFINE_COMMON_PROLOGUE_OVERLOAD(T) \
1308 static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win) \
1310 return win->GTKCallbackCommonPrologue((GdkEventAny *)event); \
1313 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventButton
)
1314 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion
)
1315 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing
)
1317 #undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD
1319 #define wxCOMMON_CALLBACK_PROLOGUE(event, win) \
1320 const int rc = wxGtkCallbackCommonPrologue(event, win); \
1324 // all event handlers must have C linkage as they're called from GTK+ C code
1328 //-----------------------------------------------------------------------------
1329 // "button_press_event"
1330 //-----------------------------------------------------------------------------
1333 gtk_window_button_press_callback( GtkWidget
* WXUNUSED_IN_GTK3(widget
),
1334 GdkEventButton
*gdk_event
,
1337 wxPROCESS_EVENT_ONCE(GdkEventButton
, gdk_event
);
1339 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1341 g_lastButtonNumber
= gdk_event
->button
;
1343 wxEventType event_type
;
1346 switch (gdk_event
->button
)
1349 down
= wxEVT_LEFT_DOWN
;
1350 dclick
= wxEVT_LEFT_DCLICK
;
1353 down
= wxEVT_MIDDLE_DOWN
;
1354 dclick
= wxEVT_MIDDLE_DCLICK
;
1357 down
= wxEVT_RIGHT_DOWN
;
1358 dclick
= wxEVT_RIGHT_DCLICK
;
1361 down
= wxEVT_AUX1_DOWN
;
1362 dclick
= wxEVT_AUX1_DCLICK
;
1365 down
= wxEVT_AUX2_DOWN
;
1366 dclick
= wxEVT_AUX2_DCLICK
;
1371 switch (gdk_event
->type
)
1373 case GDK_BUTTON_PRESS
:
1375 // GDK sends surplus button down events
1376 // before a double click event. We
1377 // need to filter these out.
1378 if (win
->m_wxwindow
)
1380 GdkEvent
* peek_event
= gdk_event_peek();
1383 const GdkEventType peek_event_type
= peek_event
->type
;
1384 gdk_event_free(peek_event
);
1385 if (peek_event_type
== GDK_2BUTTON_PRESS
||
1386 peek_event_type
== GDK_3BUTTON_PRESS
)
1393 case GDK_2BUTTON_PRESS
:
1394 event_type
= dclick
;
1396 if (gdk_event
->button
>= 1 && gdk_event
->button
<= 3)
1398 // Reset GDK internal timestamp variables in order to disable GDK
1399 // triple click events. GDK will then next time believe no button has
1400 // been clicked just before, and send a normal button click event.
1401 GdkDisplay
* display
= gtk_widget_get_display(widget
);
1402 display
->button_click_time
[1] = 0;
1403 display
->button_click_time
[0] = 0;
1405 #endif // !__WXGTK3__
1407 // we shouldn't get triple clicks at all for GTK2 because we
1408 // suppress them artificially using the code above but we still
1409 // should map them to something for GTK3 and not just ignore them
1410 // as this would lose clicks
1411 case GDK_3BUTTON_PRESS
:
1418 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1420 wxMouseEvent
event( event_type
);
1421 InitMouseEvent( win
, event
, gdk_event
);
1423 AdjustEventButtonState(event
);
1425 // find the correct window to send the event to: it may be a different one
1426 // from the one which got it at GTK+ level because some controls don't have
1427 // their own X window and thus cannot get any events.
1428 if ( !g_captureWindow
)
1429 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1431 // reset the event object and id in case win changed.
1432 event
.SetEventObject( win
);
1433 event
.SetId( win
->GetId() );
1435 bool ret
= win
->GTKProcessEvent( event
);
1436 g_lastMouseEvent
= NULL
;
1440 if ((event_type
== wxEVT_LEFT_DOWN
) && !win
->IsOfStandardClass() &&
1441 (gs_currentFocus
!= win
) /* && win->IsFocusable() */)
1446 if (event_type
== wxEVT_RIGHT_DOWN
)
1448 // generate a "context menu" event: this is similar to right mouse
1449 // click under many GUIs except that it is generated differently
1450 // (right up under MSW, ctrl-click under Mac, right down here) and
1452 // (a) it's a command event and so is propagated to the parent
1453 // (b) under some ports it can be generated from kbd too
1454 // (c) it uses screen coords (because of (a))
1455 wxContextMenuEvent
evtCtx(
1458 win
->ClientToScreen(event
.GetPosition()));
1459 evtCtx
.SetEventObject(win
);
1460 return win
->GTKProcessEvent(evtCtx
);
1466 //-----------------------------------------------------------------------------
1467 // "button_release_event"
1468 //-----------------------------------------------------------------------------
1471 gtk_window_button_release_callback( GtkWidget
*WXUNUSED(widget
),
1472 GdkEventButton
*gdk_event
,
1475 wxPROCESS_EVENT_ONCE(GdkEventButton
, gdk_event
);
1477 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1479 g_lastButtonNumber
= 0;
1481 wxEventType event_type
= wxEVT_NULL
;
1483 switch (gdk_event
->button
)
1486 event_type
= wxEVT_LEFT_UP
;
1490 event_type
= wxEVT_MIDDLE_UP
;
1494 event_type
= wxEVT_RIGHT_UP
;
1498 event_type
= wxEVT_AUX1_UP
;
1502 event_type
= wxEVT_AUX2_UP
;
1506 // unknown button, don't process
1510 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1512 wxMouseEvent
event( event_type
);
1513 InitMouseEvent( win
, event
, gdk_event
);
1515 AdjustEventButtonState(event
);
1517 if ( !g_captureWindow
)
1518 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1520 // reset the event object and id in case win changed.
1521 event
.SetEventObject( win
);
1522 event
.SetId( win
->GetId() );
1524 bool ret
= win
->GTKProcessEvent(event
);
1526 g_lastMouseEvent
= NULL
;
1531 //-----------------------------------------------------------------------------
1532 // "motion_notify_event"
1533 //-----------------------------------------------------------------------------
1536 gtk_window_motion_notify_callback( GtkWidget
* WXUNUSED(widget
),
1537 GdkEventMotion
*gdk_event
,
1540 wxPROCESS_EVENT_ONCE(GdkEventMotion
, gdk_event
);
1542 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1544 if (gdk_event
->is_hint
)
1549 gdk_window_get_device_position(gdk_event
->window
, gdk_event
->device
, &x
, &y
, NULL
);
1551 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, NULL
);
1557 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1559 wxMouseEvent
event( wxEVT_MOTION
);
1560 InitMouseEvent(win
, event
, gdk_event
);
1562 if ( g_captureWindow
)
1564 // synthesise a mouse enter or leave event if needed
1565 GdkWindow
* winUnderMouse
=
1567 gdk_device_get_window_at_position(gdk_event
->device
, NULL
, NULL
);
1569 gdk_window_at_pointer(NULL
, NULL
);
1571 // This seems to be necessary and actually been added to
1572 // GDK itself in version 2.0.X
1575 bool hasMouse
= winUnderMouse
== gdk_event
->window
;
1576 if ( hasMouse
!= g_captureWindowHasMouse
)
1578 // the mouse changed window
1579 g_captureWindowHasMouse
= hasMouse
;
1581 wxMouseEvent
eventM(g_captureWindowHasMouse
? wxEVT_ENTER_WINDOW
1582 : wxEVT_LEAVE_WINDOW
);
1583 InitMouseEvent(win
, eventM
, gdk_event
);
1584 eventM
.SetEventObject(win
);
1585 win
->GTKProcessEvent(eventM
);
1590 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1592 // reset the event object and id in case win changed.
1593 event
.SetEventObject( win
);
1594 event
.SetId( win
->GetId() );
1597 if ( !g_captureWindow
)
1599 wxSetCursorEvent
cevent( event
.m_x
, event
.m_y
);
1600 if (win
->GTKProcessEvent( cevent
))
1602 win
->SetCursor( cevent
.GetCursor() );
1606 bool ret
= win
->GTKProcessEvent(event
);
1608 g_lastMouseEvent
= NULL
;
1613 //-----------------------------------------------------------------------------
1614 // "scroll_event" (mouse wheel event)
1615 //-----------------------------------------------------------------------------
1617 static void AdjustRangeValue(GtkRange
* range
, double step
)
1619 if (range
&& gtk_widget_get_visible(GTK_WIDGET(range
)))
1621 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
1622 double value
= gtk_adjustment_get_value(adj
);
1623 value
+= step
* gtk_adjustment_get_step_increment(adj
);
1624 gtk_range_set_value(range
, value
);
1629 scroll_event(GtkWidget
* widget
, GdkEventScroll
* gdk_event
, wxWindow
* win
)
1631 wxMouseEvent
event(wxEVT_MOUSEWHEEL
);
1632 InitMouseEvent(win
, event
, gdk_event
);
1634 event
.m_wheelDelta
= 120;
1635 event
.m_linesPerAction
= 3;
1636 event
.m_columnsPerAction
= 3;
1638 GtkRange
* range_h
= win
->m_scrollBar
[wxWindow::ScrollDir_Horz
];
1639 GtkRange
* range_v
= win
->m_scrollBar
[wxWindow::ScrollDir_Vert
];
1640 const bool is_range_h
= (void*)widget
== range_h
;
1641 const bool is_range_v
= (void*)widget
== range_v
;
1642 GdkScrollDirection direction
= gdk_event
->direction
;
1647 direction
= GDK_SCROLL_LEFT
;
1649 case GDK_SCROLL_DOWN
:
1651 direction
= GDK_SCROLL_RIGHT
;
1653 case GDK_SCROLL_LEFT
:
1655 direction
= GDK_SCROLL_UP
;
1657 case GDK_SCROLL_RIGHT
:
1659 direction
= GDK_SCROLL_DOWN
;
1663 #if GTK_CHECK_VERSION(3,4,0)
1664 case GDK_SCROLL_SMOOTH
:
1665 double delta_x
= gdk_event
->delta_x
;
1666 double delta_y
= gdk_event
->delta_y
;
1675 else if (delta_y
== 0)
1685 event
.m_wheelAxis
= wxMOUSE_WHEEL_HORIZONTAL
;
1686 event
.m_wheelRotation
= int(event
.m_wheelDelta
* delta_x
);
1687 if (!win
->GTKProcessEvent(event
))
1688 AdjustRangeValue(range_h
, event
.m_columnsPerAction
* delta_x
);
1692 event
.m_wheelAxis
= wxMOUSE_WHEEL_VERTICAL
;
1693 event
.m_wheelRotation
= int(event
.m_wheelDelta
* -delta_y
);
1694 if (!win
->GTKProcessEvent(event
))
1695 AdjustRangeValue(range_v
, event
.m_linesPerAction
* delta_y
);
1698 #endif // GTK_CHECK_VERSION(3,4,0)
1705 case GDK_SCROLL_DOWN
:
1707 event
.m_wheelAxis
= wxMOUSE_WHEEL_VERTICAL
;
1708 step
= event
.m_linesPerAction
;
1710 case GDK_SCROLL_LEFT
:
1711 case GDK_SCROLL_RIGHT
:
1713 event
.m_wheelAxis
= wxMOUSE_WHEEL_HORIZONTAL
;
1714 step
= event
.m_columnsPerAction
;
1720 event
.m_wheelRotation
= event
.m_wheelDelta
;
1721 if (direction
== GDK_SCROLL_DOWN
|| direction
== GDK_SCROLL_LEFT
)
1722 event
.m_wheelRotation
= -event
.m_wheelRotation
;
1724 if (!win
->GTKProcessEvent(event
))
1726 if (direction
== GDK_SCROLL_UP
|| direction
== GDK_SCROLL_LEFT
)
1728 AdjustRangeValue(range
, step
);
1734 //-----------------------------------------------------------------------------
1736 //-----------------------------------------------------------------------------
1738 static gboolean
wxgtk_window_popup_menu_callback(GtkWidget
*, wxWindowGTK
* win
)
1740 wxContextMenuEvent
event(wxEVT_CONTEXT_MENU
, win
->GetId(), wxPoint(-1, -1));
1741 event
.SetEventObject(win
);
1742 return win
->GTKProcessEvent(event
);
1745 //-----------------------------------------------------------------------------
1747 //-----------------------------------------------------------------------------
1750 gtk_window_focus_in_callback( GtkWidget
* WXUNUSED(widget
),
1751 GdkEventFocus
*WXUNUSED(event
),
1754 return win
->GTKHandleFocusIn();
1757 //-----------------------------------------------------------------------------
1758 // "focus_out_event"
1759 //-----------------------------------------------------------------------------
1762 gtk_window_focus_out_callback( GtkWidget
* WXUNUSED(widget
),
1763 GdkEventFocus
* WXUNUSED(gdk_event
),
1766 return win
->GTKHandleFocusOut();
1769 //-----------------------------------------------------------------------------
1771 //-----------------------------------------------------------------------------
1774 wx_window_focus_callback(GtkWidget
*widget
,
1775 GtkDirectionType
WXUNUSED(direction
),
1778 // the default handler for focus signal in GtkScrolledWindow sets
1779 // focus to the window itself even if it doesn't accept focus, i.e. has no
1780 // GTK_CAN_FOCUS in its style -- work around this by forcibly preventing
1781 // the signal from reaching gtk_scrolled_window_focus() if we don't have
1782 // any children which might accept focus (we know we don't accept the focus
1783 // ourselves as this signal is only connected in this case)
1784 if ( win
->GetChildren().empty() )
1785 g_signal_stop_emission_by_name(widget
, "focus");
1787 // we didn't change the focus
1791 //-----------------------------------------------------------------------------
1792 // "enter_notify_event"
1793 //-----------------------------------------------------------------------------
1796 gtk_window_enter_callback( GtkWidget
*,
1797 GdkEventCrossing
*gdk_event
,
1800 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1802 // Event was emitted after a grab
1803 if (gdk_event
->mode
!= GDK_CROSSING_NORMAL
) return FALSE
;
1805 wxMouseEvent
event( wxEVT_ENTER_WINDOW
);
1806 InitMouseEvent(win
, event
, gdk_event
);
1808 if ( !g_captureWindow
)
1810 wxSetCursorEvent
cevent( event
.m_x
, event
.m_y
);
1811 if (win
->GTKProcessEvent( cevent
))
1813 win
->SetCursor( cevent
.GetCursor() );
1817 return win
->GTKProcessEvent(event
);
1820 //-----------------------------------------------------------------------------
1821 // "leave_notify_event"
1822 //-----------------------------------------------------------------------------
1825 gtk_window_leave_callback( GtkWidget
*,
1826 GdkEventCrossing
*gdk_event
,
1829 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1831 // Event was emitted after an ungrab
1832 if (gdk_event
->mode
!= GDK_CROSSING_NORMAL
) return FALSE
;
1834 wxMouseEvent
event( wxEVT_LEAVE_WINDOW
);
1835 InitMouseEvent(win
, event
, gdk_event
);
1837 return win
->GTKProcessEvent(event
);
1840 //-----------------------------------------------------------------------------
1841 // "value_changed" from scrollbar
1842 //-----------------------------------------------------------------------------
1845 gtk_scrollbar_value_changed(GtkRange
* range
, wxWindow
* win
)
1847 wxEventType eventType
= win
->GTKGetScrollEventType(range
);
1848 if (eventType
!= wxEVT_NULL
)
1850 // Convert scroll event type to scrollwin event type
1851 eventType
+= wxEVT_SCROLLWIN_TOP
- wxEVT_SCROLL_TOP
;
1853 // find the scrollbar which generated the event
1854 wxWindowGTK::ScrollDir dir
= win
->ScrollDirFromRange(range
);
1856 // generate the corresponding wx event
1857 const int orient
= wxWindow::OrientFromScrollDir(dir
);
1858 wxScrollWinEvent
event(eventType
, win
->GetScrollPos(orient
), orient
);
1859 event
.SetEventObject(win
);
1861 win
->GTKProcessEvent(event
);
1865 //-----------------------------------------------------------------------------
1866 // "button_press_event" from scrollbar
1867 //-----------------------------------------------------------------------------
1870 gtk_scrollbar_button_press_event(GtkRange
*, GdkEventButton
*, wxWindow
* win
)
1872 g_blockEventsOnScroll
= true;
1873 win
->m_mouseButtonDown
= true;
1878 //-----------------------------------------------------------------------------
1879 // "event_after" from scrollbar
1880 //-----------------------------------------------------------------------------
1883 gtk_scrollbar_event_after(GtkRange
* range
, GdkEvent
* event
, wxWindow
* win
)
1885 if (event
->type
== GDK_BUTTON_RELEASE
)
1887 g_signal_handlers_block_by_func(range
, (void*)gtk_scrollbar_event_after
, win
);
1889 const int orient
= wxWindow::OrientFromScrollDir(
1890 win
->ScrollDirFromRange(range
));
1891 wxScrollWinEvent
evt(wxEVT_SCROLLWIN_THUMBRELEASE
,
1892 win
->GetScrollPos(orient
), orient
);
1893 evt
.SetEventObject(win
);
1894 win
->GTKProcessEvent(evt
);
1898 //-----------------------------------------------------------------------------
1899 // "button_release_event" from scrollbar
1900 //-----------------------------------------------------------------------------
1903 gtk_scrollbar_button_release_event(GtkRange
* range
, GdkEventButton
*, wxWindow
* win
)
1905 g_blockEventsOnScroll
= false;
1906 win
->m_mouseButtonDown
= false;
1907 // If thumb tracking
1908 if (win
->m_isScrolling
)
1910 win
->m_isScrolling
= false;
1911 // Hook up handler to send thumb release event after this emission is finished.
1912 // To allow setting scroll position from event handler, sending event must
1913 // be deferred until after the GtkRange handler for this signal has run
1914 g_signal_handlers_unblock_by_func(range
, (void*)gtk_scrollbar_event_after
, win
);
1920 //-----------------------------------------------------------------------------
1921 // "realize" from m_widget
1922 //-----------------------------------------------------------------------------
1925 gtk_window_realized_callback(GtkWidget
* WXUNUSED(widget
), wxWindowGTK
* win
)
1927 win
->GTKHandleRealized();
1930 //-----------------------------------------------------------------------------
1931 // "size_allocate" from m_wxwindow or m_widget
1932 //-----------------------------------------------------------------------------
1935 size_allocate(GtkWidget
*, GtkAllocation
* alloc
, wxWindow
* win
)
1937 int w
= alloc
->width
;
1938 int h
= alloc
->height
;
1939 if (win
->m_wxwindow
)
1942 WX_PIZZA(win
->m_wxwindow
)->get_border(border
);
1943 w
-= border
.left
+ border
.right
;
1944 h
-= border
.top
+ border
.bottom
;
1949 gtk_widget_get_allocation(win
->m_widget
, &a
);
1950 // update position for widgets in native containers, such as wxToolBar
1951 if (!WX_IS_PIZZA(gtk_widget_get_parent(win
->m_widget
)))
1956 win
->m_useCachedClientSize
= true;
1957 if (win
->m_clientWidth
!= w
|| win
->m_clientHeight
!= h
)
1959 win
->m_clientWidth
= w
;
1960 win
->m_clientHeight
= h
;
1961 // this callback can be connected to m_wxwindow,
1962 // so always get size from m_widget->allocation
1963 win
->m_width
= a
.width
;
1964 win
->m_height
= a
.height
;
1965 if (!win
->m_nativeSizeEvent
)
1967 wxSizeEvent
event(win
->GetSize(), win
->GetId());
1968 event
.SetEventObject(win
);
1969 win
->GTKProcessEvent(event
);
1974 //-----------------------------------------------------------------------------
1976 //-----------------------------------------------------------------------------
1978 #if GTK_CHECK_VERSION(2, 8, 0)
1980 gtk_window_grab_broken( GtkWidget
*,
1981 GdkEventGrabBroken
*event
,
1984 // Mouse capture has been lost involuntarily, notify the application
1985 if(!event
->keyboard
&& wxWindow::GetCapture() == win
)
1987 wxMouseCaptureLostEvent
evt( win
->GetId() );
1988 evt
.SetEventObject( win
);
1989 win
->HandleWindowEvent( evt
);
1995 //-----------------------------------------------------------------------------
1996 // "style_set"/"style_updated"
1997 //-----------------------------------------------------------------------------
2000 static void style_updated(GtkWidget
*, wxWindow
* win
)
2002 static void style_updated(GtkWidget
*, GtkStyle
*, wxWindow
* win
)
2005 wxSysColourChangedEvent event
;
2006 event
.SetEventObject(win
);
2007 win
->GTKProcessEvent(event
);
2010 //-----------------------------------------------------------------------------
2012 //-----------------------------------------------------------------------------
2014 static void unrealize(GtkWidget
*, wxWindow
* win
)
2016 win
->GTKHandleUnrealize();
2021 void wxWindowGTK::GTKHandleRealized()
2026 GdkWindow
* const window
= GTKGetDrawingWindow();
2030 gtk_im_context_set_client_window
2034 : gtk_widget_get_window(m_widget
)
2038 // Use composited window if background is transparent, if supported.
2039 if (m_backgroundStyle
== wxBG_STYLE_TRANSPARENT
)
2041 #if wxGTK_HAS_COMPOSITING_SUPPORT
2042 if (IsTransparentBackgroundSupported())
2045 gdk_window_set_composited(window
, true);
2048 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
2050 // We revert to erase mode if transparency is not supported
2051 m_backgroundStyle
= wxBG_STYLE_ERASE
;
2057 m_backgroundStyle
== wxBG_STYLE_PAINT
||
2058 m_backgroundStyle
== wxBG_STYLE_TRANSPARENT
))
2060 gdk_window_set_back_pixmap(window
, NULL
, false);
2064 wxWindowCreateEvent
event(static_cast<wxWindow
*>(this));
2065 event
.SetEventObject( this );
2066 GTKProcessEvent( event
);
2068 GTKUpdateCursor(true, false);
2070 if (m_wxwindow
&& IsTopLevel())
2072 // attaching to style changed signal after realization avoids initial
2073 // changes we don't care about
2074 const gchar
*detailed_signal
=
2080 g_signal_connect(m_wxwindow
,
2082 G_CALLBACK(style_updated
), this);
2086 void wxWindowGTK::GTKHandleUnrealize()
2088 // unrealizing a frozen window seems to have some lingering effect
2089 // preventing updates to the affected area
2096 gtk_im_context_set_client_window(m_imContext
, NULL
);
2100 g_signal_handlers_disconnect_by_func(
2101 m_wxwindow
, (void*)style_updated
, this);
2106 // ----------------------------------------------------------------------------
2107 // this wxWindowBase function is implemented here (in platform-specific file)
2108 // because it is static and so couldn't be made virtual
2109 // ----------------------------------------------------------------------------
2111 wxWindow
*wxWindowBase::DoFindFocus()
2114 // For compatibility with wxMSW, pretend that showing a popup menu doesn't
2115 // change the focus and that it remains on the window showing it, even
2116 // though the real focus does change in GTK.
2117 extern wxMenu
*wxCurrentPopupMenu
;
2118 if ( wxCurrentPopupMenu
)
2119 return wxCurrentPopupMenu
->GetInvokingWindow();
2120 #endif // wxUSE_MENUS
2122 wxWindowGTK
*focus
= gs_pendingFocus
? gs_pendingFocus
: gs_currentFocus
;
2123 // the cast is necessary when we compile in wxUniversal mode
2124 return static_cast<wxWindow
*>(focus
);
2127 void wxWindowGTK::AddChildGTK(wxWindowGTK
* child
)
2129 wxASSERT_MSG(m_wxwindow
, "Cannot add a child to a window without a client area");
2131 // the window might have been scrolled already, we
2132 // have to adapt the position
2133 wxPizza
* pizza
= WX_PIZZA(m_wxwindow
);
2134 child
->m_x
+= pizza
->m_scroll_x
;
2135 child
->m_y
+= pizza
->m_scroll_y
;
2137 pizza
->put(child
->m_widget
,
2138 child
->m_x
, child
->m_y
, child
->m_width
, child
->m_height
);
2141 //-----------------------------------------------------------------------------
2143 //-----------------------------------------------------------------------------
2145 wxWindow
*wxGetActiveWindow()
2147 return wxWindow::FindFocus();
2151 // Under Unix this is implemented using X11 functions in utilsx11.cpp but we
2152 // need to have this function under Windows too, so provide at least a stub.
2153 #ifndef GDK_WINDOWING_X11
2154 bool wxGetKeyState(wxKeyCode
WXUNUSED(key
))
2156 wxFAIL_MSG(wxS("Not implemented under Windows"));
2159 #endif // __WINDOWS__
2161 static GdkDisplay
* GetDisplay()
2163 wxWindow
* tlw
= NULL
;
2164 if (!wxTopLevelWindows
.empty())
2165 tlw
= wxTopLevelWindows
.front();
2166 GdkDisplay
* display
;
2167 if (tlw
&& tlw
->m_widget
)
2168 display
= gtk_widget_get_display(tlw
->m_widget
);
2170 display
= gdk_display_get_default();
2174 wxMouseState
wxGetMouseState()
2180 GdkModifierType mask
;
2182 GdkDisplay
* display
= GetDisplay();
2184 GdkDeviceManager
* manager
= gdk_display_get_device_manager(display
);
2185 GdkDevice
* device
= gdk_device_manager_get_client_pointer(manager
);
2187 gdk_device_get_position(device
, &screen
, &x
, &y
);
2188 GdkWindow
* window
= gdk_screen_get_root_window(screen
);
2189 gdk_device_get_state(device
, window
, NULL
, &mask
);
2191 gdk_display_get_pointer(display
, NULL
, &x
, &y
, &mask
);
2196 ms
.SetLeftDown((mask
& GDK_BUTTON1_MASK
) != 0);
2197 ms
.SetMiddleDown((mask
& GDK_BUTTON2_MASK
) != 0);
2198 ms
.SetRightDown((mask
& GDK_BUTTON3_MASK
) != 0);
2199 // see the comment in InitMouseEvent()
2200 ms
.SetAux1Down((mask
& GDK_BUTTON4_MASK
) != 0);
2201 ms
.SetAux2Down((mask
& GDK_BUTTON5_MASK
) != 0);
2203 ms
.SetControlDown((mask
& GDK_CONTROL_MASK
) != 0);
2204 ms
.SetShiftDown((mask
& GDK_SHIFT_MASK
) != 0);
2205 ms
.SetAltDown((mask
& GDK_MOD1_MASK
) != 0);
2206 ms
.SetMetaDown((mask
& GDK_META_MASK
) != 0);
2211 //-----------------------------------------------------------------------------
2213 //-----------------------------------------------------------------------------
2215 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2217 #ifdef __WXUNIVERSAL__
2218 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK
, wxWindowBase
)
2219 #endif // __WXUNIVERSAL__
2221 void wxWindowGTK::Init()
2226 m_focusWidget
= NULL
;
2234 m_showOnIdle
= false;
2237 m_nativeSizeEvent
= false;
2239 m_paintContext
= NULL
;
2240 m_styleProvider
= NULL
;
2243 m_isScrolling
= false;
2244 m_mouseButtonDown
= false;
2246 // initialize scrolling stuff
2247 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
2249 m_scrollBar
[dir
] = NULL
;
2250 m_scrollPos
[dir
] = 0;
2255 m_useCachedClientSize
= false;
2257 m_clipPaintRegion
= false;
2259 m_cursor
= *wxSTANDARD_CURSOR
;
2262 m_imKeyEvent
= NULL
;
2264 m_dirtyTabOrder
= false;
2267 wxWindowGTK::wxWindowGTK()
2272 wxWindowGTK::wxWindowGTK( wxWindow
*parent
,
2277 const wxString
&name
)
2281 Create( parent
, id
, pos
, size
, style
, name
);
2284 void wxWindowGTK::GTKCreateScrolledWindowWith(GtkWidget
* view
)
2286 wxASSERT_MSG( HasFlag(wxHSCROLL
) || HasFlag(wxVSCROLL
),
2287 wxS("Must not be called if scrolling is not needed.") );
2289 m_widget
= gtk_scrolled_window_new( NULL
, NULL
);
2291 GtkScrolledWindow
*scrolledWindow
= GTK_SCROLLED_WINDOW(m_widget
);
2293 // There is a conflict with default bindings at GTK+
2294 // level between scrolled windows and notebooks both of which want to use
2295 // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
2296 // direction and notebooks for changing pages -- we decide that if we don't
2297 // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
2298 // means we can get working keyboard navigation in notebooks
2299 if ( !HasFlag(wxHSCROLL
) )
2302 bindings
= gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget
));
2305 gtk_binding_entry_remove(bindings
, GDK_Page_Up
, GDK_CONTROL_MASK
);
2306 gtk_binding_entry_remove(bindings
, GDK_Page_Down
, GDK_CONTROL_MASK
);
2310 // If wx[HV]SCROLL is not given, the corresponding scrollbar is not shown
2311 // at all. Otherwise it may be shown only on demand (default) or always, if
2312 // the wxALWAYS_SHOW_SB is specified.
2313 GtkPolicyType horzPolicy
= HasFlag(wxHSCROLL
)
2314 ? HasFlag(wxALWAYS_SHOW_SB
)
2316 : GTK_POLICY_AUTOMATIC
2318 GtkPolicyType vertPolicy
= HasFlag(wxVSCROLL
)
2319 ? HasFlag(wxALWAYS_SHOW_SB
)
2321 : GTK_POLICY_AUTOMATIC
2323 gtk_scrolled_window_set_policy( scrolledWindow
, horzPolicy
, vertPolicy
);
2325 m_scrollBar
[ScrollDir_Horz
] = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(scrolledWindow
));
2326 m_scrollBar
[ScrollDir_Vert
] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(scrolledWindow
));
2327 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2328 gtk_range_set_inverted( m_scrollBar
[ScrollDir_Horz
], TRUE
);
2330 gtk_container_add( GTK_CONTAINER(m_widget
), view
);
2332 // connect various scroll-related events
2333 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
2335 // these handlers block mouse events to any window during scrolling
2336 // such as motion events and prevent GTK and wxWidgets from fighting
2337 // over where the slider should be
2338 g_signal_connect(m_scrollBar
[dir
], "button_press_event",
2339 G_CALLBACK(gtk_scrollbar_button_press_event
), this);
2340 g_signal_connect(m_scrollBar
[dir
], "button_release_event",
2341 G_CALLBACK(gtk_scrollbar_button_release_event
), this);
2343 gulong handler_id
= g_signal_connect(m_scrollBar
[dir
], "event_after",
2344 G_CALLBACK(gtk_scrollbar_event_after
), this);
2345 g_signal_handler_block(m_scrollBar
[dir
], handler_id
);
2347 // these handlers get notified when scrollbar slider moves
2348 g_signal_connect_after(m_scrollBar
[dir
], "value_changed",
2349 G_CALLBACK(gtk_scrollbar_value_changed
), this);
2352 gtk_widget_show( view
);
2355 bool wxWindowGTK::Create( wxWindow
*parent
,
2360 const wxString
&name
)
2362 // Get default border
2363 wxBorder border
= GetBorder(style
);
2365 style
&= ~wxBORDER_MASK
;
2368 if (!PreCreation( parent
, pos
, size
) ||
2369 !CreateBase( parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
))
2371 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2375 // We should accept the native look
2377 GtkScrolledWindowClass
*scroll_class
= GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget
) );
2378 scroll_class
->scrollbar_spacing
= 0;
2382 m_wxwindow
= wxPizza::New(m_windowStyle
);
2383 #ifndef __WXUNIVERSAL__
2384 if (HasFlag(wxPizza::BORDER_STYLES
))
2386 g_signal_connect(m_wxwindow
, "parent_set",
2387 G_CALLBACK(parent_set
), this);
2390 if (!HasFlag(wxHSCROLL
) && !HasFlag(wxVSCROLL
))
2391 m_widget
= m_wxwindow
;
2393 GTKCreateScrolledWindowWith(m_wxwindow
);
2394 g_object_ref(m_widget
);
2397 m_parent
->DoAddChild( this );
2399 m_focusWidget
= m_wxwindow
;
2401 SetCanFocus(AcceptsFocus());
2408 void wxWindowGTK::GTKDisconnect(void* instance
)
2410 g_signal_handlers_disconnect_matched(instance
,
2411 GSignalMatchType(G_SIGNAL_MATCH_DATA
), 0, 0, NULL
, NULL
, this);
2414 wxWindowGTK::~wxWindowGTK()
2418 if (gs_currentFocus
== this)
2419 gs_currentFocus
= NULL
;
2420 if (gs_pendingFocus
== this)
2421 gs_pendingFocus
= NULL
;
2423 if ( gs_deferredFocusOut
== this )
2424 gs_deferredFocusOut
= NULL
;
2426 // Unlike the above cases, which can happen in normal circumstances, a
2427 // window shouldn't be destroyed while it still has capture, so even though
2428 // we still reset the global pointer to avoid leaving it dangling and
2429 // crashing afterwards, also complain about it.
2430 if ( g_captureWindow
== this )
2432 wxFAIL_MSG( wxS("Destroying window with mouse capture") );
2433 g_captureWindow
= NULL
;
2438 GTKDisconnect(m_wxwindow
);
2439 GtkWidget
* parent
= gtk_widget_get_parent(m_wxwindow
);
2441 GTKDisconnect(parent
);
2443 if (m_widget
&& m_widget
!= m_wxwindow
)
2444 GTKDisconnect(m_widget
);
2446 // destroy children before destroying this window itself
2449 // delete before the widgets to avoid a crash on solaris
2452 g_object_unref(m_imContext
);
2456 // avoid problem with GTK+ 2.18 where a frozen window causes the whole
2457 // TLW to be frozen, and if the window is then destroyed, nothing ever
2458 // gets painted again
2463 if (m_styleProvider
)
2464 g_object_unref(m_styleProvider
);
2469 // Note that gtk_widget_destroy() does not destroy the widget, it just
2470 // emits the "destroy" signal. The widget is not actually destroyed
2471 // until its reference count drops to zero.
2472 gtk_widget_destroy(m_widget
);
2473 // Release our reference, should be the last one
2474 g_object_unref(m_widget
);
2480 bool wxWindowGTK::PreCreation( wxWindowGTK
*parent
, const wxPoint
&pos
, const wxSize
&size
)
2482 if ( GTKNeedsParent() )
2484 wxCHECK_MSG( parent
, false, wxT("Must have non-NULL parent") );
2487 // Use either the given size, or the default if -1 is given.
2488 // See wxWindowBase for these functions.
2489 m_width
= WidthDefault(size
.x
) ;
2490 m_height
= HeightDefault(size
.y
);
2492 if (pos
!= wxDefaultPosition
)
2501 void wxWindowGTK::PostCreation()
2503 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
2505 #if wxGTK_HAS_COMPOSITING_SUPPORT
2506 // Set RGBA visual as soon as possible to minimize the possibility that
2507 // somebody uses the wrong one.
2508 if ( m_backgroundStyle
== wxBG_STYLE_TRANSPARENT
&&
2509 IsTransparentBackgroundSupported() )
2511 GdkScreen
*screen
= gtk_widget_get_screen (m_widget
);
2513 gtk_widget_set_visual(m_widget
, gdk_screen_get_rgba_visual(screen
));
2515 GdkColormap
*rgba_colormap
= gdk_screen_get_rgba_colormap (screen
);
2518 gtk_widget_set_colormap(m_widget
, rgba_colormap
);
2521 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
2527 // these get reported to wxWidgets -> wxPaintEvent
2529 g_signal_connect(m_wxwindow
, "draw", G_CALLBACK(draw
), this);
2531 g_signal_connect(m_wxwindow
, "expose_event", G_CALLBACK(expose_event
), this);
2534 if (GetLayoutDirection() == wxLayout_LeftToRight
)
2535 gtk_widget_set_redraw_on_allocate(m_wxwindow
, HasFlag(wxFULL_REPAINT_ON_RESIZE
));
2538 // Create input method handler
2539 m_imContext
= gtk_im_multicontext_new();
2541 // Cannot handle drawing preedited text yet
2542 gtk_im_context_set_use_preedit( m_imContext
, FALSE
);
2544 g_signal_connect (m_imContext
, "commit",
2545 G_CALLBACK (gtk_wxwindow_commit_cb
), this);
2550 if (!GTK_IS_WINDOW(m_widget
))
2552 if (m_focusWidget
== NULL
)
2553 m_focusWidget
= m_widget
;
2557 g_signal_connect (m_focusWidget
, "focus_in_event",
2558 G_CALLBACK (gtk_window_focus_in_callback
), this);
2559 g_signal_connect (m_focusWidget
, "focus_out_event",
2560 G_CALLBACK (gtk_window_focus_out_callback
), this);
2564 g_signal_connect_after (m_focusWidget
, "focus_in_event",
2565 G_CALLBACK (gtk_window_focus_in_callback
), this);
2566 g_signal_connect_after (m_focusWidget
, "focus_out_event",
2567 G_CALLBACK (gtk_window_focus_out_callback
), this);
2571 if ( !AcceptsFocusFromKeyboard() )
2575 g_signal_connect(m_widget
, "focus",
2576 G_CALLBACK(wx_window_focus_callback
), this);
2579 // connect to the various key and mouse handlers
2581 GtkWidget
*connect_widget
= GetConnectWidget();
2583 ConnectWidget( connect_widget
);
2585 // We cannot set colours, fonts and cursors before the widget has been
2586 // realized, so we do this directly after realization -- unless the widget
2587 // was in fact realized already.
2588 if ( gtk_widget_get_realized(connect_widget
) )
2590 GTKHandleRealized();
2594 g_signal_connect (connect_widget
, "realize",
2595 G_CALLBACK (gtk_window_realized_callback
), this);
2597 g_signal_connect(connect_widget
, "unrealize", G_CALLBACK(unrealize
), this);
2601 g_signal_connect(m_wxwindow
? m_wxwindow
: m_widget
, "size_allocate",
2602 G_CALLBACK(size_allocate
), this);
2605 #if GTK_CHECK_VERSION(2, 8, 0)
2607 if ( gtk_check_version(2,8,0) == NULL
)
2610 // Make sure we can notify the app when mouse capture is lost
2613 g_signal_connect (m_wxwindow
, "grab_broken_event",
2614 G_CALLBACK (gtk_window_grab_broken
), this);
2617 if ( connect_widget
!= m_wxwindow
)
2619 g_signal_connect (connect_widget
, "grab_broken_event",
2620 G_CALLBACK (gtk_window_grab_broken
), this);
2623 #endif // GTK+ >= 2.8
2625 if (!WX_IS_PIZZA(gtk_widget_get_parent(m_widget
)) && !GTK_IS_WINDOW(m_widget
))
2626 gtk_widget_set_size_request(m_widget
, m_width
, m_height
);
2628 // apply any font or color changes made before creation
2629 GTKApplyWidgetStyle();
2631 InheritAttributes();
2633 SetLayoutDirection(wxLayout_Default
);
2635 // unless the window was created initially hidden (i.e. Hide() had been
2636 // called before Create()), we should show it at GTK+ level as well
2638 gtk_widget_show( m_widget
);
2642 wxWindowGTK::GTKConnectWidget(const char *signal
, wxGTKCallback callback
)
2644 return g_signal_connect(m_widget
, signal
, callback
, this);
2647 void wxWindowGTK::ConnectWidget( GtkWidget
*widget
)
2649 g_signal_connect (widget
, "key_press_event",
2650 G_CALLBACK (gtk_window_key_press_callback
), this);
2651 g_signal_connect (widget
, "key_release_event",
2652 G_CALLBACK (gtk_window_key_release_callback
), this);
2653 g_signal_connect (widget
, "button_press_event",
2654 G_CALLBACK (gtk_window_button_press_callback
), this);
2655 g_signal_connect (widget
, "button_release_event",
2656 G_CALLBACK (gtk_window_button_release_callback
), this);
2657 g_signal_connect (widget
, "motion_notify_event",
2658 G_CALLBACK (gtk_window_motion_notify_callback
), this);
2660 g_signal_connect(widget
, "scroll_event", G_CALLBACK(scroll_event
), this);
2661 GtkRange
* range
= m_scrollBar
[ScrollDir_Horz
];
2663 g_signal_connect(range
, "scroll_event", G_CALLBACK(scroll_event
), this);
2664 range
= m_scrollBar
[ScrollDir_Vert
];
2666 g_signal_connect(range
, "scroll_event", G_CALLBACK(scroll_event
), this);
2668 g_signal_connect (widget
, "popup_menu",
2669 G_CALLBACK (wxgtk_window_popup_menu_callback
), this);
2670 g_signal_connect (widget
, "enter_notify_event",
2671 G_CALLBACK (gtk_window_enter_callback
), this);
2672 g_signal_connect (widget
, "leave_notify_event",
2673 G_CALLBACK (gtk_window_leave_callback
), this);
2676 static GSList
* gs_queueResizeList
;
2679 static gboolean
queue_resize(void*)
2681 gdk_threads_enter();
2682 for (GSList
* p
= gs_queueResizeList
; p
; p
= p
->next
)
2686 gtk_widget_queue_resize(GTK_WIDGET(p
->data
));
2687 g_object_remove_weak_pointer(G_OBJECT(p
->data
), &p
->data
);
2690 g_slist_free(gs_queueResizeList
);
2691 gs_queueResizeList
= NULL
;
2692 gdk_threads_leave();
2697 void wxWindowGTK::DoMoveWindow(int x
, int y
, int width
, int height
)
2699 gtk_widget_set_size_request(m_widget
, width
, height
);
2700 GtkWidget
* parent
= gtk_widget_get_parent(m_widget
);
2701 if (WX_IS_PIZZA(parent
))
2702 WX_PIZZA(parent
)->move(m_widget
, x
, y
, width
, height
);
2704 // With GTK3, gtk_widget_queue_resize() is ignored while a size-allocate
2705 // is in progress. This situation is common in wxWidgets, since
2706 // size-allocate can generate wxSizeEvent and size event handlers often
2707 // call SetSize(), directly or indirectly. Work around this by deferring
2708 // the queue-resize until after size-allocate processing is finished.
2709 if (g_slist_find(gs_queueResizeList
, m_widget
) == NULL
)
2711 if (gs_queueResizeList
== NULL
)
2712 g_idle_add_full(GTK_PRIORITY_RESIZE
, queue_resize
, NULL
, NULL
);
2713 gs_queueResizeList
= g_slist_prepend(gs_queueResizeList
, m_widget
);
2714 g_object_add_weak_pointer(G_OBJECT(m_widget
), &gs_queueResizeList
->data
);
2718 void wxWindowGTK::ConstrainSize()
2721 // GPE's window manager doesn't like size hints at all, esp. when the user
2722 // has to use the virtual keyboard, so don't constrain size there
2726 const wxSize minSize
= GetMinSize();
2727 const wxSize maxSize
= GetMaxSize();
2728 if (minSize
.x
> 0 && m_width
< minSize
.x
) m_width
= minSize
.x
;
2729 if (minSize
.y
> 0 && m_height
< minSize
.y
) m_height
= minSize
.y
;
2730 if (maxSize
.x
> 0 && m_width
> maxSize
.x
) m_width
= maxSize
.x
;
2731 if (maxSize
.y
> 0 && m_height
> maxSize
.y
) m_height
= maxSize
.y
;
2735 void wxWindowGTK::DoSetSize( int x
, int y
, int width
, int height
, int sizeFlags
)
2737 wxCHECK_RET(m_widget
, "invalid window");
2739 int scrollX
= 0, scrollY
= 0;
2740 GtkWidget
* parent
= gtk_widget_get_parent(m_widget
);
2741 if (WX_IS_PIZZA(parent
))
2743 wxPizza
* pizza
= WX_PIZZA(parent
);
2744 scrollX
= pizza
->m_scroll_x
;
2745 scrollY
= pizza
->m_scroll_y
;
2747 if (x
!= -1 || (sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
2751 if (y
!= -1 || (sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
2756 // calculate the best size if we should auto size the window
2757 if ( ((sizeFlags
& wxSIZE_AUTO_WIDTH
) && width
== -1) ||
2758 ((sizeFlags
& wxSIZE_AUTO_HEIGHT
) && height
== -1) )
2760 const wxSize sizeBest
= GetBestSize();
2761 if ( (sizeFlags
& wxSIZE_AUTO_WIDTH
) && width
== -1 )
2763 if ( (sizeFlags
& wxSIZE_AUTO_HEIGHT
) && height
== -1 )
2764 height
= sizeBest
.y
;
2772 const bool sizeChange
= m_width
!= width
|| m_height
!= height
;
2775 m_useCachedClientSize
= false;
2777 if (sizeChange
|| m_x
!= x
|| m_y
!= y
)
2784 /* the default button has a border around it */
2785 if (gtk_widget_get_can_default(m_widget
))
2787 GtkBorder
*default_border
= NULL
;
2788 gtk_widget_style_get( m_widget
, "default_border", &default_border
, NULL
);
2791 x
-= default_border
->left
;
2792 y
-= default_border
->top
;
2793 width
+= default_border
->left
+ default_border
->right
;
2794 height
+= default_border
->top
+ default_border
->bottom
;
2795 gtk_border_free( default_border
);
2799 DoMoveWindow(x
, y
, width
, height
);
2802 if ((sizeChange
&& !m_nativeSizeEvent
) || (sizeFlags
& wxSIZE_FORCE_EVENT
))
2804 // update these variables to keep size_allocate handler
2805 // from sending another size event for this change
2806 DoGetClientSize(&m_clientWidth
, &m_clientHeight
);
2808 wxSizeEvent
event( wxSize(m_width
,m_height
), GetId() );
2809 event
.SetEventObject( this );
2810 HandleWindowEvent( event
);
2814 bool wxWindowGTK::GTKShowFromOnIdle()
2816 if (IsShown() && m_showOnIdle
&& !gtk_widget_get_visible (m_widget
))
2818 GtkAllocation alloc
;
2821 alloc
.width
= m_width
;
2822 alloc
.height
= m_height
;
2823 gtk_widget_size_allocate( m_widget
, &alloc
);
2824 gtk_widget_show( m_widget
);
2825 wxShowEvent
eventShow(GetId(), true);
2826 eventShow
.SetEventObject(this);
2827 HandleWindowEvent(eventShow
);
2828 m_showOnIdle
= false;
2835 void wxWindowGTK::OnInternalIdle()
2837 if ( gs_deferredFocusOut
)
2838 GTKHandleDeferredFocusOut();
2840 // Check if we have to show window now
2841 if (GTKShowFromOnIdle()) return;
2843 if ( m_dirtyTabOrder
)
2845 m_dirtyTabOrder
= false;
2849 wxWindowBase::OnInternalIdle();
2852 void wxWindowGTK::DoGetSize( int *width
, int *height
) const
2854 if (width
) (*width
) = m_width
;
2855 if (height
) (*height
) = m_height
;
2858 void wxWindowGTK::DoSetClientSize( int width
, int height
)
2860 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2862 const wxSize size
= GetSize();
2863 const wxSize clientSize
= GetClientSize();
2864 SetSize(width
+ (size
.x
- clientSize
.x
), height
+ (size
.y
- clientSize
.y
));
2867 void wxWindowGTK::DoGetClientSize( int *width
, int *height
) const
2869 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2871 if (m_useCachedClientSize
)
2873 if (width
) *width
= m_clientWidth
;
2874 if (height
) *height
= m_clientHeight
;
2883 // if window is scrollable, account for scrollbars
2884 if ( GTK_IS_SCROLLED_WINDOW(m_widget
) )
2886 GtkPolicyType policy
[ScrollDir_Max
];
2887 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(m_widget
),
2888 &policy
[ScrollDir_Horz
],
2889 &policy
[ScrollDir_Vert
]);
2891 // get scrollbar spacing the same way the GTK-private function
2892 // _gtk_scrolled_window_get_scrollbar_spacing() does it
2893 int scrollbar_spacing
=
2894 GTK_SCROLLED_WINDOW_GET_CLASS(m_widget
)->scrollbar_spacing
;
2895 if (scrollbar_spacing
< 0)
2897 gtk_widget_style_get(
2898 m_widget
, "scrollbar-spacing", &scrollbar_spacing
, NULL
);
2901 for ( int i
= 0; i
< ScrollDir_Max
; i
++ )
2903 // don't account for the scrollbars we don't have
2904 GtkRange
* const range
= m_scrollBar
[i
];
2908 // nor for the ones we have but don't current show
2909 switch ( policy
[i
] )
2911 case GTK_POLICY_NEVER
:
2912 // never shown so doesn't take any place
2915 case GTK_POLICY_ALWAYS
:
2916 // no checks necessary
2919 case GTK_POLICY_AUTOMATIC
:
2920 // may be shown or not, check
2921 GtkAdjustment
*adj
= gtk_range_get_adjustment(range
);
2922 if (gtk_adjustment_get_upper(adj
) <= gtk_adjustment_get_page_size(adj
))
2928 GtkWidget
* widget
= GTK_WIDGET(range
);
2929 if (i
== ScrollDir_Horz
)
2933 gtk_widget_get_preferred_height(widget
, NULL
, &req
.height
);
2934 h
-= req
.height
+ scrollbar_spacing
;
2941 gtk_widget_get_preferred_width(widget
, NULL
, &req
.width
);
2942 w
-= req
.width
+ scrollbar_spacing
;
2945 #else // !__WXGTK3__
2946 gtk_widget_size_request(GTK_WIDGET(range
), &req
);
2947 if (i
== ScrollDir_Horz
)
2948 h
-= req
.height
+ scrollbar_spacing
;
2950 w
-= req
.width
+ scrollbar_spacing
;
2951 #endif // !__WXGTK3__
2955 const wxSize sizeBorders
= DoGetBorderSize();
2965 if (width
) *width
= w
;
2966 if (height
) *height
= h
;
2969 wxSize
wxWindowGTK::DoGetBorderSize() const
2972 return wxWindowBase::DoGetBorderSize();
2975 WX_PIZZA(m_wxwindow
)->get_border(border
);
2976 return wxSize(border
.left
+ border
.right
, border
.top
+ border
.bottom
);
2979 void wxWindowGTK::DoGetPosition( int *x
, int *y
) const
2983 GtkWidget
* parent
= NULL
;
2985 parent
= gtk_widget_get_parent(m_widget
);
2986 if (WX_IS_PIZZA(parent
))
2988 wxPizza
* pizza
= WX_PIZZA(parent
);
2989 dx
= pizza
->m_scroll_x
;
2990 dy
= pizza
->m_scroll_y
;
2992 if (x
) (*x
) = m_x
- dx
;
2993 if (y
) (*y
) = m_y
- dy
;
2996 void wxWindowGTK::DoClientToScreen( int *x
, int *y
) const
2998 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3000 if (gtk_widget_get_window(m_widget
) == NULL
) return;
3002 GdkWindow
*source
= NULL
;
3004 source
= gtk_widget_get_window(m_wxwindow
);
3006 source
= gtk_widget_get_window(m_widget
);
3010 gdk_window_get_origin( source
, &org_x
, &org_y
);
3014 if (!gtk_widget_get_has_window(m_widget
))
3017 gtk_widget_get_allocation(m_widget
, &a
);
3026 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3027 *x
= (GetClientSize().x
- *x
) + org_x
;
3035 void wxWindowGTK::DoScreenToClient( int *x
, int *y
) const
3037 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3039 if (!gtk_widget_get_realized(m_widget
)) return;
3041 GdkWindow
*source
= NULL
;
3043 source
= gtk_widget_get_window(m_wxwindow
);
3045 source
= gtk_widget_get_window(m_widget
);
3049 gdk_window_get_origin( source
, &org_x
, &org_y
);
3053 if (!gtk_widget_get_has_window(m_widget
))
3056 gtk_widget_get_allocation(m_widget
, &a
);
3064 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3065 *x
= (GetClientSize().x
- *x
) - org_x
;
3072 bool wxWindowGTK::Show( bool show
)
3074 if ( !wxWindowBase::Show(show
) )
3080 // notice that we may call Hide() before the window is created and this is
3081 // actually useful to create it hidden initially -- but we can't call
3082 // Show() before it is created
3085 wxASSERT_MSG( !show
, "can't show invalid window" );
3093 // defer until later
3097 gtk_widget_show(m_widget
);
3101 gtk_widget_hide(m_widget
);
3104 wxShowEvent
eventShow(GetId(), show
);
3105 eventShow
.SetEventObject(this);
3106 HandleWindowEvent(eventShow
);
3111 void wxWindowGTK::DoEnable( bool enable
)
3113 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3115 gtk_widget_set_sensitive( m_widget
, enable
);
3116 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
3117 gtk_widget_set_sensitive( m_wxwindow
, enable
);
3120 int wxWindowGTK::GetCharHeight() const
3122 wxCHECK_MSG( (m_widget
!= NULL
), 12, wxT("invalid window") );
3124 wxFont font
= GetFont();
3125 wxCHECK_MSG( font
.IsOk(), 12, wxT("invalid font") );
3127 PangoContext
* context
= gtk_widget_get_pango_context(m_widget
);
3132 PangoFontDescription
*desc
= font
.GetNativeFontInfo()->description
;
3133 PangoLayout
*layout
= pango_layout_new(context
);
3134 pango_layout_set_font_description(layout
, desc
);
3135 pango_layout_set_text(layout
, "H", 1);
3136 PangoLayoutLine
*line
= (PangoLayoutLine
*)pango_layout_get_lines(layout
)->data
;
3138 PangoRectangle rect
;
3139 pango_layout_line_get_extents(line
, NULL
, &rect
);
3141 g_object_unref (layout
);
3143 return (int) PANGO_PIXELS(rect
.height
);
3146 int wxWindowGTK::GetCharWidth() const
3148 wxCHECK_MSG( (m_widget
!= NULL
), 8, wxT("invalid window") );
3150 wxFont font
= GetFont();
3151 wxCHECK_MSG( font
.IsOk(), 8, wxT("invalid font") );
3153 PangoContext
* context
= gtk_widget_get_pango_context(m_widget
);
3158 PangoFontDescription
*desc
= font
.GetNativeFontInfo()->description
;
3159 PangoLayout
*layout
= pango_layout_new(context
);
3160 pango_layout_set_font_description(layout
, desc
);
3161 pango_layout_set_text(layout
, "g", 1);
3162 PangoLayoutLine
*line
= (PangoLayoutLine
*)pango_layout_get_lines(layout
)->data
;
3164 PangoRectangle rect
;
3165 pango_layout_line_get_extents(line
, NULL
, &rect
);
3167 g_object_unref (layout
);
3169 return (int) PANGO_PIXELS(rect
.width
);
3172 void wxWindowGTK::DoGetTextExtent( const wxString
& string
,
3176 int *externalLeading
,
3177 const wxFont
*theFont
) const
3179 // ensure we work with a valid font
3181 if ( !theFont
|| !theFont
->IsOk() )
3182 fontToUse
= GetFont();
3184 fontToUse
= *theFont
;
3186 wxCHECK_RET( fontToUse
.IsOk(), wxT("invalid font") );
3188 const wxWindow
* win
= static_cast<const wxWindow
*>(this);
3189 wxTextMeasure
txm(win
, &fontToUse
);
3190 txm
.GetTextExtent(string
, x
, y
, descent
, externalLeading
);
3193 void wxWindowGTK::GTKDisableFocusOutEvent()
3195 g_signal_handlers_block_by_func( m_focusWidget
,
3196 (gpointer
) gtk_window_focus_out_callback
, this);
3199 void wxWindowGTK::GTKEnableFocusOutEvent()
3201 g_signal_handlers_unblock_by_func( m_focusWidget
,
3202 (gpointer
) gtk_window_focus_out_callback
, this);
3205 bool wxWindowGTK::GTKHandleFocusIn()
3207 // Disable default focus handling for custom windows since the default GTK+
3208 // handler issues a repaint
3209 const bool retval
= m_wxwindow
? true : false;
3212 // NB: if there's still unprocessed deferred focus-out event (see
3213 // GTKHandleFocusOut() for explanation), we need to process it first so
3214 // that the order of focus events -- focus-out first, then focus-in
3215 // elsewhere -- is preserved
3216 if ( gs_deferredFocusOut
)
3218 if ( GTKNeedsToFilterSameWindowFocus() &&
3219 gs_deferredFocusOut
== this )
3221 // GTK+ focus changed from this wxWindow back to itself, so don't
3222 // emit any events at all
3223 wxLogTrace(TRACE_FOCUS
,
3224 "filtered out spurious focus change within %s(%p, %s)",
3225 GetClassInfo()->GetClassName(), this, GetLabel());
3226 gs_deferredFocusOut
= NULL
;
3230 // otherwise we need to send focus-out first
3231 wxASSERT_MSG ( gs_deferredFocusOut
!= this,
3232 "GTKHandleFocusIn(GTKFocus_Normal) called even though focus changed back to itself - derived class should handle this" );
3233 GTKHandleDeferredFocusOut();
3237 wxLogTrace(TRACE_FOCUS
,
3238 "handling focus_in event for %s(%p, %s)",
3239 GetClassInfo()->GetClassName(), this, GetLabel());
3242 gtk_im_context_focus_in(m_imContext
);
3244 gs_currentFocus
= this;
3245 gs_pendingFocus
= NULL
;
3248 // caret needs to be informed about focus change
3249 wxCaret
*caret
= GetCaret();
3252 caret
->OnSetFocus();
3254 #endif // wxUSE_CARET
3256 // Notify the parent keeping track of focus for the kbd navigation
3257 // purposes that we got it.
3258 wxChildFocusEvent
eventChildFocus(static_cast<wxWindow
*>(this));
3259 GTKProcessEvent(eventChildFocus
);
3261 wxFocusEvent
eventFocus(wxEVT_SET_FOCUS
, GetId());
3262 eventFocus
.SetEventObject(this);
3263 GTKProcessEvent(eventFocus
);
3268 bool wxWindowGTK::GTKHandleFocusOut()
3270 // Disable default focus handling for custom windows since the default GTK+
3271 // handler issues a repaint
3272 const bool retval
= m_wxwindow
? true : false;
3275 // NB: If a control is composed of several GtkWidgets and when focus
3276 // changes from one of them to another within the same wxWindow, we get
3277 // a focus-out event followed by focus-in for another GtkWidget owned
3278 // by the same wx control. We don't want to generate two spurious
3279 // wxEVT_SET_FOCUS events in this case, so we defer sending wx events
3280 // from GTKHandleFocusOut() until we know for sure it's not coming back
3281 // (i.e. in GTKHandleFocusIn() or at idle time).
3282 if ( GTKNeedsToFilterSameWindowFocus() )
3284 wxASSERT_MSG( gs_deferredFocusOut
== NULL
,
3285 "deferred focus out event already pending" );
3286 wxLogTrace(TRACE_FOCUS
,
3287 "deferring focus_out event for %s(%p, %s)",
3288 GetClassInfo()->GetClassName(), this, GetLabel());
3289 gs_deferredFocusOut
= this;
3293 GTKHandleFocusOutNoDeferring();
3298 void wxWindowGTK::GTKHandleFocusOutNoDeferring()
3300 wxLogTrace(TRACE_FOCUS
,
3301 "handling focus_out event for %s(%p, %s)",
3302 GetClassInfo()->GetClassName(), this, GetLabel());
3305 gtk_im_context_focus_out(m_imContext
);
3307 if ( gs_currentFocus
!= this )
3309 // Something is terribly wrong, gs_currentFocus is out of sync with the
3310 // real focus. We will reset it to NULL anyway, because after this
3311 // focus-out event is handled, one of the following with happen:
3313 // * either focus will go out of the app altogether, in which case
3314 // gs_currentFocus _should_ be NULL
3316 // * or it goes to another control, in which case focus-in event will
3317 // follow immediately and it will set gs_currentFocus to the right
3319 wxLogDebug("window %s(%p, %s) lost focus even though it didn't have it",
3320 GetClassInfo()->GetClassName(), this, GetLabel());
3322 gs_currentFocus
= NULL
;
3325 // caret needs to be informed about focus change
3326 wxCaret
*caret
= GetCaret();
3329 caret
->OnKillFocus();
3331 #endif // wxUSE_CARET
3333 wxFocusEvent
event( wxEVT_KILL_FOCUS
, GetId() );
3334 event
.SetEventObject( this );
3335 event
.SetWindow( FindFocus() );
3336 GTKProcessEvent( event
);
3340 void wxWindowGTK::GTKHandleDeferredFocusOut()
3342 // NB: See GTKHandleFocusOut() for explanation. This function is called
3343 // from either GTKHandleFocusIn() or OnInternalIdle() to process
3345 if ( gs_deferredFocusOut
)
3347 wxWindowGTK
*win
= gs_deferredFocusOut
;
3348 gs_deferredFocusOut
= NULL
;
3350 wxLogTrace(TRACE_FOCUS
,
3351 "processing deferred focus_out event for %s(%p, %s)",
3352 win
->GetClassInfo()->GetClassName(), win
, win
->GetLabel());
3354 win
->GTKHandleFocusOutNoDeferring();
3358 void wxWindowGTK::SetFocus()
3360 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
3362 // Setting "physical" focus is not immediate in GTK+ and while
3363 // gtk_widget_is_focus ("determines if the widget is the focus widget
3364 // within its toplevel", i.e. returns true for one widget per TLW, not
3365 // globally) returns true immediately after grabbing focus,
3366 // GTK_WIDGET_HAS_FOCUS (which returns true only for the one widget that
3367 // has focus at the moment) takes effect only after the window is shown
3368 // (if it was hidden at the moment of the call) or at the next event loop
3371 // Because we want to FindFocus() call immediately following
3372 // foo->SetFocus() to return foo, we have to keep track of "pending" focus
3374 gs_pendingFocus
= this;
3376 GtkWidget
*widget
= m_wxwindow
? m_wxwindow
: m_focusWidget
;
3378 if ( GTK_IS_CONTAINER(widget
) &&
3379 !gtk_widget_get_can_focus(widget
) )
3381 wxLogTrace(TRACE_FOCUS
,
3382 wxT("Setting focus to a child of %s(%p, %s)"),
3383 GetClassInfo()->GetClassName(), this, GetLabel().c_str());
3384 gtk_widget_child_focus(widget
, GTK_DIR_TAB_FORWARD
);
3388 wxLogTrace(TRACE_FOCUS
,
3389 wxT("Setting focus to %s(%p, %s)"),
3390 GetClassInfo()->GetClassName(), this, GetLabel().c_str());
3391 gtk_widget_grab_focus(widget
);
3395 void wxWindowGTK::SetCanFocus(bool canFocus
)
3397 wxCHECK_RET(m_widget
, "invalid window");
3399 gtk_widget_set_can_focus(m_widget
, canFocus
);
3401 if ( m_wxwindow
&& (m_widget
!= m_wxwindow
) )
3403 gtk_widget_set_can_focus(m_wxwindow
, canFocus
);
3407 bool wxWindowGTK::Reparent( wxWindowBase
*newParentBase
)
3409 wxCHECK_MSG( (m_widget
!= NULL
), false, wxT("invalid window") );
3411 wxWindowGTK
* const newParent
= (wxWindowGTK
*)newParentBase
;
3413 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3415 if ( !wxWindowBase::Reparent(newParent
) )
3418 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3420 // Notice that old m_parent pointer might be non-NULL here but the widget
3421 // still not have any parent at GTK level if it's a notebook page that had
3422 // been removed from the notebook so test this at GTK level and not wx one.
3423 if ( GtkWidget
*parentGTK
= gtk_widget_get_parent(m_widget
) )
3424 gtk_container_remove(GTK_CONTAINER(parentGTK
), m_widget
);
3426 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3430 if (gtk_widget_get_visible (newParent
->m_widget
))
3432 m_showOnIdle
= true;
3433 gtk_widget_hide( m_widget
);
3435 /* insert GTK representation */
3436 newParent
->AddChildGTK(this);
3439 SetLayoutDirection(wxLayout_Default
);
3444 void wxWindowGTK::DoAddChild(wxWindowGTK
*child
)
3446 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
3447 wxASSERT_MSG( (child
!= NULL
), wxT("invalid child window") );
3452 /* insert GTK representation */
3456 void wxWindowGTK::AddChild(wxWindowBase
*child
)
3458 wxWindowBase::AddChild(child
);
3459 m_dirtyTabOrder
= true;
3460 wxTheApp
->WakeUpIdle();
3463 void wxWindowGTK::RemoveChild(wxWindowBase
*child
)
3465 wxWindowBase::RemoveChild(child
);
3466 m_dirtyTabOrder
= true;
3467 wxTheApp
->WakeUpIdle();
3471 wxLayoutDirection
wxWindowGTK::GTKGetLayout(GtkWidget
*widget
)
3473 return gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
3474 ? wxLayout_RightToLeft
3475 : wxLayout_LeftToRight
;
3479 void wxWindowGTK::GTKSetLayout(GtkWidget
*widget
, wxLayoutDirection dir
)
3481 wxASSERT_MSG( dir
!= wxLayout_Default
, wxT("invalid layout direction") );
3483 gtk_widget_set_direction(widget
,
3484 dir
== wxLayout_RightToLeft
? GTK_TEXT_DIR_RTL
3485 : GTK_TEXT_DIR_LTR
);
3488 wxLayoutDirection
wxWindowGTK::GetLayoutDirection() const
3490 return GTKGetLayout(m_widget
);
3493 void wxWindowGTK::SetLayoutDirection(wxLayoutDirection dir
)
3495 if ( dir
== wxLayout_Default
)
3497 const wxWindow
*const parent
= GetParent();
3500 // inherit layout from parent.
3501 dir
= parent
->GetLayoutDirection();
3503 else // no parent, use global default layout
3505 dir
= wxTheApp
->GetLayoutDirection();
3509 if ( dir
== wxLayout_Default
)
3512 GTKSetLayout(m_widget
, dir
);
3514 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
3515 GTKSetLayout(m_wxwindow
, dir
);
3519 wxWindowGTK::AdjustForLayoutDirection(wxCoord x
,
3520 wxCoord
WXUNUSED(width
),
3521 wxCoord
WXUNUSED(widthTotal
)) const
3523 // We now mirror the coordinates of RTL windows in wxPizza
3527 void wxWindowGTK::DoMoveInTabOrder(wxWindow
*win
, WindowOrder move
)
3529 wxWindowBase::DoMoveInTabOrder(win
, move
);
3530 m_dirtyTabOrder
= true;
3531 wxTheApp
->WakeUpIdle();
3534 bool wxWindowGTK::DoNavigateIn(int flags
)
3536 if ( flags
& wxNavigationKeyEvent::WinChange
)
3538 wxFAIL_MSG( wxT("not implemented") );
3542 else // navigate inside the container
3544 wxWindow
*parent
= wxGetTopLevelParent((wxWindow
*)this);
3545 wxCHECK_MSG( parent
, false, wxT("every window must have a TLW parent") );
3547 GtkDirectionType dir
;
3548 dir
= flags
& wxNavigationKeyEvent::IsForward
? GTK_DIR_TAB_FORWARD
3549 : GTK_DIR_TAB_BACKWARD
;
3552 g_signal_emit_by_name(parent
->m_widget
, "focus", dir
, &rc
);
3558 bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3560 // none needed by default
3564 void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget
* WXUNUSED(w
))
3566 // nothing to do by default since none is needed
3569 void wxWindowGTK::RealizeTabOrder()
3573 if ( !m_children
.empty() )
3575 // we don't only construct the correct focus chain but also use
3576 // this opportunity to update the mnemonic widgets for the widgets
3579 GList
*chain
= NULL
;
3580 wxWindowGTK
* mnemonicWindow
= NULL
;
3582 for ( wxWindowList::const_iterator i
= m_children
.begin();
3583 i
!= m_children
.end();
3586 wxWindowGTK
*win
= *i
;
3588 bool focusableFromKeyboard
= win
->AcceptsFocusFromKeyboard();
3590 if ( mnemonicWindow
)
3592 if ( focusableFromKeyboard
)
3594 // wxComboBox et al. needs to focus on on a different
3595 // widget than m_widget, so if the main widget isn't
3596 // focusable try the connect widget
3597 GtkWidget
* w
= win
->m_widget
;
3598 if ( !gtk_widget_get_can_focus(w
) )
3600 w
= win
->GetConnectWidget();
3601 if ( !gtk_widget_get_can_focus(w
) )
3607 mnemonicWindow
->GTKWidgetDoSetMnemonic(w
);
3608 mnemonicWindow
= NULL
;
3612 else if ( win
->GTKWidgetNeedsMnemonic() )
3614 mnemonicWindow
= win
;
3617 if ( focusableFromKeyboard
)
3618 chain
= g_list_prepend(chain
, win
->m_widget
);
3621 chain
= g_list_reverse(chain
);
3623 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow
), chain
);
3628 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow
));
3633 void wxWindowGTK::Raise()
3635 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3637 if (m_wxwindow
&& gtk_widget_get_window(m_wxwindow
))
3639 gdk_window_raise(gtk_widget_get_window(m_wxwindow
));
3641 else if (gtk_widget_get_window(m_widget
))
3643 gdk_window_raise(gtk_widget_get_window(m_widget
));
3647 void wxWindowGTK::Lower()
3649 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3651 if (m_wxwindow
&& gtk_widget_get_window(m_wxwindow
))
3653 gdk_window_lower(gtk_widget_get_window(m_wxwindow
));
3655 else if (gtk_widget_get_window(m_widget
))
3657 gdk_window_lower(gtk_widget_get_window(m_widget
));
3661 bool wxWindowGTK::SetCursor( const wxCursor
&cursor
)
3663 if ( !wxWindowBase::SetCursor(cursor
.IsOk() ? cursor
: *wxSTANDARD_CURSOR
) )
3671 void wxWindowGTK::GTKUpdateCursor(bool update_self
/*=true*/, bool recurse
/*=true*/)
3675 wxCursor
cursor(g_globalCursor
.IsOk() ? g_globalCursor
: GetCursor());
3676 if ( cursor
.IsOk() )
3678 wxArrayGdkWindows windowsThis
;
3679 GdkWindow
* window
= GTKGetWindow(windowsThis
);
3681 gdk_window_set_cursor( window
, cursor
.GetCursor() );
3684 const size_t count
= windowsThis
.size();
3685 for ( size_t n
= 0; n
< count
; n
++ )
3687 GdkWindow
*win
= windowsThis
[n
];
3688 // It can be zero if the window has not been realized yet.
3691 gdk_window_set_cursor(win
, cursor
.GetCursor());
3700 for (wxWindowList::iterator it
= GetChildren().begin(); it
!= GetChildren().end(); ++it
)
3702 (*it
)->GTKUpdateCursor( true );
3707 void wxWindowGTK::WarpPointer( int x
, int y
)
3709 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3711 ClientToScreen(&x
, &y
);
3712 GdkDisplay
* display
= gtk_widget_get_display(m_widget
);
3713 GdkScreen
* screen
= gtk_widget_get_screen(m_widget
);
3715 GdkDeviceManager
* manager
= gdk_display_get_device_manager(display
);
3716 gdk_device_warp(gdk_device_manager_get_client_pointer(manager
), screen
, x
, y
);
3718 #ifdef GDK_WINDOWING_X11
3719 XWarpPointer(GDK_DISPLAY_XDISPLAY(display
),
3721 GDK_WINDOW_XID(gdk_screen_get_root_window(screen
)),
3727 wxWindowGTK::ScrollDir
wxWindowGTK::ScrollDirFromRange(GtkRange
*range
) const
3729 // find the scrollbar which generated the event
3730 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
3732 if ( range
== m_scrollBar
[dir
] )
3733 return (ScrollDir
)dir
;
3736 wxFAIL_MSG( wxT("event from unknown scrollbar received") );
3738 return ScrollDir_Max
;
3741 bool wxWindowGTK::DoScrollByUnits(ScrollDir dir
, ScrollUnit unit
, int units
)
3743 bool changed
= false;
3744 GtkRange
* range
= m_scrollBar
[dir
];
3745 if ( range
&& units
)
3747 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
3748 double inc
= unit
== ScrollUnit_Line
? gtk_adjustment_get_step_increment(adj
)
3749 : gtk_adjustment_get_page_increment(adj
);
3751 const int posOld
= wxRound(gtk_adjustment_get_value(adj
));
3752 gtk_range_set_value(range
, posOld
+ units
*inc
);
3754 changed
= wxRound(gtk_adjustment_get_value(adj
)) != posOld
;
3760 bool wxWindowGTK::ScrollLines(int lines
)
3762 return DoScrollByUnits(ScrollDir_Vert
, ScrollUnit_Line
, lines
);
3765 bool wxWindowGTK::ScrollPages(int pages
)
3767 return DoScrollByUnits(ScrollDir_Vert
, ScrollUnit_Page
, pages
);
3770 void wxWindowGTK::Refresh(bool WXUNUSED(eraseBackground
),
3775 if (gtk_widget_get_mapped(m_wxwindow
))
3777 GdkWindow
* window
= gtk_widget_get_window(m_wxwindow
);
3780 GdkRectangle r
= { rect
->x
, rect
->y
, rect
->width
, rect
->height
};
3781 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3782 r
.x
= gdk_window_get_width(window
) - r
.x
- rect
->width
;
3783 gdk_window_invalidate_rect(window
, &r
, true);
3786 gdk_window_invalidate_rect(window
, NULL
, true);
3791 if (gtk_widget_get_mapped(m_widget
))
3794 gtk_widget_queue_draw_area(m_widget
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
3796 gtk_widget_queue_draw(m_widget
);
3801 void wxWindowGTK::Update()
3803 if (m_widget
&& gtk_widget_get_mapped(m_widget
))
3805 GdkDisplay
* display
= gtk_widget_get_display(m_widget
);
3806 // Flush everything out to the server, and wait for it to finish.
3807 // This ensures nothing will overwrite the drawing we are about to do.
3808 gdk_display_sync(display
);
3810 GdkWindow
* window
= GTKGetDrawingWindow();
3812 window
= gtk_widget_get_window(m_widget
);
3813 gdk_window_process_updates(window
, true);
3815 // Flush again, but no need to wait for it to finish
3816 gdk_display_flush(display
);
3820 bool wxWindowGTK::DoIsExposed( int x
, int y
) const
3822 return m_updateRegion
.Contains(x
, y
) != wxOutRegion
;
3825 bool wxWindowGTK::DoIsExposed( int x
, int y
, int w
, int h
) const
3827 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3828 return m_updateRegion
.Contains(x
-w
, y
, w
, h
) != wxOutRegion
;
3830 return m_updateRegion
.Contains(x
, y
, w
, h
) != wxOutRegion
;
3834 void wxWindowGTK::GTKSendPaintEvents(cairo_t
* cr
)
3836 void wxWindowGTK::GTKSendPaintEvents(const GdkRegion
* region
)
3840 m_paintContext
= cr
;
3841 double x1
, y1
, x2
, y2
;
3842 cairo_clip_extents(cr
, &x1
, &y1
, &x2
, &y2
);
3843 m_updateRegion
= wxRegion(int(x1
), int(y1
), int(x2
- x1
), int(y2
- y1
));
3844 #else // !__WXGTK3__
3845 m_updateRegion
= wxRegion(region
);
3846 #if wxGTK_HAS_COMPOSITING_SUPPORT
3849 #endif // !__WXGTK3__
3850 // Clip to paint region in wxClientDC
3851 m_clipPaintRegion
= true;
3853 m_nativeUpdateRegion
= m_updateRegion
;
3855 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3857 // Transform m_updateRegion under RTL
3858 m_updateRegion
.Clear();
3860 const int width
= gdk_window_get_width(GTKGetDrawingWindow());
3862 wxRegionIterator
upd( m_nativeUpdateRegion
);
3866 rect
.x
= upd
.GetX();
3867 rect
.y
= upd
.GetY();
3868 rect
.width
= upd
.GetWidth();
3869 rect
.height
= upd
.GetHeight();
3871 rect
.x
= width
- rect
.x
- rect
.width
;
3872 m_updateRegion
.Union( rect
);
3878 switch ( GetBackgroundStyle() )
3880 case wxBG_STYLE_TRANSPARENT
:
3881 #if wxGTK_HAS_COMPOSITING_SUPPORT
3882 if (IsTransparentBackgroundSupported())
3884 // Set a transparent background, so that overlaying in parent
3885 // might indeed let see through where this child did not
3886 // explicitly paint.
3887 // NB: it works also for top level windows (but this is the
3888 // windows manager which then does the compositing job)
3890 cr
= gdk_cairo_create(m_wxwindow
->window
);
3891 gdk_cairo_region(cr
, m_nativeUpdateRegion
.GetRegion());
3894 cairo_set_operator(cr
, CAIRO_OPERATOR_CLEAR
);
3896 cairo_set_operator(cr
, CAIRO_OPERATOR_OVER
);
3898 cairo_surface_flush(cairo_get_target(cr
));
3901 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
3904 case wxBG_STYLE_ERASE
:
3907 wxGTKCairoDC
dc(cr
);
3909 wxWindowDC
dc( (wxWindow
*)this );
3910 dc
.SetDeviceClippingRegion( m_updateRegion
);
3912 // Work around gtk-qt <= 0.60 bug whereby the window colour
3916 GetOptionInt("gtk.window.force-background-colour") )
3918 dc
.SetBackground(GetBackgroundColour());
3921 #endif // !__WXGTK3__
3922 wxEraseEvent
erase_event( GetId(), &dc
);
3923 erase_event
.SetEventObject( this );
3925 if ( HandleWindowEvent(erase_event
) )
3927 // background erased, don't do it again
3933 case wxBG_STYLE_SYSTEM
:
3934 if ( GetThemeEnabled() )
3936 GdkWindow
* gdkWindow
= GTKGetDrawingWindow();
3937 const int w
= gdk_window_get_width(gdkWindow
);
3938 const int h
= gdk_window_get_height(gdkWindow
);
3940 GtkStyleContext
* sc
= gtk_widget_get_style_context(m_wxwindow
);
3941 gtk_render_background(sc
, cr
, 0, 0, w
, h
);
3943 // find ancestor from which to steal background
3944 wxWindow
*parent
= wxGetTopLevelParent((wxWindow
*)this);
3946 parent
= (wxWindow
*)this;
3948 m_nativeUpdateRegion
.GetBox(rect
.x
, rect
.y
, rect
.width
, rect
.height
);
3949 gtk_paint_flat_box(gtk_widget_get_style(parent
->m_widget
),
3951 gtk_widget_get_state(m_wxwindow
),
3957 #endif // !__WXGTK3__
3961 case wxBG_STYLE_PAINT
:
3962 // nothing to do: window will be painted over in EVT_PAINT
3966 wxFAIL_MSG( "unsupported background style" );
3969 wxNcPaintEvent
nc_paint_event( GetId() );
3970 nc_paint_event
.SetEventObject( this );
3971 HandleWindowEvent( nc_paint_event
);
3973 wxPaintEvent
paint_event( GetId() );
3974 paint_event
.SetEventObject( this );
3975 HandleWindowEvent( paint_event
);
3977 #if wxGTK_HAS_COMPOSITING_SUPPORT
3978 if (IsTransparentBackgroundSupported())
3979 { // now composite children which need it
3980 // Overlay all our composite children on top of the painted area
3981 wxWindowList::compatibility_iterator node
;
3982 for ( node
= m_children
.GetFirst(); node
; node
= node
->GetNext() )
3984 wxWindow
*compositeChild
= node
->GetData();
3985 if (compositeChild
->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT
)
3990 cr
= gdk_cairo_create(m_wxwindow
->window
);
3991 gdk_cairo_region(cr
, m_nativeUpdateRegion
.GetRegion());
3994 #endif // !__WXGTK3__
3995 GtkWidget
*child
= compositeChild
->m_wxwindow
;
3996 GtkAllocation alloc
;
3997 gtk_widget_get_allocation(child
, &alloc
);
3999 // The source data is the (composited) child
4000 gdk_cairo_set_source_window(
4001 cr
, gtk_widget_get_window(child
), alloc
.x
, alloc
.y
);
4011 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
4013 m_clipPaintRegion
= false;
4015 m_paintContext
= NULL
;
4017 m_updateRegion
.Clear();
4018 m_nativeUpdateRegion
.Clear();
4021 void wxWindowGTK::SetDoubleBuffered( bool on
)
4023 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
4026 gtk_widget_set_double_buffered( m_wxwindow
, on
);
4029 bool wxWindowGTK::IsDoubleBuffered() const
4031 return gtk_widget_get_double_buffered( m_wxwindow
) != 0;
4034 void wxWindowGTK::ClearBackground()
4036 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4040 void wxWindowGTK::DoSetToolTip( wxToolTip
*tip
)
4042 if (m_tooltip
!= tip
)
4044 wxWindowBase::DoSetToolTip(tip
);
4047 m_tooltip
->GTKSetWindow(static_cast<wxWindow
*>(this));
4049 GTKApplyToolTip(NULL
);
4053 void wxWindowGTK::GTKApplyToolTip(const char* tip
)
4055 wxToolTip::GTKApply(GetConnectWidget(), tip
);
4057 #endif // wxUSE_TOOLTIPS
4059 bool wxWindowGTK::SetBackgroundColour( const wxColour
&colour
)
4061 if (!wxWindowBase::SetBackgroundColour(colour
))
4069 // We need the pixel value e.g. for background clearing.
4070 m_backgroundColour
.CalcPixel(gtk_widget_get_colormap(m_widget
));
4074 // apply style change (forceStyle=true so that new style is applied
4075 // even if the bg colour changed from valid to wxNullColour)
4076 GTKApplyWidgetStyle(true);
4082 bool wxWindowGTK::SetForegroundColour( const wxColour
&colour
)
4084 if (!wxWindowBase::SetForegroundColour(colour
))
4092 // We need the pixel value e.g. for background clearing.
4093 m_foregroundColour
.CalcPixel(gtk_widget_get_colormap(m_widget
));
4097 // apply style change (forceStyle=true so that new style is applied
4098 // even if the bg colour changed from valid to wxNullColour):
4099 GTKApplyWidgetStyle(true);
4105 PangoContext
*wxWindowGTK::GTKGetPangoDefaultContext()
4107 return gtk_widget_get_pango_context( m_widget
);
4111 GtkRcStyle
* wxWindowGTK::GTKCreateWidgetStyle()
4113 GtkRcStyle
*style
= gtk_rc_style_new();
4115 if ( m_font
.IsOk() )
4118 pango_font_description_copy( m_font
.GetNativeFontInfo()->description
);
4121 int flagsNormal
= 0,
4124 flagsInsensitive
= 0;
4126 if ( m_foregroundColour
.IsOk() )
4128 const GdkColor
*fg
= m_foregroundColour
.GetColor();
4130 style
->fg
[GTK_STATE_NORMAL
] =
4131 style
->text
[GTK_STATE_NORMAL
] = *fg
;
4132 flagsNormal
|= GTK_RC_FG
| GTK_RC_TEXT
;
4134 style
->fg
[GTK_STATE_PRELIGHT
] =
4135 style
->text
[GTK_STATE_PRELIGHT
] = *fg
;
4136 flagsPrelight
|= GTK_RC_FG
| GTK_RC_TEXT
;
4138 style
->fg
[GTK_STATE_ACTIVE
] =
4139 style
->text
[GTK_STATE_ACTIVE
] = *fg
;
4140 flagsActive
|= GTK_RC_FG
| GTK_RC_TEXT
;
4143 if ( m_backgroundColour
.IsOk() )
4145 const GdkColor
*bg
= m_backgroundColour
.GetColor();
4147 style
->bg
[GTK_STATE_NORMAL
] =
4148 style
->base
[GTK_STATE_NORMAL
] = *bg
;
4149 flagsNormal
|= GTK_RC_BG
| GTK_RC_BASE
;
4151 style
->bg
[GTK_STATE_PRELIGHT
] =
4152 style
->base
[GTK_STATE_PRELIGHT
] = *bg
;
4153 flagsPrelight
|= GTK_RC_BG
| GTK_RC_BASE
;
4155 style
->bg
[GTK_STATE_ACTIVE
] =
4156 style
->base
[GTK_STATE_ACTIVE
] = *bg
;
4157 flagsActive
|= GTK_RC_BG
| GTK_RC_BASE
;
4159 style
->bg
[GTK_STATE_INSENSITIVE
] =
4160 style
->base
[GTK_STATE_INSENSITIVE
] = *bg
;
4161 flagsInsensitive
|= GTK_RC_BG
| GTK_RC_BASE
;
4164 style
->color_flags
[GTK_STATE_NORMAL
] = (GtkRcFlags
)flagsNormal
;
4165 style
->color_flags
[GTK_STATE_PRELIGHT
] = (GtkRcFlags
)flagsPrelight
;
4166 style
->color_flags
[GTK_STATE_ACTIVE
] = (GtkRcFlags
)flagsActive
;
4167 style
->color_flags
[GTK_STATE_INSENSITIVE
] = (GtkRcFlags
)flagsInsensitive
;
4171 #endif // !__WXGTK3__
4173 void wxWindowGTK::GTKApplyWidgetStyle(bool forceStyle
)
4175 if (forceStyle
|| m_font
.IsOk() ||
4176 m_foregroundColour
.IsOk() || m_backgroundColour
.IsOk())
4179 if (m_backgroundColour
.IsOk())
4181 // create a GtkStyleProvider to override "background-image"
4182 if (m_styleProvider
== NULL
)
4183 m_styleProvider
= GTK_STYLE_PROVIDER(gtk_css_provider_new());
4185 "*{background-image:-gtk-gradient(linear,0 0,0 1,"
4186 "from(rgba(%u,%u,%u,%g)),to(rgba(%u,%u,%u,%g)))}";
4187 char buf
[sizeof(css
) + 20];
4188 const unsigned r
= m_backgroundColour
.Red();
4189 const unsigned g
= m_backgroundColour
.Green();
4190 const unsigned b
= m_backgroundColour
.Blue();
4191 const double a
= m_backgroundColour
.Alpha() / 255.0;
4192 g_snprintf(buf
, sizeof(buf
), css
, r
, g
, b
, a
, r
, g
, b
, a
);
4193 gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(m_styleProvider
), buf
, -1, NULL
);
4195 DoApplyWidgetStyle(NULL
);
4197 GtkRcStyle
* style
= GTKCreateWidgetStyle();
4198 DoApplyWidgetStyle(style
);
4199 g_object_unref(style
);
4203 // Style change may affect GTK+'s size calculation:
4204 InvalidateBestSize();
4207 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle
*style
)
4209 GtkWidget
* widget
= m_wxwindow
? m_wxwindow
: m_widget
;
4211 // block the signal temporarily to avoid sending
4212 // wxSysColourChangedEvents when we change the colours ourselves
4213 bool unblock
= false;
4214 if (m_wxwindow
&& IsTopLevel())
4217 g_signal_handlers_block_by_func(
4218 m_wxwindow
, (void*)style_updated
, this);
4221 GTKApplyStyle(widget
, style
);
4225 g_signal_handlers_unblock_by_func(
4226 m_wxwindow
, (void*)style_updated
, this);
4230 void wxWindowGTK::GTKApplyStyle(GtkWidget
* widget
, GtkRcStyle
* WXUNUSED_IN_GTK3(style
))
4233 const PangoFontDescription
* pfd
= NULL
;
4235 pfd
= pango_font_description_copy(m_font
.GetNativeFontInfo()->description
);
4236 gtk_widget_override_font(widget
, pfd
);
4237 gtk_widget_override_color(widget
, GTK_STATE_FLAG_NORMAL
, m_foregroundColour
);
4238 gtk_widget_override_background_color(widget
, GTK_STATE_FLAG_NORMAL
, m_backgroundColour
);
4240 // setting background color has no effect with some themes when the widget style
4241 // has a "background-image" property, so we need to override that as well
4243 GtkStyleContext
* context
= gtk_widget_get_style_context(widget
);
4244 if (m_styleProvider
)
4245 gtk_style_context_remove_provider(context
, m_styleProvider
);
4246 cairo_pattern_t
* pattern
= NULL
;
4247 if (m_backgroundColour
.IsOk())
4249 gtk_style_context_get(context
,
4250 GTK_STATE_FLAG_NORMAL
, "background-image", &pattern
, NULL
);
4254 cairo_pattern_destroy(pattern
);
4255 gtk_style_context_add_provider(context
,
4256 m_styleProvider
, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
4259 gtk_widget_modify_style(widget
, style
);
4263 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style
)
4265 if (!wxWindowBase::SetBackgroundStyle(style
))
4270 if ((style
== wxBG_STYLE_PAINT
|| style
== wxBG_STYLE_TRANSPARENT
) &&
4271 (window
= GTKGetDrawingWindow()))
4273 gdk_window_set_back_pixmap(window
, NULL
, false);
4275 #endif // !__WXGTK3__
4280 bool wxWindowGTK::IsTransparentBackgroundSupported(wxString
* reason
) const
4282 #if wxGTK_HAS_COMPOSITING_SUPPORT
4284 if (gtk_check_version(wxGTK_VERSION_REQUIRED_FOR_COMPOSITING
) != NULL
)
4288 *reason
= _("GTK+ installed on this machine is too old to "
4289 "support screen compositing, please install "
4290 "GTK+ 2.12 or later.");
4295 #endif // !__WXGTK3__
4297 // NB: We don't check here if the particular kind of widget supports
4298 // transparency, we check only if it would be possible for a generic window
4300 wxCHECK_MSG ( m_widget
, false, "Window must be created first" );
4302 if (!gdk_screen_is_composited(gtk_widget_get_screen(m_widget
)))
4306 *reason
= _("Compositing not supported by this system, "
4307 "please enable it in your Window Manager.");
4317 *reason
= _("This program was compiled with a too old version of GTK+, "
4318 "please rebuild with GTK+ 2.12 or newer.");
4322 #endif // wxGTK_HAS_COMPOSITING_SUPPORT/!wxGTK_HAS_COMPOSITING_SUPPORT
4325 // ----------------------------------------------------------------------------
4326 // Pop-up menu stuff
4327 // ----------------------------------------------------------------------------
4329 #if wxUSE_MENUS_NATIVE
4333 void wxPopupMenuPositionCallback( GtkMenu
*menu
,
4335 gboolean
* WXUNUSED(whatever
),
4336 gpointer user_data
)
4338 // ensure that the menu appears entirely on screen
4341 gtk_widget_get_preferred_size(GTK_WIDGET(menu
), &req
, NULL
);
4343 gtk_widget_get_child_requisition(GTK_WIDGET(menu
), &req
);
4346 wxSize sizeScreen
= wxGetDisplaySize();
4347 wxPoint
*pos
= (wxPoint
*)user_data
;
4349 gint xmax
= sizeScreen
.x
- req
.width
,
4350 ymax
= sizeScreen
.y
- req
.height
;
4352 *x
= pos
->x
< xmax
? pos
->x
: xmax
;
4353 *y
= pos
->y
< ymax
? pos
->y
: ymax
;
4357 bool wxWindowGTK::DoPopupMenu( wxMenu
*menu
, int x
, int y
)
4359 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
4365 GtkMenuPositionFunc posfunc
;
4366 if ( x
== -1 && y
== -1 )
4368 // use GTK's default positioning algorithm
4374 pos
= ClientToScreen(wxPoint(x
, y
));
4376 posfunc
= wxPopupMenuPositionCallback
;
4379 menu
->m_popupShown
= true;
4381 GTK_MENU(menu
->m_menu
),
4382 NULL
, // parent menu shell
4383 NULL
, // parent menu item
4384 posfunc
, // function to position it
4385 userdata
, // client data
4386 0, // button used to activate it
4387 gtk_get_current_event_time()
4390 // it is possible for gtk_menu_popup() to fail
4391 if (!gtk_widget_get_visible(GTK_WIDGET(menu
->m_menu
)))
4393 menu
->m_popupShown
= false;
4397 while (menu
->m_popupShown
)
4399 gtk_main_iteration();
4405 #endif // wxUSE_MENUS_NATIVE
4407 #if wxUSE_DRAG_AND_DROP
4409 void wxWindowGTK::SetDropTarget( wxDropTarget
*dropTarget
)
4411 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4413 GtkWidget
*dnd_widget
= GetConnectWidget();
4415 if (m_dropTarget
) m_dropTarget
->GtkUnregisterWidget( dnd_widget
);
4417 if (m_dropTarget
) delete m_dropTarget
;
4418 m_dropTarget
= dropTarget
;
4420 if (m_dropTarget
) m_dropTarget
->GtkRegisterWidget( dnd_widget
);
4423 #endif // wxUSE_DRAG_AND_DROP
4425 GtkWidget
* wxWindowGTK::GetConnectWidget()
4427 GtkWidget
*connect_widget
= m_widget
;
4428 if (m_wxwindow
) connect_widget
= m_wxwindow
;
4430 return connect_widget
;
4433 bool wxWindowGTK::GTKIsOwnWindow(GdkWindow
*window
) const
4435 wxArrayGdkWindows windowsThis
;
4436 GdkWindow
* const winThis
= GTKGetWindow(windowsThis
);
4438 return winThis
? window
== winThis
4439 : windowsThis
.Index(window
) != wxNOT_FOUND
;
4442 GdkWindow
*wxWindowGTK::GTKGetWindow(wxArrayGdkWindows
& WXUNUSED(windows
)) const
4444 return m_wxwindow
? GTKGetDrawingWindow() : gtk_widget_get_window(m_widget
);
4447 bool wxWindowGTK::SetFont( const wxFont
&font
)
4449 if (!wxWindowBase::SetFont(font
))
4454 // apply style change (forceStyle=true so that new style is applied
4455 // even if the font changed from valid to wxNullFont):
4456 GTKApplyWidgetStyle(true);
4462 void wxWindowGTK::DoCaptureMouse()
4464 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4466 GdkWindow
*window
= NULL
;
4468 window
= GTKGetDrawingWindow();
4470 window
= gtk_widget_get_window(GetConnectWidget());
4472 wxCHECK_RET( window
, wxT("CaptureMouse() failed") );
4474 const wxCursor
* cursor
= &m_cursor
;
4475 if (!cursor
->IsOk())
4476 cursor
= wxSTANDARD_CURSOR
;
4478 const GdkEventMask mask
= GdkEventMask(
4479 GDK_BUTTON_PRESS_MASK
|
4480 GDK_BUTTON_RELEASE_MASK
|
4481 GDK_POINTER_MOTION_HINT_MASK
|
4482 GDK_POINTER_MOTION_MASK
);
4484 GdkDisplay
* display
= gdk_window_get_display(window
);
4485 GdkDeviceManager
* manager
= gdk_display_get_device_manager(display
);
4486 GdkDevice
* device
= gdk_device_manager_get_client_pointer(manager
);
4488 device
, window
, GDK_OWNERSHIP_NONE
, false, mask
,
4489 cursor
->GetCursor(), unsigned(GDK_CURRENT_TIME
));
4491 gdk_pointer_grab( window
, FALSE
,
4494 cursor
->GetCursor(),
4495 (guint32
)GDK_CURRENT_TIME
);
4497 g_captureWindow
= this;
4498 g_captureWindowHasMouse
= true;
4501 void wxWindowGTK::DoReleaseMouse()
4503 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4505 wxCHECK_RET( g_captureWindow
, wxT("can't release mouse - not captured") );
4507 g_captureWindow
= NULL
;
4509 GdkWindow
*window
= NULL
;
4511 window
= GTKGetDrawingWindow();
4513 window
= gtk_widget_get_window(GetConnectWidget());
4519 GdkDisplay
* display
= gdk_window_get_display(window
);
4520 GdkDeviceManager
* manager
= gdk_display_get_device_manager(display
);
4521 GdkDevice
* device
= gdk_device_manager_get_client_pointer(manager
);
4522 gdk_device_ungrab(device
, unsigned(GDK_CURRENT_TIME
));
4524 gdk_pointer_ungrab ( (guint32
)GDK_CURRENT_TIME
);
4528 void wxWindowGTK::GTKReleaseMouseAndNotify()
4531 wxMouseCaptureLostEvent
evt(GetId());
4532 evt
.SetEventObject( this );
4533 HandleWindowEvent( evt
);
4537 wxWindow
*wxWindowBase::GetCapture()
4539 return (wxWindow
*)g_captureWindow
;
4542 bool wxWindowGTK::IsRetained() const
4547 void wxWindowGTK::SetScrollbar(int orient
,
4551 bool WXUNUSED(update
))
4553 const int dir
= ScrollDirFromOrient(orient
);
4554 GtkRange
* const sb
= m_scrollBar
[dir
];
4555 wxCHECK_RET( sb
, wxT("this window is not scrollable") );
4559 // GtkRange requires upper > lower
4564 g_signal_handlers_block_by_func(
4565 sb
, (void*)gtk_scrollbar_value_changed
, this);
4567 gtk_range_set_increments(sb
, 1, thumbVisible
);
4568 gtk_adjustment_set_page_size(gtk_range_get_adjustment(sb
), thumbVisible
);
4569 gtk_range_set_range(sb
, 0, range
);
4570 gtk_range_set_value(sb
, pos
);
4571 m_scrollPos
[dir
] = gtk_range_get_value(sb
);
4573 g_signal_handlers_unblock_by_func(
4574 sb
, (void*)gtk_scrollbar_value_changed
, this);
4577 void wxWindowGTK::SetScrollPos(int orient
, int pos
, bool WXUNUSED(refresh
))
4579 const int dir
= ScrollDirFromOrient(orient
);
4580 GtkRange
* const sb
= m_scrollBar
[dir
];
4581 wxCHECK_RET( sb
, wxT("this window is not scrollable") );
4583 // This check is more than an optimization. Without it, the slider
4584 // will not move smoothly while tracking when using wxScrollHelper.
4585 if (GetScrollPos(orient
) != pos
)
4587 g_signal_handlers_block_by_func(
4588 sb
, (void*)gtk_scrollbar_value_changed
, this);
4590 gtk_range_set_value(sb
, pos
);
4591 m_scrollPos
[dir
] = gtk_range_get_value(sb
);
4593 g_signal_handlers_unblock_by_func(
4594 sb
, (void*)gtk_scrollbar_value_changed
, this);
4598 int wxWindowGTK::GetScrollThumb(int orient
) const
4600 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4601 wxCHECK_MSG( sb
, 0, wxT("this window is not scrollable") );
4603 return wxRound(gtk_adjustment_get_page_size(gtk_range_get_adjustment(sb
)));
4606 int wxWindowGTK::GetScrollPos( int orient
) const
4608 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4609 wxCHECK_MSG( sb
, 0, wxT("this window is not scrollable") );
4611 return wxRound(gtk_range_get_value(sb
));
4614 int wxWindowGTK::GetScrollRange( int orient
) const
4616 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4617 wxCHECK_MSG( sb
, 0, wxT("this window is not scrollable") );
4619 return wxRound(gtk_adjustment_get_upper(gtk_range_get_adjustment(sb
)));
4622 // Determine if increment is the same as +/-x, allowing for some small
4623 // difference due to possible inexactness in floating point arithmetic
4624 static inline bool IsScrollIncrement(double increment
, double x
)
4626 wxASSERT(increment
>= 0);
4627 if ( increment
== 0. )
4629 const double tolerance
= 1.0 / 1024;
4630 return fabs(increment
- fabs(x
)) < tolerance
;
4633 wxEventType
wxWindowGTK::GTKGetScrollEventType(GtkRange
* range
)
4635 wxASSERT(range
== m_scrollBar
[0] || range
== m_scrollBar
[1]);
4637 const int barIndex
= range
== m_scrollBar
[1];
4639 const double value
= gtk_range_get_value(range
);
4641 // save previous position
4642 const double oldPos
= m_scrollPos
[barIndex
];
4643 // update current position
4644 m_scrollPos
[barIndex
] = value
;
4645 // If event should be ignored, or integral position has not changed
4646 if (g_blockEventsOnDrag
|| wxRound(value
) == wxRound(oldPos
))
4651 wxEventType eventType
= wxEVT_SCROLL_THUMBTRACK
;
4654 // Difference from last change event
4655 const double diff
= value
- oldPos
;
4656 const bool isDown
= diff
> 0;
4658 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
4659 if (IsScrollIncrement(gtk_adjustment_get_step_increment(adj
), diff
))
4661 eventType
= isDown
? wxEVT_SCROLL_LINEDOWN
: wxEVT_SCROLL_LINEUP
;
4663 else if (IsScrollIncrement(gtk_adjustment_get_page_increment(adj
), diff
))
4665 eventType
= isDown
? wxEVT_SCROLL_PAGEDOWN
: wxEVT_SCROLL_PAGEUP
;
4667 else if (m_mouseButtonDown
)
4669 // Assume track event
4670 m_isScrolling
= true;
4676 void wxWindowGTK::ScrollWindow( int dx
, int dy
, const wxRect
* WXUNUSED(rect
) )
4678 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4680 wxCHECK_RET( m_wxwindow
!= NULL
, wxT("window needs client area for scrolling") );
4682 // No scrolling requested.
4683 if ((dx
== 0) && (dy
== 0)) return;
4685 m_clipPaintRegion
= true;
4687 WX_PIZZA(m_wxwindow
)->scroll(dx
, dy
);
4689 m_clipPaintRegion
= false;
4692 bool restoreCaret
= (GetCaret() != NULL
&& GetCaret()->IsVisible());
4695 wxRect
caretRect(GetCaret()->GetPosition(), GetCaret()->GetSize());
4697 caretRect
.width
+= dx
;
4700 caretRect
.x
+= dx
; caretRect
.width
-= dx
;
4703 caretRect
.height
+= dy
;
4706 caretRect
.y
+= dy
; caretRect
.height
-= dy
;
4709 RefreshRect(caretRect
);
4711 #endif // wxUSE_CARET
4714 void wxWindowGTK::GTKScrolledWindowSetBorder(GtkWidget
* w
, int wxstyle
)
4716 //RN: Note that static controls usually have no border on gtk, so maybe
4717 //it makes sense to treat that as simply no border at the wx level
4719 if (!(wxstyle
& wxNO_BORDER
) && !(wxstyle
& wxBORDER_STATIC
))
4721 GtkShadowType gtkstyle
;
4723 if(wxstyle
& wxBORDER_RAISED
)
4724 gtkstyle
= GTK_SHADOW_OUT
;
4725 else if ((wxstyle
& wxBORDER_SUNKEN
) || (wxstyle
& wxBORDER_THEME
))
4726 gtkstyle
= GTK_SHADOW_IN
;
4729 else if (wxstyle
& wxBORDER_DOUBLE
)
4730 gtkstyle
= GTK_SHADOW_ETCHED_IN
;
4733 gtkstyle
= GTK_SHADOW_IN
;
4735 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w
),
4740 // Find the wxWindow at the current mouse position, also returning the mouse
4742 wxWindow
* wxFindWindowAtPointer(wxPoint
& pt
)
4744 pt
= wxGetMousePosition();
4745 wxWindow
* found
= wxFindWindowAtPoint(pt
);
4749 // Get the current mouse position.
4750 void wxGetMousePosition(int* x
, int* y
)
4752 GdkDisplay
* display
= GetDisplay();
4754 GdkDeviceManager
* manager
= gdk_display_get_device_manager(display
);
4755 GdkDevice
* device
= gdk_device_manager_get_client_pointer(manager
);
4756 gdk_device_get_position(device
, NULL
, x
, y
);
4758 gdk_display_get_pointer(display
, NULL
, x
, y
, NULL
);
4762 wxPoint
wxGetMousePosition()
4765 wxGetMousePosition(&pt
.x
, &pt
.y
);
4769 GdkWindow
* wxWindowGTK::GTKGetDrawingWindow() const
4771 GdkWindow
* window
= NULL
;
4773 window
= gtk_widget_get_window(m_wxwindow
);
4777 // ----------------------------------------------------------------------------
4779 // ----------------------------------------------------------------------------
4781 void wxWindowGTK::GTKFreezeWidget(GtkWidget
* widget
)
4783 if (widget
&& gtk_widget_get_has_window(widget
))
4785 GdkWindow
* window
= gtk_widget_get_window(widget
);
4787 gdk_window_freeze_updates(window
);
4791 void wxWindowGTK::GTKThawWidget(GtkWidget
* widget
)
4793 if (widget
&& gtk_widget_get_has_window(widget
))
4795 GdkWindow
* window
= gtk_widget_get_window(widget
);
4797 gdk_window_thaw_updates(window
);
4801 void wxWindowGTK::DoFreeze()
4803 GtkWidget
* widget
= m_wxwindow
;
4806 GTKFreezeWidget(widget
);
4809 void wxWindowGTK::DoThaw()
4811 GtkWidget
* widget
= m_wxwindow
;
4814 GTKThawWidget(widget
);