1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/window.cpp
3 // Purpose: wxWindowGTK implementation
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling, Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
14 #define XWarpPointer XWARPPOINTER
17 #include "wx/window.h"
22 #include "wx/toplevel.h"
23 #include "wx/dcclient.h"
25 #include "wx/settings.h"
26 #include "wx/msgdlg.h"
31 #include "wx/tooltip.h"
33 #include "wx/fontutil.h"
34 #include "wx/sysopt.h"
36 #include "wx/gtk/dc.h"
42 #include "wx/gtk/private.h"
43 #include "wx/gtk/private/gtk2-compat.h"
44 #include "wx/gtk/private/event.h"
45 #include "wx/gtk/private/win_gtk.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 = %ld"),
758 event
.GetEventType() == wxEVT_KEY_UP
? wxT("release")
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
? key_code
: keysym
);
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
);
863 GtkIMContext
*context
;
864 GdkEventKey
*lastKeyEvent
;
868 context
= gtk_im_multicontext_new();
873 g_object_unref (context
);
880 // Send wxEVT_CHAR_HOOK event to the parent of the window and return true only
881 // if it was processed (and not skipped).
882 bool SendCharHookEvent(const wxKeyEvent
& event
, wxWindow
*win
)
884 // wxEVT_CHAR_HOOK must be sent to allow the parent windows (e.g. a dialog
885 // which typically closes when Esc key is pressed in any of its controls)
886 // to handle key events in all of its children unless the mouse is captured
887 // in which case we consider that the keyboard should be "captured" too.
888 if ( !g_captureWindow
)
890 wxKeyEvent
eventCharHook(wxEVT_CHAR_HOOK
, event
);
891 if ( win
->HandleWindowEvent(eventCharHook
)
892 && !event
.IsNextEventAllowed() )
899 // Adjust wxEVT_CHAR event key code fields. This function takes care of two
901 // (a) Ctrl-letter key presses generate key codes in range 1..26
902 // (b) Unicode key codes are same as key codes for the codes in 1..255 range
903 void AdjustCharEventKeyCodes(wxKeyEvent
& event
)
905 const int code
= event
.m_keyCode
;
907 // Check for (a) above.
908 if ( event
.ControlDown() )
910 // We intentionally don't use isupper/lower() here, we really need
911 // ASCII letters only as it doesn't make sense to translate any other
912 // ones into this range which has only 26 slots.
913 if ( code
>= 'a' && code
<= 'z' )
914 event
.m_keyCode
= code
- 'a' + 1;
915 else if ( code
>= 'A' && code
<= 'Z' )
916 event
.m_keyCode
= code
- 'A' + 1;
919 // Adjust the Unicode equivalent in the same way too.
920 if ( event
.m_keyCode
!= code
)
921 event
.m_uniChar
= event
.m_keyCode
;
922 #endif // wxUSE_UNICODE
926 // Check for (b) from above.
928 // FIXME: Should we do it for key codes up to 255?
929 if ( !event
.m_uniChar
&& code
< WXK_DELETE
)
930 event
.m_uniChar
= code
;
931 #endif // wxUSE_UNICODE
934 } // anonymous namespace
936 // If a widget does not handle a key or mouse event, GTK+ sends it up the
937 // parent chain until it is handled. These events are not supposed to propagate
938 // in wxWidgets, so this code avoids handling them in any parent wxWindow,
939 // while still allowing the event to propagate so things like native keyboard
940 // navigation will work.
941 #define wxPROCESS_EVENT_ONCE(EventType, event) \
942 static EventType eventPrev; \
943 if (memcmp(&eventPrev, event, sizeof(EventType)) == 0) \
949 gtk_window_key_press_callback( GtkWidget
*WXUNUSED(widget
),
950 GdkEventKey
*gdk_event
,
955 if (g_blockEventsOnDrag
)
958 wxPROCESS_EVENT_ONCE(GdkEventKey
, gdk_event
);
960 wxKeyEvent
event( wxEVT_KEY_DOWN
);
962 bool return_after_IM
= false;
964 if( wxTranslateGTKKeyEventToWx(event
, win
, gdk_event
) )
966 // Send the CHAR_HOOK event first
967 if ( SendCharHookEvent(event
, win
) )
969 // Don't do anything at all with this event any more.
973 // Next check for accelerators.
975 wxWindowGTK
*ancestor
= win
;
978 int command
= ancestor
->GetAcceleratorTable()->GetCommand( event
);
981 wxCommandEvent
menu_event( wxEVT_COMMAND_MENU_SELECTED
, command
);
982 ret
= ancestor
->HandleWindowEvent( menu_event
);
986 // if the accelerator wasn't handled as menu event, try
987 // it as button click (for compatibility with other
989 wxCommandEvent
button_event( wxEVT_COMMAND_BUTTON_CLICKED
, command
);
990 ret
= ancestor
->HandleWindowEvent( button_event
);
995 if (ancestor
->IsTopLevel())
997 ancestor
= ancestor
->GetParent();
999 #endif // wxUSE_ACCEL
1001 // If not an accelerator, then emit KEY_DOWN event
1003 ret
= win
->HandleWindowEvent( event
);
1007 // Return after IM processing as we cannot do
1008 // anything with it anyhow.
1009 return_after_IM
= true;
1012 if (!ret
&& win
->m_imData
)
1014 win
->m_imData
->lastKeyEvent
= gdk_event
;
1016 // We should let GTK+ IM filter key event first. According to GTK+ 2.0 API
1017 // docs, if IM filter returns true, no further processing should be done.
1018 // we should send the key_down event anyway.
1019 bool intercepted_by_IM
=
1020 gtk_im_context_filter_keypress(win
->m_imData
->context
, gdk_event
) != 0;
1021 win
->m_imData
->lastKeyEvent
= NULL
;
1022 if (intercepted_by_IM
)
1024 wxLogTrace(TRACE_KEYS
, wxT("Key event intercepted by IM"));
1029 if (return_after_IM
)
1032 // Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
1033 // will only be sent if it is not in an accelerator table.
1036 KeySym keysym
= gdk_event
->keyval
;
1037 // Find key code for EVT_CHAR and EVT_CHAR_HOOK events
1038 long key_code
= wxTranslateKeySymToWXKey(keysym
, true /* isChar */);
1041 if ( wxIsAsciiKeysym(keysym
) )
1044 key_code
= (unsigned char)keysym
;
1046 // gdk_event->string is actually deprecated
1047 else if ( gdk_event
->length
== 1 )
1049 key_code
= (unsigned char)gdk_event
->string
[0];
1055 wxKeyEvent
eventChar(wxEVT_CHAR
, event
);
1057 wxLogTrace(TRACE_KEYS
, wxT("Char event: %ld"), key_code
);
1059 eventChar
.m_keyCode
= key_code
;
1061 eventChar
.m_uniChar
= gdk_keyval_to_unicode(key_code
);
1062 #endif // wxUSE_UNICODE
1064 AdjustCharEventKeyCodes(eventChar
);
1066 ret
= win
->HandleWindowEvent(eventChar
);
1076 gtk_wxwindow_commit_cb (GtkIMContext
* WXUNUSED(context
),
1080 wxKeyEvent
event( wxEVT_CHAR
);
1082 // take modifiers, cursor position, timestamp etc. from the last
1083 // key_press_event that was fed into Input Method:
1084 if (window
->m_imData
->lastKeyEvent
)
1086 wxFillOtherKeyEventFields(event
,
1087 window
, window
->m_imData
->lastKeyEvent
);
1091 event
.SetEventObject( window
);
1094 const wxString
data(wxGTK_CONV_BACK_SYS(str
));
1098 for( wxString::const_iterator pstr
= data
.begin(); pstr
!= data
.end(); ++pstr
)
1101 event
.m_uniChar
= *pstr
;
1102 // Backward compatible for ISO-8859-1
1103 event
.m_keyCode
= *pstr
< 256 ? event
.m_uniChar
: 0;
1104 wxLogTrace(TRACE_KEYS
, wxT("IM sent character '%c'"), event
.m_uniChar
);
1106 event
.m_keyCode
= (char)*pstr
;
1107 #endif // wxUSE_UNICODE
1109 AdjustCharEventKeyCodes(event
);
1111 window
->HandleWindowEvent(event
);
1117 //-----------------------------------------------------------------------------
1118 // "key_release_event" from any window
1119 //-----------------------------------------------------------------------------
1123 gtk_window_key_release_callback( GtkWidget
* WXUNUSED(widget
),
1124 GdkEventKey
*gdk_event
,
1130 if (g_blockEventsOnDrag
)
1133 wxPROCESS_EVENT_ONCE(GdkEventKey
, gdk_event
);
1135 wxKeyEvent
event( wxEVT_KEY_UP
);
1136 if ( !wxTranslateGTKKeyEventToWx(event
, win
, gdk_event
) )
1138 // unknown key pressed, ignore (the event would be useless anyhow)
1142 return win
->GTKProcessEvent(event
);
1146 // ============================================================================
1148 // ============================================================================
1150 // ----------------------------------------------------------------------------
1151 // mouse event processing helpers
1152 // ----------------------------------------------------------------------------
1154 static void AdjustEventButtonState(wxMouseEvent
& event
)
1156 // GDK reports the old state of the button for a button press event, but
1157 // for compatibility with MSW and common sense we want m_leftDown be TRUE
1158 // for a LEFT_DOWN event, not FALSE, so we will invert
1159 // left/right/middleDown for the corresponding click events
1161 if ((event
.GetEventType() == wxEVT_LEFT_DOWN
) ||
1162 (event
.GetEventType() == wxEVT_LEFT_DCLICK
) ||
1163 (event
.GetEventType() == wxEVT_LEFT_UP
))
1165 event
.m_leftDown
= !event
.m_leftDown
;
1169 if ((event
.GetEventType() == wxEVT_MIDDLE_DOWN
) ||
1170 (event
.GetEventType() == wxEVT_MIDDLE_DCLICK
) ||
1171 (event
.GetEventType() == wxEVT_MIDDLE_UP
))
1173 event
.m_middleDown
= !event
.m_middleDown
;
1177 if ((event
.GetEventType() == wxEVT_RIGHT_DOWN
) ||
1178 (event
.GetEventType() == wxEVT_RIGHT_DCLICK
) ||
1179 (event
.GetEventType() == wxEVT_RIGHT_UP
))
1181 event
.m_rightDown
= !event
.m_rightDown
;
1185 if ((event
.GetEventType() == wxEVT_AUX1_DOWN
) ||
1186 (event
.GetEventType() == wxEVT_AUX1_DCLICK
))
1188 event
.m_aux1Down
= true;
1192 if ((event
.GetEventType() == wxEVT_AUX2_DOWN
) ||
1193 (event
.GetEventType() == wxEVT_AUX2_DCLICK
))
1195 event
.m_aux2Down
= true;
1200 // find the window to send the mouse event to
1202 wxWindowGTK
*FindWindowForMouseEvent(wxWindowGTK
*win
, wxCoord
& x
, wxCoord
& y
)
1207 if (win
->m_wxwindow
)
1209 wxPizza
* pizza
= WX_PIZZA(win
->m_wxwindow
);
1210 xx
+= pizza
->m_scroll_x
;
1211 yy
+= pizza
->m_scroll_y
;
1214 wxWindowList::compatibility_iterator node
= win
->GetChildren().GetFirst();
1217 wxWindow
* child
= static_cast<wxWindow
*>(node
->GetData());
1219 node
= node
->GetNext();
1220 if (!child
->IsShown())
1223 if (child
->GTKIsTransparentForMouse())
1225 // wxStaticBox is transparent in the box itself
1226 int xx1
= child
->m_x
;
1227 int yy1
= child
->m_y
;
1228 int xx2
= child
->m_x
+ child
->m_width
;
1229 int yy2
= child
->m_y
+ child
->m_height
;
1232 if (((xx
>= xx1
) && (xx
<= xx1
+10) && (yy
>= yy1
) && (yy
<= yy2
)) ||
1234 ((xx
>= xx2
-10) && (xx
<= xx2
) && (yy
>= yy1
) && (yy
<= yy2
)) ||
1236 ((xx
>= xx1
) && (xx
<= xx2
) && (yy
>= yy1
) && (yy
<= yy1
+10)) ||
1238 ((xx
>= xx1
) && (xx
<= xx2
) && (yy
>= yy2
-1) && (yy
<= yy2
)))
1249 if ((child
->m_wxwindow
== NULL
) &&
1250 win
->IsClientAreaChild(child
) &&
1251 (child
->m_x
<= xx
) &&
1252 (child
->m_y
<= yy
) &&
1253 (child
->m_x
+child
->m_width
>= xx
) &&
1254 (child
->m_y
+child
->m_height
>= yy
))
1267 // ----------------------------------------------------------------------------
1268 // common event handlers helpers
1269 // ----------------------------------------------------------------------------
1271 bool wxWindowGTK::GTKProcessEvent(wxEvent
& event
) const
1273 // nothing special at this level
1274 return HandleWindowEvent(event
);
1277 bool wxWindowGTK::GTKShouldIgnoreEvent() const
1279 return !m_hasVMT
|| g_blockEventsOnDrag
;
1282 int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny
*event
) const
1286 if (g_blockEventsOnDrag
)
1288 if (g_blockEventsOnScroll
)
1291 if (!GTKIsOwnWindow(event
->window
))
1297 // overloads for all GDK event types we use here: we need to have this as
1298 // GdkEventXXX can't be implicitly cast to GdkEventAny even if it, in fact,
1299 // derives from it in the sense that the structs have the same layout
1300 #define wxDEFINE_COMMON_PROLOGUE_OVERLOAD(T) \
1301 static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win) \
1303 return win->GTKCallbackCommonPrologue((GdkEventAny *)event); \
1306 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventButton
)
1307 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion
)
1308 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing
)
1310 #undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD
1312 #define wxCOMMON_CALLBACK_PROLOGUE(event, win) \
1313 const int rc = wxGtkCallbackCommonPrologue(event, win); \
1317 // all event handlers must have C linkage as they're called from GTK+ C code
1321 //-----------------------------------------------------------------------------
1322 // "button_press_event"
1323 //-----------------------------------------------------------------------------
1326 gtk_window_button_press_callback( GtkWidget
* WXUNUSED_IN_GTK3(widget
),
1327 GdkEventButton
*gdk_event
,
1330 wxPROCESS_EVENT_ONCE(GdkEventButton
, gdk_event
);
1332 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1334 g_lastButtonNumber
= gdk_event
->button
;
1336 wxEventType event_type
;
1339 switch (gdk_event
->button
)
1342 down
= wxEVT_LEFT_DOWN
;
1343 dclick
= wxEVT_LEFT_DCLICK
;
1346 down
= wxEVT_MIDDLE_DOWN
;
1347 dclick
= wxEVT_MIDDLE_DCLICK
;
1350 down
= wxEVT_RIGHT_DOWN
;
1351 dclick
= wxEVT_RIGHT_DCLICK
;
1354 down
= wxEVT_AUX1_DOWN
;
1355 dclick
= wxEVT_AUX1_DCLICK
;
1358 down
= wxEVT_AUX2_DOWN
;
1359 dclick
= wxEVT_AUX2_DCLICK
;
1364 switch (gdk_event
->type
)
1366 case GDK_BUTTON_PRESS
:
1368 // GDK sends surplus button down events
1369 // before a double click event. We
1370 // need to filter these out.
1371 if (win
->m_wxwindow
)
1373 GdkEvent
* peek_event
= gdk_event_peek();
1376 const GdkEventType peek_event_type
= peek_event
->type
;
1377 gdk_event_free(peek_event
);
1378 if (peek_event_type
== GDK_2BUTTON_PRESS
||
1379 peek_event_type
== GDK_3BUTTON_PRESS
)
1386 case GDK_2BUTTON_PRESS
:
1387 event_type
= dclick
;
1389 if (gdk_event
->button
>= 1 && gdk_event
->button
<= 3)
1391 // Reset GDK internal timestamp variables in order to disable GDK
1392 // triple click events. GDK will then next time believe no button has
1393 // been clicked just before, and send a normal button click event.
1394 GdkDisplay
* display
= gtk_widget_get_display(widget
);
1395 display
->button_click_time
[1] = 0;
1396 display
->button_click_time
[0] = 0;
1398 #endif // !__WXGTK3__
1400 // we shouldn't get triple clicks at all for GTK2 because we
1401 // suppress them artificially using the code above but we still
1402 // should map them to something for GTK3 and not just ignore them
1403 // as this would lose clicks
1404 case GDK_3BUTTON_PRESS
:
1411 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1413 wxMouseEvent
event( event_type
);
1414 InitMouseEvent( win
, event
, gdk_event
);
1416 AdjustEventButtonState(event
);
1418 // find the correct window to send the event to: it may be a different one
1419 // from the one which got it at GTK+ level because some controls don't have
1420 // their own X window and thus cannot get any events.
1421 if ( !g_captureWindow
)
1422 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1424 // reset the event object and id in case win changed.
1425 event
.SetEventObject( win
);
1426 event
.SetId( win
->GetId() );
1428 bool ret
= win
->GTKProcessEvent( event
);
1429 g_lastMouseEvent
= NULL
;
1433 if ((event_type
== wxEVT_LEFT_DOWN
) && !win
->IsOfStandardClass() &&
1434 (gs_currentFocus
!= win
) /* && win->IsFocusable() */)
1439 if (event_type
== wxEVT_RIGHT_DOWN
)
1441 // generate a "context menu" event: this is similar to right mouse
1442 // click under many GUIs except that it is generated differently
1443 // (right up under MSW, ctrl-click under Mac, right down here) and
1445 // (a) it's a command event and so is propagated to the parent
1446 // (b) under some ports it can be generated from kbd too
1447 // (c) it uses screen coords (because of (a))
1448 wxContextMenuEvent
evtCtx(
1451 win
->ClientToScreen(event
.GetPosition()));
1452 evtCtx
.SetEventObject(win
);
1453 return win
->GTKProcessEvent(evtCtx
);
1459 //-----------------------------------------------------------------------------
1460 // "button_release_event"
1461 //-----------------------------------------------------------------------------
1464 gtk_window_button_release_callback( GtkWidget
*WXUNUSED(widget
),
1465 GdkEventButton
*gdk_event
,
1468 wxPROCESS_EVENT_ONCE(GdkEventButton
, gdk_event
);
1470 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1472 g_lastButtonNumber
= 0;
1474 wxEventType event_type
= wxEVT_NULL
;
1476 switch (gdk_event
->button
)
1479 event_type
= wxEVT_LEFT_UP
;
1483 event_type
= wxEVT_MIDDLE_UP
;
1487 event_type
= wxEVT_RIGHT_UP
;
1491 event_type
= wxEVT_AUX1_UP
;
1495 event_type
= wxEVT_AUX2_UP
;
1499 // unknown button, don't process
1503 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1505 wxMouseEvent
event( event_type
);
1506 InitMouseEvent( win
, event
, gdk_event
);
1508 AdjustEventButtonState(event
);
1510 if ( !g_captureWindow
)
1511 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1513 // reset the event object and id in case win changed.
1514 event
.SetEventObject( win
);
1515 event
.SetId( win
->GetId() );
1517 bool ret
= win
->GTKProcessEvent(event
);
1519 g_lastMouseEvent
= NULL
;
1524 //-----------------------------------------------------------------------------
1525 // "motion_notify_event"
1526 //-----------------------------------------------------------------------------
1529 gtk_window_motion_notify_callback( GtkWidget
* WXUNUSED(widget
),
1530 GdkEventMotion
*gdk_event
,
1533 wxPROCESS_EVENT_ONCE(GdkEventMotion
, gdk_event
);
1535 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1537 if (gdk_event
->is_hint
)
1541 GdkModifierType state
;
1542 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, &state
);
1547 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1549 wxMouseEvent
event( wxEVT_MOTION
);
1550 InitMouseEvent(win
, event
, gdk_event
);
1552 if ( g_captureWindow
)
1554 // synthesise a mouse enter or leave event if needed
1555 GdkWindow
*winUnderMouse
= gdk_window_at_pointer(NULL
, NULL
);
1556 // This seems to be necessary and actually been added to
1557 // GDK itself in version 2.0.X
1560 bool hasMouse
= winUnderMouse
== gdk_event
->window
;
1561 if ( hasMouse
!= g_captureWindowHasMouse
)
1563 // the mouse changed window
1564 g_captureWindowHasMouse
= hasMouse
;
1566 wxMouseEvent
eventM(g_captureWindowHasMouse
? wxEVT_ENTER_WINDOW
1567 : wxEVT_LEAVE_WINDOW
);
1568 InitMouseEvent(win
, eventM
, gdk_event
);
1569 eventM
.SetEventObject(win
);
1570 win
->GTKProcessEvent(eventM
);
1575 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1577 // reset the event object and id in case win changed.
1578 event
.SetEventObject( win
);
1579 event
.SetId( win
->GetId() );
1582 if ( !g_captureWindow
)
1584 wxSetCursorEvent
cevent( event
.m_x
, event
.m_y
);
1585 if (win
->GTKProcessEvent( cevent
))
1587 win
->SetCursor( cevent
.GetCursor() );
1591 bool ret
= win
->GTKProcessEvent(event
);
1593 g_lastMouseEvent
= NULL
;
1598 //-----------------------------------------------------------------------------
1599 // "scroll_event" (mouse wheel event)
1600 //-----------------------------------------------------------------------------
1603 window_scroll_event_hscrollbar(GtkWidget
*, GdkEventScroll
* gdk_event
, wxWindow
* win
)
1605 if (gdk_event
->direction
!= GDK_SCROLL_LEFT
&&
1606 gdk_event
->direction
!= GDK_SCROLL_RIGHT
)
1611 GtkRange
*range
= win
->m_scrollBar
[wxWindow::ScrollDir_Horz
];
1613 if (range
&& gtk_widget_get_visible(GTK_WIDGET(range
)))
1615 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
1616 double delta
= gtk_adjustment_get_step_increment(adj
) * 3;
1617 if (gdk_event
->direction
== GDK_SCROLL_LEFT
)
1620 gtk_range_set_value(range
, gtk_adjustment_get_value(adj
) + delta
);
1629 window_scroll_event(GtkWidget
*, GdkEventScroll
* gdk_event
, wxWindow
* win
)
1631 wxMouseEvent
event(wxEVT_MOUSEWHEEL
);
1632 InitMouseEvent(win
, event
, gdk_event
);
1634 // FIXME: Get these values from GTK or GDK
1635 event
.m_linesPerAction
= 3;
1636 event
.m_wheelDelta
= 120;
1638 // Determine the scroll direction.
1639 switch (gdk_event
->direction
)
1642 case GDK_SCROLL_RIGHT
:
1643 event
.m_wheelRotation
= 120;
1646 case GDK_SCROLL_DOWN
:
1647 case GDK_SCROLL_LEFT
:
1648 event
.m_wheelRotation
= -120;
1652 return false; // Unknown/unhandled direction
1655 // And the scroll axis.
1656 switch (gdk_event
->direction
)
1659 case GDK_SCROLL_DOWN
:
1660 event
.m_wheelAxis
= wxMOUSE_WHEEL_VERTICAL
;
1663 case GDK_SCROLL_LEFT
:
1664 case GDK_SCROLL_RIGHT
:
1665 event
.m_wheelAxis
= wxMOUSE_WHEEL_HORIZONTAL
;
1669 if (win
->GTKProcessEvent(event
))
1672 GtkRange
*range
= win
->m_scrollBar
[wxWindow::ScrollDir_Vert
];
1674 if (range
&& gtk_widget_get_visible(GTK_WIDGET(range
)))
1676 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
1677 double delta
= gtk_adjustment_get_step_increment(adj
) * 3;
1678 if (gdk_event
->direction
== GDK_SCROLL_UP
)
1681 gtk_range_set_value(range
, gtk_adjustment_get_value(adj
) + delta
);
1689 //-----------------------------------------------------------------------------
1691 //-----------------------------------------------------------------------------
1693 static gboolean
wxgtk_window_popup_menu_callback(GtkWidget
*, wxWindowGTK
* win
)
1695 wxContextMenuEvent
event(wxEVT_CONTEXT_MENU
, win
->GetId(), wxPoint(-1, -1));
1696 event
.SetEventObject(win
);
1697 return win
->GTKProcessEvent(event
);
1700 //-----------------------------------------------------------------------------
1702 //-----------------------------------------------------------------------------
1705 gtk_window_focus_in_callback( GtkWidget
* WXUNUSED(widget
),
1706 GdkEventFocus
*WXUNUSED(event
),
1709 return win
->GTKHandleFocusIn();
1712 //-----------------------------------------------------------------------------
1713 // "focus_out_event"
1714 //-----------------------------------------------------------------------------
1717 gtk_window_focus_out_callback( GtkWidget
* WXUNUSED(widget
),
1718 GdkEventFocus
* WXUNUSED(gdk_event
),
1721 return win
->GTKHandleFocusOut();
1724 //-----------------------------------------------------------------------------
1726 //-----------------------------------------------------------------------------
1729 wx_window_focus_callback(GtkWidget
*widget
,
1730 GtkDirectionType
WXUNUSED(direction
),
1733 // the default handler for focus signal in GtkScrolledWindow sets
1734 // focus to the window itself even if it doesn't accept focus, i.e. has no
1735 // GTK_CAN_FOCUS in its style -- work around this by forcibly preventing
1736 // the signal from reaching gtk_scrolled_window_focus() if we don't have
1737 // any children which might accept focus (we know we don't accept the focus
1738 // ourselves as this signal is only connected in this case)
1739 if ( win
->GetChildren().empty() )
1740 g_signal_stop_emission_by_name(widget
, "focus");
1742 // we didn't change the focus
1746 //-----------------------------------------------------------------------------
1747 // "enter_notify_event"
1748 //-----------------------------------------------------------------------------
1751 gtk_window_enter_callback( GtkWidget
*widget
,
1752 GdkEventCrossing
*gdk_event
,
1755 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1757 // Event was emitted after a grab
1758 if (gdk_event
->mode
!= GDK_CROSSING_NORMAL
) return FALSE
;
1762 GdkModifierType state
= (GdkModifierType
)0;
1764 gdk_window_get_pointer(gtk_widget_get_window(widget
), &x
, &y
, &state
);
1766 wxMouseEvent
event( wxEVT_ENTER_WINDOW
);
1767 InitMouseEvent(win
, event
, gdk_event
);
1768 wxPoint pt
= win
->GetClientAreaOrigin();
1769 event
.m_x
= x
+ pt
.x
;
1770 event
.m_y
= y
+ pt
.y
;
1772 if ( !g_captureWindow
)
1774 wxSetCursorEvent
cevent( event
.m_x
, event
.m_y
);
1775 if (win
->GTKProcessEvent( cevent
))
1777 win
->SetCursor( cevent
.GetCursor() );
1781 return win
->GTKProcessEvent(event
);
1784 //-----------------------------------------------------------------------------
1785 // "leave_notify_event"
1786 //-----------------------------------------------------------------------------
1789 gtk_window_leave_callback( GtkWidget
*widget
,
1790 GdkEventCrossing
*gdk_event
,
1793 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1795 // Event was emitted after an ungrab
1796 if (gdk_event
->mode
!= GDK_CROSSING_NORMAL
) return FALSE
;
1798 wxMouseEvent
event( wxEVT_LEAVE_WINDOW
);
1802 GdkModifierType state
= (GdkModifierType
)0;
1804 gdk_window_get_pointer(gtk_widget_get_window(widget
), &x
, &y
, &state
);
1806 InitMouseEvent(win
, event
, gdk_event
);
1808 return win
->GTKProcessEvent(event
);
1811 //-----------------------------------------------------------------------------
1812 // "value_changed" from scrollbar
1813 //-----------------------------------------------------------------------------
1816 gtk_scrollbar_value_changed(GtkRange
* range
, wxWindow
* win
)
1818 wxEventType eventType
= win
->GTKGetScrollEventType(range
);
1819 if (eventType
!= wxEVT_NULL
)
1821 // Convert scroll event type to scrollwin event type
1822 eventType
+= wxEVT_SCROLLWIN_TOP
- wxEVT_SCROLL_TOP
;
1824 // find the scrollbar which generated the event
1825 wxWindowGTK::ScrollDir dir
= win
->ScrollDirFromRange(range
);
1827 // generate the corresponding wx event
1828 const int orient
= wxWindow::OrientFromScrollDir(dir
);
1829 wxScrollWinEvent
event(eventType
, win
->GetScrollPos(orient
), orient
);
1830 event
.SetEventObject(win
);
1832 win
->GTKProcessEvent(event
);
1836 //-----------------------------------------------------------------------------
1837 // "button_press_event" from scrollbar
1838 //-----------------------------------------------------------------------------
1841 gtk_scrollbar_button_press_event(GtkRange
*, GdkEventButton
*, wxWindow
* win
)
1843 g_blockEventsOnScroll
= true;
1844 win
->m_mouseButtonDown
= true;
1849 //-----------------------------------------------------------------------------
1850 // "event_after" from scrollbar
1851 //-----------------------------------------------------------------------------
1854 gtk_scrollbar_event_after(GtkRange
* range
, GdkEvent
* event
, wxWindow
* win
)
1856 if (event
->type
== GDK_BUTTON_RELEASE
)
1858 g_signal_handlers_block_by_func(range
, (void*)gtk_scrollbar_event_after
, win
);
1860 const int orient
= wxWindow::OrientFromScrollDir(
1861 win
->ScrollDirFromRange(range
));
1862 wxScrollWinEvent
evt(wxEVT_SCROLLWIN_THUMBRELEASE
,
1863 win
->GetScrollPos(orient
), orient
);
1864 evt
.SetEventObject(win
);
1865 win
->GTKProcessEvent(evt
);
1869 //-----------------------------------------------------------------------------
1870 // "button_release_event" from scrollbar
1871 //-----------------------------------------------------------------------------
1874 gtk_scrollbar_button_release_event(GtkRange
* range
, GdkEventButton
*, wxWindow
* win
)
1876 g_blockEventsOnScroll
= false;
1877 win
->m_mouseButtonDown
= false;
1878 // If thumb tracking
1879 if (win
->m_isScrolling
)
1881 win
->m_isScrolling
= false;
1882 // Hook up handler to send thumb release event after this emission is finished.
1883 // To allow setting scroll position from event handler, sending event must
1884 // be deferred until after the GtkRange handler for this signal has run
1885 g_signal_handlers_unblock_by_func(range
, (void*)gtk_scrollbar_event_after
, win
);
1891 //-----------------------------------------------------------------------------
1892 // "realize" from m_widget
1893 //-----------------------------------------------------------------------------
1896 gtk_window_realized_callback(GtkWidget
* WXUNUSED(widget
), wxWindowGTK
* win
)
1898 win
->GTKHandleRealized();
1901 //-----------------------------------------------------------------------------
1902 // "size_allocate" from m_wxwindow or m_widget
1903 //-----------------------------------------------------------------------------
1906 size_allocate(GtkWidget
*, GtkAllocation
* alloc
, wxWindow
* win
)
1908 int w
= alloc
->width
;
1909 int h
= alloc
->height
;
1910 if (win
->m_wxwindow
)
1913 WX_PIZZA(win
->m_wxwindow
)->get_border(border
);
1914 w
-= border
.left
+ border
.right
;
1915 h
-= border
.top
+ border
.bottom
;
1919 if (win
->m_oldClientWidth
!= w
|| win
->m_oldClientHeight
!= h
)
1921 win
->m_oldClientWidth
= w
;
1922 win
->m_oldClientHeight
= h
;
1923 // this callback can be connected to m_wxwindow,
1924 // so always get size from m_widget->allocation
1926 gtk_widget_get_allocation(win
->m_widget
, &a
);
1927 win
->m_width
= a
.width
;
1928 win
->m_height
= a
.height
;
1929 if (!win
->m_nativeSizeEvent
)
1931 wxSizeEvent
event(win
->GetSize(), win
->GetId());
1932 event
.SetEventObject(win
);
1933 win
->GTKProcessEvent(event
);
1938 //-----------------------------------------------------------------------------
1940 //-----------------------------------------------------------------------------
1942 #if GTK_CHECK_VERSION(2, 8, 0)
1944 gtk_window_grab_broken( GtkWidget
*,
1945 GdkEventGrabBroken
*event
,
1948 // Mouse capture has been lost involuntarily, notify the application
1949 if(!event
->keyboard
&& wxWindow::GetCapture() == win
)
1951 wxMouseCaptureLostEvent
evt( win
->GetId() );
1952 evt
.SetEventObject( win
);
1953 win
->HandleWindowEvent( evt
);
1959 //-----------------------------------------------------------------------------
1960 // "style_set"/"style_updated"
1961 //-----------------------------------------------------------------------------
1964 static void style_updated(GtkWidget
*, wxWindow
* win
)
1966 static void style_updated(GtkWidget
*, GtkStyle
*, wxWindow
* win
)
1969 if (win
->IsTopLevel())
1971 wxSysColourChangedEvent event
;
1972 event
.SetEventObject(win
);
1973 win
->GTKProcessEvent(event
);
1977 // Border width could change, which will change client size.
1978 // Make sure size event occurs for this
1979 win
->m_oldClientWidth
= 0;
1983 //-----------------------------------------------------------------------------
1985 //-----------------------------------------------------------------------------
1987 static void unrealize(GtkWidget
*, wxWindow
* win
)
1989 win
->GTKHandleUnrealize();
1994 void wxWindowGTK::GTKHandleRealized()
2001 gtk_im_context_set_client_window
2004 m_wxwindow
? GTKGetDrawingWindow()
2005 : gtk_widget_get_window(m_widget
)
2009 // Use composited window if background is transparent, if supported.
2010 if (m_backgroundStyle
== wxBG_STYLE_TRANSPARENT
)
2012 #if wxGTK_HAS_COMPOSITING_SUPPORT
2013 if (IsTransparentBackgroundSupported())
2015 GdkWindow
* const window
= GTKGetDrawingWindow();
2017 gdk_window_set_composited(window
, true);
2020 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
2022 // We revert to erase mode if transparency is not supported
2023 m_backgroundStyle
= wxBG_STYLE_ERASE
;
2028 // We cannot set colours and fonts before the widget
2029 // been realized, so we do this directly after realization
2030 // or otherwise in idle time
2032 if (m_needsStyleChange
)
2034 SetBackgroundStyle(GetBackgroundStyle());
2035 m_needsStyleChange
= false;
2038 wxWindowCreateEvent
event(static_cast<wxWindow
*>(this));
2039 event
.SetEventObject( this );
2040 GTKProcessEvent( event
);
2042 GTKUpdateCursor(true, false);
2045 (IsTopLevel() || HasFlag(wxBORDER_RAISED
| wxBORDER_SUNKEN
| wxBORDER_THEME
)))
2047 // attaching to style changed signal after realization avoids initial
2048 // changes we don't care about
2049 const gchar
*detailed_signal
=
2055 g_signal_connect(m_wxwindow
,
2057 G_CALLBACK(style_updated
), this);
2061 void wxWindowGTK::GTKHandleUnrealize()
2063 // unrealizing a frozen window seems to have some lingering effect
2064 // preventing updates to the affected area
2071 gtk_im_context_set_client_window(m_imData
->context
, NULL
);
2073 g_signal_handlers_disconnect_by_func(
2074 m_wxwindow
, (void*)style_updated
, this);
2078 // ----------------------------------------------------------------------------
2079 // this wxWindowBase function is implemented here (in platform-specific file)
2080 // because it is static and so couldn't be made virtual
2081 // ----------------------------------------------------------------------------
2083 wxWindow
*wxWindowBase::DoFindFocus()
2085 // For compatibility with wxMSW, pretend that showing a popup menu doesn't
2086 // change the focus and that it remains on the window showing it, even
2087 // though the real focus does change in GTK.
2088 extern wxMenu
*wxCurrentPopupMenu
;
2089 if ( wxCurrentPopupMenu
)
2090 return wxCurrentPopupMenu
->GetInvokingWindow();
2092 wxWindowGTK
*focus
= gs_pendingFocus
? gs_pendingFocus
: gs_currentFocus
;
2093 // the cast is necessary when we compile in wxUniversal mode
2094 return static_cast<wxWindow
*>(focus
);
2097 void wxWindowGTK::AddChildGTK(wxWindowGTK
* child
)
2099 wxASSERT_MSG(m_wxwindow
, "Cannot add a child to a window without a client area");
2101 // the window might have been scrolled already, we
2102 // have to adapt the position
2103 wxPizza
* pizza
= WX_PIZZA(m_wxwindow
);
2104 child
->m_x
+= pizza
->m_scroll_x
;
2105 child
->m_y
+= pizza
->m_scroll_y
;
2107 pizza
->put(child
->m_widget
,
2108 child
->m_x
, child
->m_y
, child
->m_width
, child
->m_height
);
2111 //-----------------------------------------------------------------------------
2113 //-----------------------------------------------------------------------------
2115 wxWindow
*wxGetActiveWindow()
2117 return wxWindow::FindFocus();
2121 // Under Unix this is implemented using X11 functions in utilsx11.cpp but we
2122 // need to have this function under Windows too, so provide at least a stub.
2123 #ifndef GDK_WINDOWING_X11
2124 bool wxGetKeyState(wxKeyCode
WXUNUSED(key
))
2126 wxFAIL_MSG(wxS("Not implemented under Windows"));
2129 #endif // __WINDOWS__
2131 static void GetMouseState(int& x
, int& y
, GdkModifierType
& mask
)
2133 wxWindow
* tlw
= NULL
;
2134 if (!wxTopLevelWindows
.empty())
2135 tlw
= wxTopLevelWindows
.front();
2136 GdkDisplay
* display
;
2137 if (tlw
&& tlw
->m_widget
)
2138 display
= gtk_widget_get_display(tlw
->m_widget
);
2140 display
= gdk_display_get_default();
2141 gdk_display_get_pointer(display
, NULL
, &x
, &y
, &mask
);
2144 wxMouseState
wxGetMouseState()
2150 GdkModifierType mask
;
2152 GetMouseState(x
, y
, mask
);
2156 ms
.SetLeftDown((mask
& GDK_BUTTON1_MASK
) != 0);
2157 ms
.SetMiddleDown((mask
& GDK_BUTTON2_MASK
) != 0);
2158 ms
.SetRightDown((mask
& GDK_BUTTON3_MASK
) != 0);
2159 // see the comment in InitMouseEvent()
2160 ms
.SetAux1Down((mask
& GDK_BUTTON4_MASK
) != 0);
2161 ms
.SetAux2Down((mask
& GDK_BUTTON5_MASK
) != 0);
2163 ms
.SetControlDown((mask
& GDK_CONTROL_MASK
) != 0);
2164 ms
.SetShiftDown((mask
& GDK_SHIFT_MASK
) != 0);
2165 ms
.SetAltDown((mask
& GDK_MOD1_MASK
) != 0);
2166 ms
.SetMetaDown((mask
& GDK_META_MASK
) != 0);
2171 //-----------------------------------------------------------------------------
2173 //-----------------------------------------------------------------------------
2175 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2177 #ifdef __WXUNIVERSAL__
2178 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK
, wxWindowBase
)
2179 #endif // __WXUNIVERSAL__
2181 void wxWindowGTK::Init()
2186 m_focusWidget
= NULL
;
2196 m_showOnIdle
= false;
2199 m_nativeSizeEvent
= false;
2201 m_paintContext
= NULL
;
2204 m_isScrolling
= false;
2205 m_mouseButtonDown
= false;
2207 // initialize scrolling stuff
2208 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
2210 m_scrollBar
[dir
] = NULL
;
2211 m_scrollPos
[dir
] = 0;
2215 m_oldClientHeight
= 0;
2217 m_clipPaintRegion
= false;
2219 m_needsStyleChange
= false;
2221 m_cursor
= *wxSTANDARD_CURSOR
;
2224 m_dirtyTabOrder
= false;
2227 wxWindowGTK::wxWindowGTK()
2232 wxWindowGTK::wxWindowGTK( wxWindow
*parent
,
2237 const wxString
&name
)
2241 Create( parent
, id
, pos
, size
, style
, name
);
2244 void wxWindowGTK::GTKCreateScrolledWindowWith(GtkWidget
* view
)
2246 wxASSERT_MSG( HasFlag(wxHSCROLL
) || HasFlag(wxVSCROLL
),
2247 wxS("Must not be called if scrolling is not needed.") );
2249 m_widget
= gtk_scrolled_window_new( NULL
, NULL
);
2251 GtkScrolledWindow
*scrolledWindow
= GTK_SCROLLED_WINDOW(m_widget
);
2253 // There is a conflict with default bindings at GTK+
2254 // level between scrolled windows and notebooks both of which want to use
2255 // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
2256 // direction and notebooks for changing pages -- we decide that if we don't
2257 // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
2258 // means we can get working keyboard navigation in notebooks
2259 if ( !HasFlag(wxHSCROLL
) )
2262 bindings
= gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget
));
2265 gtk_binding_entry_remove(bindings
, GDK_Page_Up
, GDK_CONTROL_MASK
);
2266 gtk_binding_entry_remove(bindings
, GDK_Page_Down
, GDK_CONTROL_MASK
);
2270 if (HasFlag(wxALWAYS_SHOW_SB
))
2272 gtk_scrolled_window_set_policy( scrolledWindow
, GTK_POLICY_ALWAYS
, GTK_POLICY_ALWAYS
);
2276 gtk_scrolled_window_set_policy( scrolledWindow
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2279 m_scrollBar
[ScrollDir_Horz
] = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(scrolledWindow
));
2280 m_scrollBar
[ScrollDir_Vert
] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(scrolledWindow
));
2281 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2282 gtk_range_set_inverted( m_scrollBar
[ScrollDir_Horz
], TRUE
);
2284 gtk_container_add( GTK_CONTAINER(m_widget
), view
);
2286 // connect various scroll-related events
2287 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
2289 // these handlers block mouse events to any window during scrolling
2290 // such as motion events and prevent GTK and wxWidgets from fighting
2291 // over where the slider should be
2292 g_signal_connect(m_scrollBar
[dir
], "button_press_event",
2293 G_CALLBACK(gtk_scrollbar_button_press_event
), this);
2294 g_signal_connect(m_scrollBar
[dir
], "button_release_event",
2295 G_CALLBACK(gtk_scrollbar_button_release_event
), this);
2297 gulong handler_id
= g_signal_connect(m_scrollBar
[dir
], "event_after",
2298 G_CALLBACK(gtk_scrollbar_event_after
), this);
2299 g_signal_handler_block(m_scrollBar
[dir
], handler_id
);
2301 // these handlers get notified when scrollbar slider moves
2302 g_signal_connect_after(m_scrollBar
[dir
], "value_changed",
2303 G_CALLBACK(gtk_scrollbar_value_changed
), this);
2306 gtk_widget_show( view
);
2309 bool wxWindowGTK::Create( wxWindow
*parent
,
2314 const wxString
&name
)
2316 // Get default border
2317 wxBorder border
= GetBorder(style
);
2319 style
&= ~wxBORDER_MASK
;
2322 if (!PreCreation( parent
, pos
, size
) ||
2323 !CreateBase( parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
))
2325 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2329 // We should accept the native look
2331 GtkScrolledWindowClass
*scroll_class
= GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget
) );
2332 scroll_class
->scrollbar_spacing
= 0;
2336 m_wxwindow
= wxPizza::New(m_windowStyle
);
2337 #ifndef __WXUNIVERSAL__
2338 if (HasFlag(wxPizza::BORDER_STYLES
))
2340 g_signal_connect(m_wxwindow
, "parent_set",
2341 G_CALLBACK(parent_set
), this);
2344 if (!HasFlag(wxHSCROLL
) && !HasFlag(wxVSCROLL
))
2345 m_widget
= m_wxwindow
;
2347 GTKCreateScrolledWindowWith(m_wxwindow
);
2348 g_object_ref(m_widget
);
2351 m_parent
->DoAddChild( this );
2353 m_focusWidget
= m_wxwindow
;
2355 SetCanFocus(AcceptsFocus());
2362 wxWindowGTK::~wxWindowGTK()
2366 if (gs_currentFocus
== this)
2367 gs_currentFocus
= NULL
;
2368 if (gs_pendingFocus
== this)
2369 gs_pendingFocus
= NULL
;
2371 if ( gs_deferredFocusOut
== this )
2372 gs_deferredFocusOut
= NULL
;
2376 // destroy children before destroying this window itself
2379 // unhook focus handlers to prevent stray events being
2380 // propagated to this (soon to be) dead object
2381 if (m_focusWidget
!= NULL
)
2383 g_signal_handlers_disconnect_by_func (m_focusWidget
,
2384 (gpointer
) gtk_window_focus_in_callback
,
2386 g_signal_handlers_disconnect_by_func (m_focusWidget
,
2387 (gpointer
) gtk_window_focus_out_callback
,
2394 // delete before the widgets to avoid a crash on solaris
2398 // avoid problem with GTK+ 2.18 where a frozen window causes the whole
2399 // TLW to be frozen, and if the window is then destroyed, nothing ever
2400 // gets painted again
2406 // Note that gtk_widget_destroy() does not destroy the widget, it just
2407 // emits the "destroy" signal. The widget is not actually destroyed
2408 // until its reference count drops to zero.
2409 gtk_widget_destroy(m_widget
);
2410 // Release our reference, should be the last one
2411 g_object_unref(m_widget
);
2417 bool wxWindowGTK::PreCreation( wxWindowGTK
*parent
, const wxPoint
&pos
, const wxSize
&size
)
2419 if ( GTKNeedsParent() )
2421 wxCHECK_MSG( parent
, false, wxT("Must have non-NULL parent") );
2424 // Use either the given size, or the default if -1 is given.
2425 // See wxWindowBase for these functions.
2426 m_width
= WidthDefault(size
.x
) ;
2427 m_height
= HeightDefault(size
.y
);
2429 if (pos
!= wxDefaultPosition
)
2438 void wxWindowGTK::PostCreation()
2440 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
2442 #if wxGTK_HAS_COMPOSITING_SUPPORT
2443 // Set RGBA visual as soon as possible to minimize the possibility that
2444 // somebody uses the wrong one.
2445 if ( m_backgroundStyle
== wxBG_STYLE_TRANSPARENT
&&
2446 IsTransparentBackgroundSupported() )
2448 GdkScreen
*screen
= gtk_widget_get_screen (m_widget
);
2450 gtk_widget_set_visual(m_widget
, gdk_screen_get_rgba_visual(screen
));
2452 GdkColormap
*rgba_colormap
= gdk_screen_get_rgba_colormap (screen
);
2455 gtk_widget_set_colormap(m_widget
, rgba_colormap
);
2458 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
2464 // these get reported to wxWidgets -> wxPaintEvent
2466 g_signal_connect(m_wxwindow
, "draw", G_CALLBACK(draw
), this);
2468 g_signal_connect(m_wxwindow
, "expose_event", G_CALLBACK(expose_event
), this);
2471 if (GetLayoutDirection() == wxLayout_LeftToRight
)
2472 gtk_widget_set_redraw_on_allocate(m_wxwindow
, HasFlag(wxFULL_REPAINT_ON_RESIZE
));
2475 // Create input method handler
2476 m_imData
= new wxGtkIMData
;
2478 // Cannot handle drawing preedited text yet
2479 gtk_im_context_set_use_preedit( m_imData
->context
, FALSE
);
2481 g_signal_connect (m_imData
->context
, "commit",
2482 G_CALLBACK (gtk_wxwindow_commit_cb
), this);
2487 if (!GTK_IS_WINDOW(m_widget
))
2489 if (m_focusWidget
== NULL
)
2490 m_focusWidget
= m_widget
;
2494 g_signal_connect (m_focusWidget
, "focus_in_event",
2495 G_CALLBACK (gtk_window_focus_in_callback
), this);
2496 g_signal_connect (m_focusWidget
, "focus_out_event",
2497 G_CALLBACK (gtk_window_focus_out_callback
), this);
2501 g_signal_connect_after (m_focusWidget
, "focus_in_event",
2502 G_CALLBACK (gtk_window_focus_in_callback
), this);
2503 g_signal_connect_after (m_focusWidget
, "focus_out_event",
2504 G_CALLBACK (gtk_window_focus_out_callback
), this);
2508 if ( !AcceptsFocusFromKeyboard() )
2512 g_signal_connect(m_widget
, "focus",
2513 G_CALLBACK(wx_window_focus_callback
), this);
2516 // connect to the various key and mouse handlers
2518 GtkWidget
*connect_widget
= GetConnectWidget();
2520 ConnectWidget( connect_widget
);
2522 // We cannot set colours, fonts and cursors before the widget has been
2523 // realized, so we do this directly after realization -- unless the widget
2524 // was in fact realized already.
2525 if ( gtk_widget_get_realized(connect_widget
) )
2527 GTKHandleRealized();
2531 g_signal_connect (connect_widget
, "realize",
2532 G_CALLBACK (gtk_window_realized_callback
), this);
2534 g_signal_connect(connect_widget
, "unrealize", G_CALLBACK(unrealize
), this);
2538 g_signal_connect(m_wxwindow
? m_wxwindow
: m_widget
, "size_allocate",
2539 G_CALLBACK(size_allocate
), this);
2542 #if GTK_CHECK_VERSION(2, 8, 0)
2544 if ( gtk_check_version(2,8,0) == NULL
)
2547 // Make sure we can notify the app when mouse capture is lost
2550 g_signal_connect (m_wxwindow
, "grab_broken_event",
2551 G_CALLBACK (gtk_window_grab_broken
), this);
2554 if ( connect_widget
!= m_wxwindow
)
2556 g_signal_connect (connect_widget
, "grab_broken_event",
2557 G_CALLBACK (gtk_window_grab_broken
), this);
2560 #endif // GTK+ >= 2.8
2562 if (!WX_IS_PIZZA(gtk_widget_get_parent(m_widget
)) && !GTK_IS_WINDOW(m_widget
))
2563 gtk_widget_set_size_request(m_widget
, m_width
, m_height
);
2565 InheritAttributes();
2569 SetLayoutDirection(wxLayout_Default
);
2571 // unless the window was created initially hidden (i.e. Hide() had been
2572 // called before Create()), we should show it at GTK+ level as well
2574 gtk_widget_show( m_widget
);
2578 wxWindowGTK::GTKConnectWidget(const char *signal
, wxGTKCallback callback
)
2580 return g_signal_connect(m_widget
, signal
, callback
, this);
2583 void wxWindowGTK::ConnectWidget( GtkWidget
*widget
)
2585 g_signal_connect (widget
, "key_press_event",
2586 G_CALLBACK (gtk_window_key_press_callback
), this);
2587 g_signal_connect (widget
, "key_release_event",
2588 G_CALLBACK (gtk_window_key_release_callback
), this);
2589 g_signal_connect (widget
, "button_press_event",
2590 G_CALLBACK (gtk_window_button_press_callback
), this);
2591 g_signal_connect (widget
, "button_release_event",
2592 G_CALLBACK (gtk_window_button_release_callback
), this);
2593 g_signal_connect (widget
, "motion_notify_event",
2594 G_CALLBACK (gtk_window_motion_notify_callback
), this);
2596 g_signal_connect (widget
, "scroll_event",
2597 G_CALLBACK (window_scroll_event
), this);
2598 if (m_scrollBar
[ScrollDir_Horz
])
2599 g_signal_connect (m_scrollBar
[ScrollDir_Horz
], "scroll_event",
2600 G_CALLBACK (window_scroll_event_hscrollbar
), this);
2601 if (m_scrollBar
[ScrollDir_Vert
])
2602 g_signal_connect (m_scrollBar
[ScrollDir_Vert
], "scroll_event",
2603 G_CALLBACK (window_scroll_event
), this);
2605 g_signal_connect (widget
, "popup_menu",
2606 G_CALLBACK (wxgtk_window_popup_menu_callback
), this);
2607 g_signal_connect (widget
, "enter_notify_event",
2608 G_CALLBACK (gtk_window_enter_callback
), this);
2609 g_signal_connect (widget
, "leave_notify_event",
2610 G_CALLBACK (gtk_window_leave_callback
), this);
2613 bool wxWindowGTK::Destroy()
2617 return wxWindowBase::Destroy();
2620 static GSList
* gs_queueResizeList
;
2623 static gboolean
queue_resize(void*)
2625 gdk_threads_enter();
2626 for (GSList
* p
= gs_queueResizeList
; p
; p
= p
->next
)
2630 gtk_widget_queue_resize(GTK_WIDGET(p
->data
));
2631 g_object_remove_weak_pointer(G_OBJECT(p
->data
), &p
->data
);
2634 g_slist_free(gs_queueResizeList
);
2635 gs_queueResizeList
= NULL
;
2636 gdk_threads_leave();
2641 void wxWindowGTK::DoMoveWindow(int x
, int y
, int width
, int height
)
2643 gtk_widget_set_size_request(m_widget
, width
, height
);
2644 GtkWidget
* parent
= gtk_widget_get_parent(m_widget
);
2645 if (WX_IS_PIZZA(parent
))
2646 WX_PIZZA(parent
)->move(m_widget
, x
, y
, width
, height
);
2648 // With GTK3, gtk_widget_queue_resize() is ignored while a size-allocate
2649 // is in progress. This situation is common in wxWidgets, since
2650 // size-allocate can generate wxSizeEvent and size event handlers often
2651 // call SetSize(), directly or indirectly. Work around this by deferring
2652 // the queue-resize until after size-allocate processing is finished.
2653 if (g_slist_find(gs_queueResizeList
, m_widget
) == NULL
)
2655 if (gs_queueResizeList
== NULL
)
2656 g_idle_add_full(GTK_PRIORITY_RESIZE
, queue_resize
, NULL
, NULL
);
2657 gs_queueResizeList
= g_slist_prepend(gs_queueResizeList
, m_widget
);
2658 g_object_add_weak_pointer(G_OBJECT(m_widget
), &gs_queueResizeList
->data
);
2662 void wxWindowGTK::ConstrainSize()
2665 // GPE's window manager doesn't like size hints at all, esp. when the user
2666 // has to use the virtual keyboard, so don't constrain size there
2670 const wxSize minSize
= GetMinSize();
2671 const wxSize maxSize
= GetMaxSize();
2672 if (minSize
.x
> 0 && m_width
< minSize
.x
) m_width
= minSize
.x
;
2673 if (minSize
.y
> 0 && m_height
< minSize
.y
) m_height
= minSize
.y
;
2674 if (maxSize
.x
> 0 && m_width
> maxSize
.x
) m_width
= maxSize
.x
;
2675 if (maxSize
.y
> 0 && m_height
> maxSize
.y
) m_height
= maxSize
.y
;
2679 void wxWindowGTK::DoSetSize( int x
, int y
, int width
, int height
, int sizeFlags
)
2681 wxCHECK_RET(m_widget
, "invalid window");
2683 int scrollX
= 0, scrollY
= 0;
2684 GtkWidget
* parent
= gtk_widget_get_parent(m_widget
);
2685 if (WX_IS_PIZZA(parent
))
2687 wxPizza
* pizza
= WX_PIZZA(parent
);
2688 scrollX
= pizza
->m_scroll_x
;
2689 scrollY
= pizza
->m_scroll_y
;
2691 if (x
!= -1 || (sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
2695 if (y
!= -1 || (sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
2700 // calculate the best size if we should auto size the window
2701 if ( ((sizeFlags
& wxSIZE_AUTO_WIDTH
) && width
== -1) ||
2702 ((sizeFlags
& wxSIZE_AUTO_HEIGHT
) && height
== -1) )
2704 const wxSize sizeBest
= GetBestSize();
2705 if ( (sizeFlags
& wxSIZE_AUTO_WIDTH
) && width
== -1 )
2707 if ( (sizeFlags
& wxSIZE_AUTO_HEIGHT
) && height
== -1 )
2708 height
= sizeBest
.y
;
2716 const bool sizeChange
= m_width
!= width
|| m_height
!= height
;
2717 if (sizeChange
|| m_x
!= x
|| m_y
!= y
)
2724 /* the default button has a border around it */
2725 if (gtk_widget_get_can_default(m_widget
))
2727 GtkBorder
*default_border
= NULL
;
2728 gtk_widget_style_get( m_widget
, "default_border", &default_border
, NULL
);
2731 x
-= default_border
->left
;
2732 y
-= default_border
->top
;
2733 width
+= default_border
->left
+ default_border
->right
;
2734 height
+= default_border
->top
+ default_border
->bottom
;
2735 gtk_border_free( default_border
);
2739 DoMoveWindow(x
, y
, width
, height
);
2742 if ((sizeChange
&& !m_nativeSizeEvent
) || (sizeFlags
& wxSIZE_FORCE_EVENT
))
2744 // update these variables to keep size_allocate handler
2745 // from sending another size event for this change
2746 GetClientSize( &m_oldClientWidth
, &m_oldClientHeight
);
2748 wxSizeEvent
event( wxSize(m_width
,m_height
), GetId() );
2749 event
.SetEventObject( this );
2750 HandleWindowEvent( event
);
2754 bool wxWindowGTK::GTKShowFromOnIdle()
2756 if (IsShown() && m_showOnIdle
&& !gtk_widget_get_visible (m_widget
))
2758 GtkAllocation alloc
;
2761 alloc
.width
= m_width
;
2762 alloc
.height
= m_height
;
2763 gtk_widget_size_allocate( m_widget
, &alloc
);
2764 gtk_widget_show( m_widget
);
2765 wxShowEvent
eventShow(GetId(), true);
2766 eventShow
.SetEventObject(this);
2767 HandleWindowEvent(eventShow
);
2768 m_showOnIdle
= false;
2775 void wxWindowGTK::OnInternalIdle()
2777 if ( gs_deferredFocusOut
)
2778 GTKHandleDeferredFocusOut();
2780 // Check if we have to show window now
2781 if (GTKShowFromOnIdle()) return;
2783 if ( m_dirtyTabOrder
)
2785 m_dirtyTabOrder
= false;
2789 wxWindowBase::OnInternalIdle();
2792 void wxWindowGTK::DoGetSize( int *width
, int *height
) const
2794 if (width
) (*width
) = m_width
;
2795 if (height
) (*height
) = m_height
;
2798 void wxWindowGTK::DoSetClientSize( int width
, int height
)
2800 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2802 const wxSize size
= GetSize();
2803 const wxSize clientSize
= GetClientSize();
2804 SetSize(width
+ (size
.x
- clientSize
.x
), height
+ (size
.y
- clientSize
.y
));
2807 void wxWindowGTK::DoGetClientSize( int *width
, int *height
) const
2809 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2816 // if window is scrollable, account for scrollbars
2817 if ( GTK_IS_SCROLLED_WINDOW(m_widget
) )
2819 GtkPolicyType policy
[ScrollDir_Max
];
2820 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(m_widget
),
2821 &policy
[ScrollDir_Horz
],
2822 &policy
[ScrollDir_Vert
]);
2823 const int scrollbar_spacing
=
2824 GTK_SCROLLED_WINDOW_GET_CLASS(m_widget
)->scrollbar_spacing
;
2826 for ( int i
= 0; i
< ScrollDir_Max
; i
++ )
2828 // don't account for the scrollbars we don't have
2829 GtkRange
* const range
= m_scrollBar
[i
];
2833 // nor for the ones we have but don't current show
2834 switch ( policy
[i
] )
2836 case GTK_POLICY_NEVER
:
2837 // never shown so doesn't take any place
2840 case GTK_POLICY_ALWAYS
:
2841 // no checks necessary
2844 case GTK_POLICY_AUTOMATIC
:
2845 // may be shown or not, check
2846 GtkAdjustment
*adj
= gtk_range_get_adjustment(range
);
2847 if (gtk_adjustment_get_upper(adj
) <= gtk_adjustment_get_page_size(adj
))
2853 GtkWidget
* widget
= GTK_WIDGET(range
);
2854 if (i
== ScrollDir_Horz
)
2858 gtk_widget_get_preferred_height(widget
, NULL
, &req
.height
);
2859 h
-= req
.height
+ scrollbar_spacing
;
2866 gtk_widget_get_preferred_width(widget
, NULL
, &req
.width
);
2867 w
-= req
.width
+ scrollbar_spacing
;
2870 #else // !__WXGTK3__
2871 gtk_widget_size_request(GTK_WIDGET(range
), &req
);
2872 if (i
== ScrollDir_Horz
)
2873 h
-= req
.height
+ scrollbar_spacing
;
2875 w
-= req
.width
+ scrollbar_spacing
;
2876 #endif // !__WXGTK3__
2880 const wxSize sizeBorders
= DoGetBorderSize();
2890 if (width
) *width
= w
;
2891 if (height
) *height
= h
;
2894 wxSize
wxWindowGTK::DoGetBorderSize() const
2897 return wxWindowBase::DoGetBorderSize();
2900 WX_PIZZA(m_wxwindow
)->get_border(border
);
2901 return wxSize(border
.left
+ border
.right
, border
.top
+ border
.bottom
);
2904 void wxWindowGTK::DoGetPosition( int *x
, int *y
) const
2908 GtkWidget
* parent
= NULL
;
2910 parent
= gtk_widget_get_parent(m_widget
);
2911 if (WX_IS_PIZZA(parent
))
2913 wxPizza
* pizza
= WX_PIZZA(parent
);
2914 dx
= pizza
->m_scroll_x
;
2915 dy
= pizza
->m_scroll_y
;
2917 if (x
) (*x
) = m_x
- dx
;
2918 if (y
) (*y
) = m_y
- dy
;
2921 void wxWindowGTK::DoClientToScreen( int *x
, int *y
) const
2923 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2925 if (gtk_widget_get_window(m_widget
) == NULL
) return;
2927 GdkWindow
*source
= NULL
;
2929 source
= gtk_widget_get_window(m_wxwindow
);
2931 source
= gtk_widget_get_window(m_widget
);
2935 gdk_window_get_origin( source
, &org_x
, &org_y
);
2939 if (!gtk_widget_get_has_window(m_widget
))
2942 gtk_widget_get_allocation(m_widget
, &a
);
2951 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2952 *x
= (GetClientSize().x
- *x
) + org_x
;
2960 void wxWindowGTK::DoScreenToClient( int *x
, int *y
) const
2962 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2964 if (!gtk_widget_get_realized(m_widget
)) return;
2966 GdkWindow
*source
= NULL
;
2968 source
= gtk_widget_get_window(m_wxwindow
);
2970 source
= gtk_widget_get_window(m_widget
);
2974 gdk_window_get_origin( source
, &org_x
, &org_y
);
2978 if (!gtk_widget_get_has_window(m_widget
))
2981 gtk_widget_get_allocation(m_widget
, &a
);
2989 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2990 *x
= (GetClientSize().x
- *x
) - org_x
;
2997 bool wxWindowGTK::Show( bool show
)
2999 if ( !wxWindowBase::Show(show
) )
3005 // notice that we may call Hide() before the window is created and this is
3006 // actually useful to create it hidden initially -- but we can't call
3007 // Show() before it is created
3010 wxASSERT_MSG( !show
, "can't show invalid window" );
3018 // defer until later
3022 gtk_widget_show(m_widget
);
3026 gtk_widget_hide(m_widget
);
3029 wxShowEvent
eventShow(GetId(), show
);
3030 eventShow
.SetEventObject(this);
3031 HandleWindowEvent(eventShow
);
3036 void wxWindowGTK::DoEnable( bool enable
)
3038 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3040 gtk_widget_set_sensitive( m_widget
, enable
);
3041 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
3042 gtk_widget_set_sensitive( m_wxwindow
, enable
);
3045 int wxWindowGTK::GetCharHeight() const
3047 wxCHECK_MSG( (m_widget
!= NULL
), 12, wxT("invalid window") );
3049 wxFont font
= GetFont();
3050 wxCHECK_MSG( font
.IsOk(), 12, wxT("invalid font") );
3052 PangoContext
* context
= gtk_widget_get_pango_context(m_widget
);
3057 PangoFontDescription
*desc
= font
.GetNativeFontInfo()->description
;
3058 PangoLayout
*layout
= pango_layout_new(context
);
3059 pango_layout_set_font_description(layout
, desc
);
3060 pango_layout_set_text(layout
, "H", 1);
3061 PangoLayoutLine
*line
= (PangoLayoutLine
*)pango_layout_get_lines(layout
)->data
;
3063 PangoRectangle rect
;
3064 pango_layout_line_get_extents(line
, NULL
, &rect
);
3066 g_object_unref (layout
);
3068 return (int) PANGO_PIXELS(rect
.height
);
3071 int wxWindowGTK::GetCharWidth() const
3073 wxCHECK_MSG( (m_widget
!= NULL
), 8, wxT("invalid window") );
3075 wxFont font
= GetFont();
3076 wxCHECK_MSG( font
.IsOk(), 8, wxT("invalid font") );
3078 PangoContext
* context
= gtk_widget_get_pango_context(m_widget
);
3083 PangoFontDescription
*desc
= font
.GetNativeFontInfo()->description
;
3084 PangoLayout
*layout
= pango_layout_new(context
);
3085 pango_layout_set_font_description(layout
, desc
);
3086 pango_layout_set_text(layout
, "g", 1);
3087 PangoLayoutLine
*line
= (PangoLayoutLine
*)pango_layout_get_lines(layout
)->data
;
3089 PangoRectangle rect
;
3090 pango_layout_line_get_extents(line
, NULL
, &rect
);
3092 g_object_unref (layout
);
3094 return (int) PANGO_PIXELS(rect
.width
);
3097 void wxWindowGTK::DoGetTextExtent( const wxString
& string
,
3101 int *externalLeading
,
3102 const wxFont
*theFont
) const
3104 wxFont fontToUse
= theFont
? *theFont
: GetFont();
3106 wxCHECK_RET( fontToUse
.IsOk(), wxT("invalid font") );
3115 PangoContext
*context
= NULL
;
3117 context
= gtk_widget_get_pango_context( m_widget
);
3126 PangoFontDescription
*desc
= fontToUse
.GetNativeFontInfo()->description
;
3127 PangoLayout
*layout
= pango_layout_new(context
);
3128 pango_layout_set_font_description(layout
, desc
);
3130 const wxCharBuffer data
= wxGTK_CONV( string
);
3132 pango_layout_set_text(layout
, data
, strlen(data
));
3135 PangoRectangle rect
;
3136 pango_layout_get_extents(layout
, NULL
, &rect
);
3138 if (x
) (*x
) = (wxCoord
) PANGO_PIXELS(rect
.width
);
3139 if (y
) (*y
) = (wxCoord
) PANGO_PIXELS(rect
.height
);
3142 PangoLayoutIter
*iter
= pango_layout_get_iter(layout
);
3143 int baseline
= pango_layout_iter_get_baseline(iter
);
3144 pango_layout_iter_free(iter
);
3145 *descent
= *y
- PANGO_PIXELS(baseline
);
3147 if (externalLeading
) (*externalLeading
) = 0; // ??
3149 g_object_unref (layout
);
3152 void wxWindowGTK::GTKDisableFocusOutEvent()
3154 g_signal_handlers_block_by_func( m_focusWidget
,
3155 (gpointer
) gtk_window_focus_out_callback
, this);
3158 void wxWindowGTK::GTKEnableFocusOutEvent()
3160 g_signal_handlers_unblock_by_func( m_focusWidget
,
3161 (gpointer
) gtk_window_focus_out_callback
, this);
3164 bool wxWindowGTK::GTKHandleFocusIn()
3166 // Disable default focus handling for custom windows since the default GTK+
3167 // handler issues a repaint
3168 const bool retval
= m_wxwindow
? true : false;
3171 // NB: if there's still unprocessed deferred focus-out event (see
3172 // GTKHandleFocusOut() for explanation), we need to process it first so
3173 // that the order of focus events -- focus-out first, then focus-in
3174 // elsewhere -- is preserved
3175 if ( gs_deferredFocusOut
)
3177 if ( GTKNeedsToFilterSameWindowFocus() &&
3178 gs_deferredFocusOut
== this )
3180 // GTK+ focus changed from this wxWindow back to itself, so don't
3181 // emit any events at all
3182 wxLogTrace(TRACE_FOCUS
,
3183 "filtered out spurious focus change within %s(%p, %s)",
3184 GetClassInfo()->GetClassName(), this, GetLabel());
3185 gs_deferredFocusOut
= NULL
;
3189 // otherwise we need to send focus-out first
3190 wxASSERT_MSG ( gs_deferredFocusOut
!= this,
3191 "GTKHandleFocusIn(GTKFocus_Normal) called even though focus changed back to itself - derived class should handle this" );
3192 GTKHandleDeferredFocusOut();
3196 wxLogTrace(TRACE_FOCUS
,
3197 "handling focus_in event for %s(%p, %s)",
3198 GetClassInfo()->GetClassName(), this, GetLabel());
3201 gtk_im_context_focus_in(m_imData
->context
);
3203 gs_currentFocus
= this;
3204 gs_pendingFocus
= NULL
;
3207 // caret needs to be informed about focus change
3208 wxCaret
*caret
= GetCaret();
3211 caret
->OnSetFocus();
3213 #endif // wxUSE_CARET
3215 // Notify the parent keeping track of focus for the kbd navigation
3216 // purposes that we got it.
3217 wxChildFocusEvent
eventChildFocus(static_cast<wxWindow
*>(this));
3218 GTKProcessEvent(eventChildFocus
);
3220 wxFocusEvent
eventFocus(wxEVT_SET_FOCUS
, GetId());
3221 eventFocus
.SetEventObject(this);
3222 GTKProcessEvent(eventFocus
);
3227 bool wxWindowGTK::GTKHandleFocusOut()
3229 // Disable default focus handling for custom windows since the default GTK+
3230 // handler issues a repaint
3231 const bool retval
= m_wxwindow
? true : false;
3234 // NB: If a control is composed of several GtkWidgets and when focus
3235 // changes from one of them to another within the same wxWindow, we get
3236 // a focus-out event followed by focus-in for another GtkWidget owned
3237 // by the same wx control. We don't want to generate two spurious
3238 // wxEVT_SET_FOCUS events in this case, so we defer sending wx events
3239 // from GTKHandleFocusOut() until we know for sure it's not coming back
3240 // (i.e. in GTKHandleFocusIn() or at idle time).
3241 if ( GTKNeedsToFilterSameWindowFocus() )
3243 wxASSERT_MSG( gs_deferredFocusOut
== NULL
,
3244 "deferred focus out event already pending" );
3245 wxLogTrace(TRACE_FOCUS
,
3246 "deferring focus_out event for %s(%p, %s)",
3247 GetClassInfo()->GetClassName(), this, GetLabel());
3248 gs_deferredFocusOut
= this;
3252 GTKHandleFocusOutNoDeferring();
3257 void wxWindowGTK::GTKHandleFocusOutNoDeferring()
3259 wxLogTrace(TRACE_FOCUS
,
3260 "handling focus_out event for %s(%p, %s)",
3261 GetClassInfo()->GetClassName(), this, GetLabel());
3264 gtk_im_context_focus_out(m_imData
->context
);
3266 if ( gs_currentFocus
!= this )
3268 // Something is terribly wrong, gs_currentFocus is out of sync with the
3269 // real focus. We will reset it to NULL anyway, because after this
3270 // focus-out event is handled, one of the following with happen:
3272 // * either focus will go out of the app altogether, in which case
3273 // gs_currentFocus _should_ be NULL
3275 // * or it goes to another control, in which case focus-in event will
3276 // follow immediately and it will set gs_currentFocus to the right
3278 wxLogDebug("window %s(%p, %s) lost focus even though it didn't have it",
3279 GetClassInfo()->GetClassName(), this, GetLabel());
3281 gs_currentFocus
= NULL
;
3284 // caret needs to be informed about focus change
3285 wxCaret
*caret
= GetCaret();
3288 caret
->OnKillFocus();
3290 #endif // wxUSE_CARET
3292 wxFocusEvent
event( wxEVT_KILL_FOCUS
, GetId() );
3293 event
.SetEventObject( this );
3294 event
.SetWindow( FindFocus() );
3295 GTKProcessEvent( event
);
3299 void wxWindowGTK::GTKHandleDeferredFocusOut()
3301 // NB: See GTKHandleFocusOut() for explanation. This function is called
3302 // from either GTKHandleFocusIn() or OnInternalIdle() to process
3304 if ( gs_deferredFocusOut
)
3306 wxWindowGTK
*win
= gs_deferredFocusOut
;
3307 gs_deferredFocusOut
= NULL
;
3309 wxLogTrace(TRACE_FOCUS
,
3310 "processing deferred focus_out event for %s(%p, %s)",
3311 win
->GetClassInfo()->GetClassName(), win
, win
->GetLabel());
3313 win
->GTKHandleFocusOutNoDeferring();
3317 void wxWindowGTK::SetFocus()
3319 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
3321 // Setting "physical" focus is not immediate in GTK+ and while
3322 // gtk_widget_is_focus ("determines if the widget is the focus widget
3323 // within its toplevel", i.e. returns true for one widget per TLW, not
3324 // globally) returns true immediately after grabbing focus,
3325 // GTK_WIDGET_HAS_FOCUS (which returns true only for the one widget that
3326 // has focus at the moment) takes effect only after the window is shown
3327 // (if it was hidden at the moment of the call) or at the next event loop
3330 // Because we want to FindFocus() call immediately following
3331 // foo->SetFocus() to return foo, we have to keep track of "pending" focus
3333 gs_pendingFocus
= this;
3335 GtkWidget
*widget
= m_wxwindow
? m_wxwindow
: m_focusWidget
;
3337 if ( GTK_IS_CONTAINER(widget
) &&
3338 !gtk_widget_get_can_focus(widget
) )
3340 wxLogTrace(TRACE_FOCUS
,
3341 wxT("Setting focus to a child of %s(%p, %s)"),
3342 GetClassInfo()->GetClassName(), this, GetLabel().c_str());
3343 gtk_widget_child_focus(widget
, GTK_DIR_TAB_FORWARD
);
3347 wxLogTrace(TRACE_FOCUS
,
3348 wxT("Setting focus to %s(%p, %s)"),
3349 GetClassInfo()->GetClassName(), this, GetLabel().c_str());
3350 gtk_widget_grab_focus(widget
);
3354 void wxWindowGTK::SetCanFocus(bool canFocus
)
3356 gtk_widget_set_can_focus(m_widget
, canFocus
);
3358 if ( m_wxwindow
&& (m_widget
!= m_wxwindow
) )
3360 gtk_widget_set_can_focus(m_wxwindow
, canFocus
);
3364 bool wxWindowGTK::Reparent( wxWindowBase
*newParentBase
)
3366 wxCHECK_MSG( (m_widget
!= NULL
), false, wxT("invalid window") );
3368 wxWindowGTK
* const newParent
= (wxWindowGTK
*)newParentBase
;
3370 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3372 if ( !wxWindowBase::Reparent(newParent
) )
3375 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3377 // Notice that old m_parent pointer might be non-NULL here but the widget
3378 // still not have any parent at GTK level if it's a notebook page that had
3379 // been removed from the notebook so test this at GTK level and not wx one.
3380 if ( GtkWidget
*parentGTK
= gtk_widget_get_parent(m_widget
) )
3381 gtk_container_remove(GTK_CONTAINER(parentGTK
), m_widget
);
3383 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3387 if (gtk_widget_get_visible (newParent
->m_widget
))
3389 m_showOnIdle
= true;
3390 gtk_widget_hide( m_widget
);
3392 /* insert GTK representation */
3393 newParent
->AddChildGTK(this);
3396 SetLayoutDirection(wxLayout_Default
);
3401 void wxWindowGTK::DoAddChild(wxWindowGTK
*child
)
3403 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
3404 wxASSERT_MSG( (child
!= NULL
), wxT("invalid child window") );
3409 /* insert GTK representation */
3413 void wxWindowGTK::AddChild(wxWindowBase
*child
)
3415 wxWindowBase::AddChild(child
);
3416 m_dirtyTabOrder
= true;
3417 wxTheApp
->WakeUpIdle();
3420 void wxWindowGTK::RemoveChild(wxWindowBase
*child
)
3422 wxWindowBase::RemoveChild(child
);
3423 m_dirtyTabOrder
= true;
3424 wxTheApp
->WakeUpIdle();
3428 wxLayoutDirection
wxWindowGTK::GTKGetLayout(GtkWidget
*widget
)
3430 return gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
3431 ? wxLayout_RightToLeft
3432 : wxLayout_LeftToRight
;
3436 void wxWindowGTK::GTKSetLayout(GtkWidget
*widget
, wxLayoutDirection dir
)
3438 wxASSERT_MSG( dir
!= wxLayout_Default
, wxT("invalid layout direction") );
3440 gtk_widget_set_direction(widget
,
3441 dir
== wxLayout_RightToLeft
? GTK_TEXT_DIR_RTL
3442 : GTK_TEXT_DIR_LTR
);
3445 wxLayoutDirection
wxWindowGTK::GetLayoutDirection() const
3447 return GTKGetLayout(m_widget
);
3450 void wxWindowGTK::SetLayoutDirection(wxLayoutDirection dir
)
3452 if ( dir
== wxLayout_Default
)
3454 const wxWindow
*const parent
= GetParent();
3457 // inherit layout from parent.
3458 dir
= parent
->GetLayoutDirection();
3460 else // no parent, use global default layout
3462 dir
= wxTheApp
->GetLayoutDirection();
3466 if ( dir
== wxLayout_Default
)
3469 GTKSetLayout(m_widget
, dir
);
3471 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
3472 GTKSetLayout(m_wxwindow
, dir
);
3476 wxWindowGTK::AdjustForLayoutDirection(wxCoord x
,
3477 wxCoord
WXUNUSED(width
),
3478 wxCoord
WXUNUSED(widthTotal
)) const
3480 // We now mirror the coordinates of RTL windows in wxPizza
3484 void wxWindowGTK::DoMoveInTabOrder(wxWindow
*win
, WindowOrder move
)
3486 wxWindowBase::DoMoveInTabOrder(win
, move
);
3487 m_dirtyTabOrder
= true;
3488 wxTheApp
->WakeUpIdle();
3491 bool wxWindowGTK::DoNavigateIn(int flags
)
3493 if ( flags
& wxNavigationKeyEvent::WinChange
)
3495 wxFAIL_MSG( wxT("not implemented") );
3499 else // navigate inside the container
3501 wxWindow
*parent
= wxGetTopLevelParent((wxWindow
*)this);
3502 wxCHECK_MSG( parent
, false, wxT("every window must have a TLW parent") );
3504 GtkDirectionType dir
;
3505 dir
= flags
& wxNavigationKeyEvent::IsForward
? GTK_DIR_TAB_FORWARD
3506 : GTK_DIR_TAB_BACKWARD
;
3509 g_signal_emit_by_name(parent
->m_widget
, "focus", dir
, &rc
);
3515 bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3517 // none needed by default
3521 void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget
* WXUNUSED(w
))
3523 // nothing to do by default since none is needed
3526 void wxWindowGTK::RealizeTabOrder()
3530 if ( !m_children
.empty() )
3532 // we don't only construct the correct focus chain but also use
3533 // this opportunity to update the mnemonic widgets for the widgets
3536 GList
*chain
= NULL
;
3537 wxWindowGTK
* mnemonicWindow
= NULL
;
3539 for ( wxWindowList::const_iterator i
= m_children
.begin();
3540 i
!= m_children
.end();
3543 wxWindowGTK
*win
= *i
;
3545 bool focusableFromKeyboard
= win
->AcceptsFocusFromKeyboard();
3547 if ( mnemonicWindow
)
3549 if ( focusableFromKeyboard
)
3551 // wxComboBox et al. needs to focus on on a different
3552 // widget than m_widget, so if the main widget isn't
3553 // focusable try the connect widget
3554 GtkWidget
* w
= win
->m_widget
;
3555 if ( !gtk_widget_get_can_focus(w
) )
3557 w
= win
->GetConnectWidget();
3558 if ( !gtk_widget_get_can_focus(w
) )
3564 mnemonicWindow
->GTKWidgetDoSetMnemonic(w
);
3565 mnemonicWindow
= NULL
;
3569 else if ( win
->GTKWidgetNeedsMnemonic() )
3571 mnemonicWindow
= win
;
3574 if ( focusableFromKeyboard
)
3575 chain
= g_list_prepend(chain
, win
->m_widget
);
3578 chain
= g_list_reverse(chain
);
3580 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow
), chain
);
3585 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow
));
3590 void wxWindowGTK::Raise()
3592 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3594 if (m_wxwindow
&& gtk_widget_get_window(m_wxwindow
))
3596 gdk_window_raise(gtk_widget_get_window(m_wxwindow
));
3598 else if (gtk_widget_get_window(m_widget
))
3600 gdk_window_raise(gtk_widget_get_window(m_widget
));
3604 void wxWindowGTK::Lower()
3606 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3608 if (m_wxwindow
&& gtk_widget_get_window(m_wxwindow
))
3610 gdk_window_lower(gtk_widget_get_window(m_wxwindow
));
3612 else if (gtk_widget_get_window(m_widget
))
3614 gdk_window_lower(gtk_widget_get_window(m_widget
));
3618 bool wxWindowGTK::SetCursor( const wxCursor
&cursor
)
3620 if ( !wxWindowBase::SetCursor(cursor
.IsOk() ? cursor
: *wxSTANDARD_CURSOR
) )
3628 void wxWindowGTK::GTKUpdateCursor(bool update_self
/*=true*/, bool recurse
/*=true*/)
3632 wxCursor
cursor(g_globalCursor
.IsOk() ? g_globalCursor
: GetCursor());
3633 if ( cursor
.IsOk() )
3635 wxArrayGdkWindows windowsThis
;
3636 GdkWindow
* window
= GTKGetWindow(windowsThis
);
3638 gdk_window_set_cursor( window
, cursor
.GetCursor() );
3641 const size_t count
= windowsThis
.size();
3642 for ( size_t n
= 0; n
< count
; n
++ )
3644 GdkWindow
*win
= windowsThis
[n
];
3645 // It can be zero if the window has not been realized yet.
3648 gdk_window_set_cursor(win
, cursor
.GetCursor());
3657 for (wxWindowList::iterator it
= GetChildren().begin(); it
!= GetChildren().end(); ++it
)
3659 (*it
)->GTKUpdateCursor( true );
3664 void wxWindowGTK::WarpPointer( int x
, int y
)
3666 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3668 ClientToScreen(&x
, &y
);
3669 GdkDisplay
* display
= gtk_widget_get_display(m_widget
);
3670 GdkScreen
* screen
= gtk_widget_get_screen(m_widget
);
3672 GdkDeviceManager
* manager
= gdk_display_get_device_manager(display
);
3673 gdk_device_warp(gdk_device_manager_get_client_pointer(manager
), screen
, x
, y
);
3675 #ifdef GDK_WINDOWING_X11
3676 XWarpPointer(GDK_DISPLAY_XDISPLAY(display
),
3678 GDK_WINDOW_XID(gdk_screen_get_root_window(screen
)),
3684 wxWindowGTK::ScrollDir
wxWindowGTK::ScrollDirFromRange(GtkRange
*range
) const
3686 // find the scrollbar which generated the event
3687 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
3689 if ( range
== m_scrollBar
[dir
] )
3690 return (ScrollDir
)dir
;
3693 wxFAIL_MSG( wxT("event from unknown scrollbar received") );
3695 return ScrollDir_Max
;
3698 bool wxWindowGTK::DoScrollByUnits(ScrollDir dir
, ScrollUnit unit
, int units
)
3700 bool changed
= false;
3701 GtkRange
* range
= m_scrollBar
[dir
];
3702 if ( range
&& units
)
3704 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
3705 double inc
= unit
== ScrollUnit_Line
? gtk_adjustment_get_step_increment(adj
)
3706 : gtk_adjustment_get_page_increment(adj
);
3708 const int posOld
= wxRound(gtk_adjustment_get_value(adj
));
3709 gtk_range_set_value(range
, posOld
+ units
*inc
);
3711 changed
= wxRound(gtk_adjustment_get_value(adj
)) != posOld
;
3717 bool wxWindowGTK::ScrollLines(int lines
)
3719 return DoScrollByUnits(ScrollDir_Vert
, ScrollUnit_Line
, lines
);
3722 bool wxWindowGTK::ScrollPages(int pages
)
3724 return DoScrollByUnits(ScrollDir_Vert
, ScrollUnit_Page
, pages
);
3727 void wxWindowGTK::Refresh(bool WXUNUSED(eraseBackground
),
3732 if (gtk_widget_get_mapped(m_wxwindow
))
3734 GdkWindow
* window
= gtk_widget_get_window(m_wxwindow
);
3737 GdkRectangle r
= { rect
->x
, rect
->y
, rect
->width
, rect
->height
};
3738 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3739 r
.x
= gdk_window_get_width(window
) - r
.x
- rect
->width
;
3740 gdk_window_invalidate_rect(window
, &r
, true);
3743 gdk_window_invalidate_rect(window
, NULL
, true);
3748 if (gtk_widget_get_mapped(m_widget
))
3751 gtk_widget_queue_draw_area(m_widget
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
3753 gtk_widget_queue_draw(m_widget
);
3758 void wxWindowGTK::Update()
3760 if (m_widget
&& gtk_widget_get_mapped(m_widget
))
3762 GdkDisplay
* display
= gtk_widget_get_display(m_widget
);
3763 // Flush everything out to the server, and wait for it to finish.
3764 // This ensures nothing will overwrite the drawing we are about to do.
3765 gdk_display_sync(display
);
3767 GdkWindow
* window
= GTKGetDrawingWindow();
3769 window
= gtk_widget_get_window(m_widget
);
3770 gdk_window_process_updates(window
, true);
3772 // Flush again, but no need to wait for it to finish
3773 gdk_display_flush(display
);
3777 bool wxWindowGTK::DoIsExposed( int x
, int y
) const
3779 return m_updateRegion
.Contains(x
, y
) != wxOutRegion
;
3782 bool wxWindowGTK::DoIsExposed( int x
, int y
, int w
, int h
) const
3784 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3785 return m_updateRegion
.Contains(x
-w
, y
, w
, h
) != wxOutRegion
;
3787 return m_updateRegion
.Contains(x
, y
, w
, h
) != wxOutRegion
;
3791 void wxWindowGTK::GTKSendPaintEvents(cairo_t
* cr
)
3793 void wxWindowGTK::GTKSendPaintEvents(const GdkRegion
* region
)
3797 m_paintContext
= cr
;
3798 double x1
, y1
, x2
, y2
;
3799 cairo_clip_extents(cr
, &x1
, &y1
, &x2
, &y2
);
3800 m_updateRegion
= wxRegion(int(x1
), int(y1
), int(x2
- x1
), int(y2
- y1
));
3801 #else // !__WXGTK3__
3802 m_updateRegion
= wxRegion(region
);
3803 #if wxGTK_HAS_COMPOSITING_SUPPORT
3806 #endif // !__WXGTK3__
3807 // Clip to paint region in wxClientDC
3808 m_clipPaintRegion
= true;
3810 m_nativeUpdateRegion
= m_updateRegion
;
3812 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3814 // Transform m_updateRegion under RTL
3815 m_updateRegion
.Clear();
3817 const int width
= gdk_window_get_width(GTKGetDrawingWindow());
3819 wxRegionIterator
upd( m_nativeUpdateRegion
);
3823 rect
.x
= upd
.GetX();
3824 rect
.y
= upd
.GetY();
3825 rect
.width
= upd
.GetWidth();
3826 rect
.height
= upd
.GetHeight();
3828 rect
.x
= width
- rect
.x
- rect
.width
;
3829 m_updateRegion
.Union( rect
);
3835 switch ( GetBackgroundStyle() )
3837 case wxBG_STYLE_TRANSPARENT
:
3838 #if wxGTK_HAS_COMPOSITING_SUPPORT
3839 if (IsTransparentBackgroundSupported())
3841 // Set a transparent background, so that overlaying in parent
3842 // might indeed let see through where this child did not
3843 // explicitly paint.
3844 // NB: it works also for top level windows (but this is the
3845 // windows manager which then does the compositing job)
3847 cr
= gdk_cairo_create(m_wxwindow
->window
);
3848 gdk_cairo_region(cr
, m_nativeUpdateRegion
.GetRegion());
3851 cairo_set_operator(cr
, CAIRO_OPERATOR_CLEAR
);
3853 cairo_set_operator(cr
, CAIRO_OPERATOR_OVER
);
3855 cairo_surface_flush(cairo_get_target(cr
));
3858 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
3861 case wxBG_STYLE_ERASE
:
3864 wxGTKCairoDC
dc(cr
);
3866 wxWindowDC
dc( (wxWindow
*)this );
3867 dc
.SetDeviceClippingRegion( m_updateRegion
);
3869 // Work around gtk-qt <= 0.60 bug whereby the window colour
3873 GetOptionInt("gtk.window.force-background-colour") )
3875 dc
.SetBackground(GetBackgroundColour());
3878 #endif // !__WXGTK3__
3879 wxEraseEvent
erase_event( GetId(), &dc
);
3880 erase_event
.SetEventObject( this );
3882 if ( HandleWindowEvent(erase_event
) )
3884 // background erased, don't do it again
3890 case wxBG_STYLE_SYSTEM
:
3891 if ( GetThemeEnabled() )
3893 GdkWindow
* gdkWindow
= GTKGetDrawingWindow();
3894 const int w
= gdk_window_get_width(gdkWindow
);
3895 const int h
= gdk_window_get_height(gdkWindow
);
3897 GtkStyleContext
* sc
= gtk_widget_get_style_context(m_wxwindow
);
3898 gtk_render_background(sc
, cr
, 0, 0, w
, h
);
3900 // find ancestor from which to steal background
3901 wxWindow
*parent
= wxGetTopLevelParent((wxWindow
*)this);
3903 parent
= (wxWindow
*)this;
3905 m_nativeUpdateRegion
.GetBox(rect
.x
, rect
.y
, rect
.width
, rect
.height
);
3906 gtk_paint_flat_box(gtk_widget_get_style(parent
->m_widget
),
3908 gtk_widget_get_state(m_wxwindow
),
3914 #endif // !__WXGTK3__
3918 case wxBG_STYLE_PAINT
:
3919 // nothing to do: window will be painted over in EVT_PAINT
3923 wxFAIL_MSG( "unsupported background style" );
3926 wxNcPaintEvent
nc_paint_event( GetId() );
3927 nc_paint_event
.SetEventObject( this );
3928 HandleWindowEvent( nc_paint_event
);
3930 wxPaintEvent
paint_event( GetId() );
3931 paint_event
.SetEventObject( this );
3932 HandleWindowEvent( paint_event
);
3934 #if wxGTK_HAS_COMPOSITING_SUPPORT
3935 if (IsTransparentBackgroundSupported())
3936 { // now composite children which need it
3937 // Overlay all our composite children on top of the painted area
3938 wxWindowList::compatibility_iterator node
;
3939 for ( node
= m_children
.GetFirst(); node
; node
= node
->GetNext() )
3941 wxWindow
*compositeChild
= node
->GetData();
3942 if (compositeChild
->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT
)
3947 cr
= gdk_cairo_create(m_wxwindow
->window
);
3948 gdk_cairo_region(cr
, m_nativeUpdateRegion
.GetRegion());
3951 #endif // !__WXGTK3__
3952 GtkWidget
*child
= compositeChild
->m_wxwindow
;
3953 GtkAllocation alloc
;
3954 gtk_widget_get_allocation(child
, &alloc
);
3956 // The source data is the (composited) child
3957 gdk_cairo_set_source_window(
3958 cr
, gtk_widget_get_window(child
), alloc
.x
, alloc
.y
);
3968 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
3970 m_clipPaintRegion
= false;
3972 m_paintContext
= NULL
;
3974 m_updateRegion
.Clear();
3975 m_nativeUpdateRegion
.Clear();
3978 void wxWindowGTK::SetDoubleBuffered( bool on
)
3980 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3983 gtk_widget_set_double_buffered( m_wxwindow
, on
);
3986 bool wxWindowGTK::IsDoubleBuffered() const
3988 return gtk_widget_get_double_buffered( m_wxwindow
) != 0;
3991 void wxWindowGTK::ClearBackground()
3993 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
3997 void wxWindowGTK::DoSetToolTip( wxToolTip
*tip
)
3999 if (m_tooltip
!= tip
)
4001 wxWindowBase::DoSetToolTip(tip
);
4004 m_tooltip
->GTKSetWindow(static_cast<wxWindow
*>(this));
4006 GTKApplyToolTip(NULL
);
4010 void wxWindowGTK::GTKApplyToolTip(const char* tip
)
4012 wxToolTip::GTKApply(GetConnectWidget(), tip
);
4014 #endif // wxUSE_TOOLTIPS
4016 bool wxWindowGTK::SetBackgroundColour( const wxColour
&colour
)
4018 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
4020 if (!wxWindowBase::SetBackgroundColour(colour
))
4026 // We need the pixel value e.g. for background clearing.
4027 m_backgroundColour
.CalcPixel(gtk_widget_get_colormap(m_widget
));
4031 // apply style change (forceStyle=true so that new style is applied
4032 // even if the bg colour changed from valid to wxNullColour)
4033 GTKApplyWidgetStyle(true);
4038 bool wxWindowGTK::SetForegroundColour( const wxColour
&colour
)
4040 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
4042 if (!wxWindowBase::SetForegroundColour(colour
))
4050 // We need the pixel value e.g. for background clearing.
4051 m_foregroundColour
.CalcPixel(gtk_widget_get_colormap(m_widget
));
4055 // apply style change (forceStyle=true so that new style is applied
4056 // even if the bg colour changed from valid to wxNullColour):
4057 GTKApplyWidgetStyle(true);
4062 PangoContext
*wxWindowGTK::GTKGetPangoDefaultContext()
4064 return gtk_widget_get_pango_context( m_widget
);
4068 GtkRcStyle
*wxWindowGTK::GTKCreateWidgetStyle(bool forceStyle
)
4070 // do we need to apply any changes at all?
4073 !m_foregroundColour
.IsOk() && !m_backgroundColour
.IsOk() )
4078 GtkRcStyle
*style
= gtk_rc_style_new();
4080 if ( m_font
.IsOk() )
4083 pango_font_description_copy( m_font
.GetNativeFontInfo()->description
);
4086 int flagsNormal
= 0,
4089 flagsInsensitive
= 0;
4091 if ( m_foregroundColour
.IsOk() )
4093 const GdkColor
*fg
= m_foregroundColour
.GetColor();
4095 style
->fg
[GTK_STATE_NORMAL
] =
4096 style
->text
[GTK_STATE_NORMAL
] = *fg
;
4097 flagsNormal
|= GTK_RC_FG
| GTK_RC_TEXT
;
4099 style
->fg
[GTK_STATE_PRELIGHT
] =
4100 style
->text
[GTK_STATE_PRELIGHT
] = *fg
;
4101 flagsPrelight
|= GTK_RC_FG
| GTK_RC_TEXT
;
4103 style
->fg
[GTK_STATE_ACTIVE
] =
4104 style
->text
[GTK_STATE_ACTIVE
] = *fg
;
4105 flagsActive
|= GTK_RC_FG
| GTK_RC_TEXT
;
4108 if ( m_backgroundColour
.IsOk() )
4110 const GdkColor
*bg
= m_backgroundColour
.GetColor();
4112 style
->bg
[GTK_STATE_NORMAL
] =
4113 style
->base
[GTK_STATE_NORMAL
] = *bg
;
4114 flagsNormal
|= GTK_RC_BG
| GTK_RC_BASE
;
4116 style
->bg
[GTK_STATE_PRELIGHT
] =
4117 style
->base
[GTK_STATE_PRELIGHT
] = *bg
;
4118 flagsPrelight
|= GTK_RC_BG
| GTK_RC_BASE
;
4120 style
->bg
[GTK_STATE_ACTIVE
] =
4121 style
->base
[GTK_STATE_ACTIVE
] = *bg
;
4122 flagsActive
|= GTK_RC_BG
| GTK_RC_BASE
;
4124 style
->bg
[GTK_STATE_INSENSITIVE
] =
4125 style
->base
[GTK_STATE_INSENSITIVE
] = *bg
;
4126 flagsInsensitive
|= GTK_RC_BG
| GTK_RC_BASE
;
4129 style
->color_flags
[GTK_STATE_NORMAL
] = (GtkRcFlags
)flagsNormal
;
4130 style
->color_flags
[GTK_STATE_PRELIGHT
] = (GtkRcFlags
)flagsPrelight
;
4131 style
->color_flags
[GTK_STATE_ACTIVE
] = (GtkRcFlags
)flagsActive
;
4132 style
->color_flags
[GTK_STATE_INSENSITIVE
] = (GtkRcFlags
)flagsInsensitive
;
4136 #endif // !__WXGTK3__
4138 void wxWindowGTK::GTKApplyWidgetStyle(bool WXUNUSED_IN_GTK3(forceStyle
))
4141 DoApplyWidgetStyle(NULL
);
4143 GtkRcStyle
*style
= GTKCreateWidgetStyle(forceStyle
);
4146 DoApplyWidgetStyle(style
);
4147 g_object_unref(style
);
4151 // Style change may affect GTK+'s size calculation:
4152 InvalidateBestSize();
4155 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle
*style
)
4157 GtkWidget
* widget
= m_wxwindow
? m_wxwindow
: m_widget
;
4159 // block the signal temporarily to avoid sending
4160 // wxSysColourChangedEvents when we change the colours ourselves
4161 bool unblock
= false;
4162 if (m_wxwindow
&& IsTopLevel())
4165 g_signal_handlers_block_by_func(
4166 m_wxwindow
, (void*)style_updated
, this);
4169 GTKApplyStyle(widget
, style
);
4173 g_signal_handlers_unblock_by_func(
4174 m_wxwindow
, (void*)style_updated
, this);
4178 void wxWindowGTK::GTKApplyStyle(GtkWidget
* widget
, GtkRcStyle
* WXUNUSED_IN_GTK3(style
))
4181 const PangoFontDescription
* pfd
= NULL
;
4183 pfd
= pango_font_description_copy(m_font
.GetNativeFontInfo()->description
);
4184 gtk_widget_override_font(widget
, pfd
);
4185 gtk_widget_override_color(widget
, GTK_STATE_FLAG_NORMAL
, m_foregroundColour
);
4186 gtk_widget_override_background_color(widget
, GTK_STATE_FLAG_NORMAL
, m_backgroundColour
);
4188 gtk_widget_modify_style(widget
, style
);
4192 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style
)
4194 if (!wxWindowBase::SetBackgroundStyle(style
))
4201 window
= GTKGetDrawingWindow();
4205 GtkWidget
* const w
= GetConnectWidget();
4206 window
= w
? gtk_widget_get_window(w
) : NULL
;
4209 bool wantNoBackPixmap
= style
== wxBG_STYLE_PAINT
|| style
== wxBG_STYLE_TRANSPARENT
;
4211 if ( wantNoBackPixmap
)
4215 // Make sure GDK/X11 doesn't refresh the window
4217 gdk_window_set_back_pixmap( window
, NULL
, FALSE
);
4218 m_needsStyleChange
= false;
4220 else // window not realized yet
4222 // Do when window is realized
4223 m_needsStyleChange
= true;
4226 // Don't apply widget style, or we get a grey background
4230 // apply style change (forceStyle=true so that new style is applied
4231 // even if the bg colour changed from valid to wxNullColour):
4232 GTKApplyWidgetStyle(true);
4234 #endif // !__WXGTK3__
4239 bool wxWindowGTK::IsTransparentBackgroundSupported(wxString
* reason
) const
4241 #if wxGTK_HAS_COMPOSITING_SUPPORT
4243 if (gtk_check_version(wxGTK_VERSION_REQUIRED_FOR_COMPOSITING
) != NULL
)
4247 *reason
= _("GTK+ installed on this machine is too old to "
4248 "support screen compositing, please install "
4249 "GTK+ 2.12 or later.");
4254 #endif // !__WXGTK3__
4256 // NB: We don't check here if the particular kind of widget supports
4257 // transparency, we check only if it would be possible for a generic window
4259 wxCHECK_MSG ( m_widget
, false, "Window must be created first" );
4261 if (!gdk_screen_is_composited(gtk_widget_get_screen(m_widget
)))
4265 *reason
= _("Compositing not supported by this system, "
4266 "please enable it in your Window Manager.");
4276 *reason
= _("This program was compiled with a too old version of GTK+, "
4277 "please rebuild with GTK+ 2.12 or newer.");
4281 #endif // wxGTK_HAS_COMPOSITING_SUPPORT/!wxGTK_HAS_COMPOSITING_SUPPORT
4284 // ----------------------------------------------------------------------------
4285 // Pop-up menu stuff
4286 // ----------------------------------------------------------------------------
4288 #if wxUSE_MENUS_NATIVE
4292 void wxPopupMenuPositionCallback( GtkMenu
*menu
,
4294 gboolean
* WXUNUSED(whatever
),
4295 gpointer user_data
)
4297 // ensure that the menu appears entirely on screen
4299 gtk_widget_get_child_requisition(GTK_WIDGET(menu
), &req
);
4301 wxSize sizeScreen
= wxGetDisplaySize();
4302 wxPoint
*pos
= (wxPoint
*)user_data
;
4304 gint xmax
= sizeScreen
.x
- req
.width
,
4305 ymax
= sizeScreen
.y
- req
.height
;
4307 *x
= pos
->x
< xmax
? pos
->x
: xmax
;
4308 *y
= pos
->y
< ymax
? pos
->y
: ymax
;
4312 bool wxWindowGTK::DoPopupMenu( wxMenu
*menu
, int x
, int y
)
4314 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
4320 GtkMenuPositionFunc posfunc
;
4321 if ( x
== -1 && y
== -1 )
4323 // use GTK's default positioning algorithm
4329 pos
= ClientToScreen(wxPoint(x
, y
));
4331 posfunc
= wxPopupMenuPositionCallback
;
4334 menu
->m_popupShown
= true;
4336 GTK_MENU(menu
->m_menu
),
4337 NULL
, // parent menu shell
4338 NULL
, // parent menu item
4339 posfunc
, // function to position it
4340 userdata
, // client data
4341 0, // button used to activate it
4342 gtk_get_current_event_time()
4345 while (menu
->m_popupShown
)
4347 gtk_main_iteration();
4353 #endif // wxUSE_MENUS_NATIVE
4355 #if wxUSE_DRAG_AND_DROP
4357 void wxWindowGTK::SetDropTarget( wxDropTarget
*dropTarget
)
4359 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4361 GtkWidget
*dnd_widget
= GetConnectWidget();
4363 if (m_dropTarget
) m_dropTarget
->GtkUnregisterWidget( dnd_widget
);
4365 if (m_dropTarget
) delete m_dropTarget
;
4366 m_dropTarget
= dropTarget
;
4368 if (m_dropTarget
) m_dropTarget
->GtkRegisterWidget( dnd_widget
);
4371 #endif // wxUSE_DRAG_AND_DROP
4373 GtkWidget
* wxWindowGTK::GetConnectWidget()
4375 GtkWidget
*connect_widget
= m_widget
;
4376 if (m_wxwindow
) connect_widget
= m_wxwindow
;
4378 return connect_widget
;
4381 bool wxWindowGTK::GTKIsOwnWindow(GdkWindow
*window
) const
4383 wxArrayGdkWindows windowsThis
;
4384 GdkWindow
* const winThis
= GTKGetWindow(windowsThis
);
4386 return winThis
? window
== winThis
4387 : windowsThis
.Index(window
) != wxNOT_FOUND
;
4390 GdkWindow
*wxWindowGTK::GTKGetWindow(wxArrayGdkWindows
& WXUNUSED(windows
)) const
4392 return m_wxwindow
? GTKGetDrawingWindow() : gtk_widget_get_window(m_widget
);
4395 bool wxWindowGTK::SetFont( const wxFont
&font
)
4397 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
4399 if (!wxWindowBase::SetFont(font
))
4402 // apply style change (forceStyle=true so that new style is applied
4403 // even if the font changed from valid to wxNullFont):
4404 GTKApplyWidgetStyle(true);
4409 void wxWindowGTK::DoCaptureMouse()
4411 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4413 GdkWindow
*window
= NULL
;
4415 window
= GTKGetDrawingWindow();
4417 window
= gtk_widget_get_window(GetConnectWidget());
4419 wxCHECK_RET( window
, wxT("CaptureMouse() failed") );
4421 const wxCursor
* cursor
= &m_cursor
;
4422 if (!cursor
->IsOk())
4423 cursor
= wxSTANDARD_CURSOR
;
4425 gdk_pointer_grab( window
, FALSE
,
4427 (GDK_BUTTON_PRESS_MASK
|
4428 GDK_BUTTON_RELEASE_MASK
|
4429 GDK_POINTER_MOTION_HINT_MASK
|
4430 GDK_POINTER_MOTION_MASK
),
4432 cursor
->GetCursor(),
4433 (guint32
)GDK_CURRENT_TIME
);
4434 g_captureWindow
= this;
4435 g_captureWindowHasMouse
= true;
4438 void wxWindowGTK::DoReleaseMouse()
4440 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4442 wxCHECK_RET( g_captureWindow
, wxT("can't release mouse - not captured") );
4444 g_captureWindow
= NULL
;
4446 GdkWindow
*window
= NULL
;
4448 window
= GTKGetDrawingWindow();
4450 window
= gtk_widget_get_window(GetConnectWidget());
4455 gdk_pointer_ungrab ( (guint32
)GDK_CURRENT_TIME
);
4458 void wxWindowGTK::GTKReleaseMouseAndNotify()
4461 wxMouseCaptureLostEvent
evt(GetId());
4462 evt
.SetEventObject( this );
4463 HandleWindowEvent( evt
);
4467 wxWindow
*wxWindowBase::GetCapture()
4469 return (wxWindow
*)g_captureWindow
;
4472 bool wxWindowGTK::IsRetained() const
4477 void wxWindowGTK::SetScrollbar(int orient
,
4481 bool WXUNUSED(update
))
4483 const int dir
= ScrollDirFromOrient(orient
);
4484 GtkRange
* const sb
= m_scrollBar
[dir
];
4485 wxCHECK_RET( sb
, wxT("this window is not scrollable") );
4489 // GtkRange requires upper > lower
4494 g_signal_handlers_block_by_func(
4495 sb
, (void*)gtk_scrollbar_value_changed
, this);
4497 gtk_range_set_increments(sb
, 1, thumbVisible
);
4498 gtk_adjustment_set_page_size(gtk_range_get_adjustment(sb
), thumbVisible
);
4499 gtk_range_set_range(sb
, 0, range
);
4500 gtk_range_set_value(sb
, pos
);
4501 m_scrollPos
[dir
] = gtk_range_get_value(sb
);
4503 g_signal_handlers_unblock_by_func(
4504 sb
, (void*)gtk_scrollbar_value_changed
, this);
4507 void wxWindowGTK::SetScrollPos(int orient
, int pos
, bool WXUNUSED(refresh
))
4509 const int dir
= ScrollDirFromOrient(orient
);
4510 GtkRange
* const sb
= m_scrollBar
[dir
];
4511 wxCHECK_RET( sb
, wxT("this window is not scrollable") );
4513 // This check is more than an optimization. Without it, the slider
4514 // will not move smoothly while tracking when using wxScrollHelper.
4515 if (GetScrollPos(orient
) != pos
)
4517 g_signal_handlers_block_by_func(
4518 sb
, (void*)gtk_scrollbar_value_changed
, this);
4520 gtk_range_set_value(sb
, pos
);
4521 m_scrollPos
[dir
] = gtk_range_get_value(sb
);
4523 g_signal_handlers_unblock_by_func(
4524 sb
, (void*)gtk_scrollbar_value_changed
, this);
4528 int wxWindowGTK::GetScrollThumb(int orient
) const
4530 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4531 wxCHECK_MSG( sb
, 0, wxT("this window is not scrollable") );
4533 return wxRound(gtk_adjustment_get_page_size(gtk_range_get_adjustment(sb
)));
4536 int wxWindowGTK::GetScrollPos( int orient
) const
4538 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4539 wxCHECK_MSG( sb
, 0, wxT("this window is not scrollable") );
4541 return wxRound(gtk_range_get_value(sb
));
4544 int wxWindowGTK::GetScrollRange( int orient
) const
4546 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4547 wxCHECK_MSG( sb
, 0, wxT("this window is not scrollable") );
4549 return wxRound(gtk_adjustment_get_upper(gtk_range_get_adjustment(sb
)));
4552 // Determine if increment is the same as +/-x, allowing for some small
4553 // difference due to possible inexactness in floating point arithmetic
4554 static inline bool IsScrollIncrement(double increment
, double x
)
4556 wxASSERT(increment
> 0);
4557 const double tolerance
= 1.0 / 1024;
4558 return fabs(increment
- fabs(x
)) < tolerance
;
4561 wxEventType
wxWindowGTK::GTKGetScrollEventType(GtkRange
* range
)
4563 wxASSERT(range
== m_scrollBar
[0] || range
== m_scrollBar
[1]);
4565 const int barIndex
= range
== m_scrollBar
[1];
4567 const double value
= gtk_range_get_value(range
);
4569 // save previous position
4570 const double oldPos
= m_scrollPos
[barIndex
];
4571 // update current position
4572 m_scrollPos
[barIndex
] = value
;
4573 // If event should be ignored, or integral position has not changed
4574 if (!m_hasVMT
|| g_blockEventsOnDrag
|| wxRound(value
) == wxRound(oldPos
))
4579 wxEventType eventType
= wxEVT_SCROLL_THUMBTRACK
;
4582 // Difference from last change event
4583 const double diff
= value
- oldPos
;
4584 const bool isDown
= diff
> 0;
4586 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
4587 if (IsScrollIncrement(gtk_adjustment_get_step_increment(adj
), diff
))
4589 eventType
= isDown
? wxEVT_SCROLL_LINEDOWN
: wxEVT_SCROLL_LINEUP
;
4591 else if (IsScrollIncrement(gtk_adjustment_get_page_increment(adj
), diff
))
4593 eventType
= isDown
? wxEVT_SCROLL_PAGEDOWN
: wxEVT_SCROLL_PAGEUP
;
4595 else if (m_mouseButtonDown
)
4597 // Assume track event
4598 m_isScrolling
= true;
4604 void wxWindowGTK::ScrollWindow( int dx
, int dy
, const wxRect
* WXUNUSED(rect
) )
4606 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4608 wxCHECK_RET( m_wxwindow
!= NULL
, wxT("window needs client area for scrolling") );
4610 // No scrolling requested.
4611 if ((dx
== 0) && (dy
== 0)) return;
4613 m_clipPaintRegion
= true;
4615 WX_PIZZA(m_wxwindow
)->scroll(dx
, dy
);
4617 m_clipPaintRegion
= false;
4620 bool restoreCaret
= (GetCaret() != NULL
&& GetCaret()->IsVisible());
4623 wxRect
caretRect(GetCaret()->GetPosition(), GetCaret()->GetSize());
4625 caretRect
.width
+= dx
;
4628 caretRect
.x
+= dx
; caretRect
.width
-= dx
;
4631 caretRect
.height
+= dy
;
4634 caretRect
.y
+= dy
; caretRect
.height
-= dy
;
4637 RefreshRect(caretRect
);
4639 #endif // wxUSE_CARET
4642 void wxWindowGTK::GTKScrolledWindowSetBorder(GtkWidget
* w
, int wxstyle
)
4644 //RN: Note that static controls usually have no border on gtk, so maybe
4645 //it makes sense to treat that as simply no border at the wx level
4647 if (!(wxstyle
& wxNO_BORDER
) && !(wxstyle
& wxBORDER_STATIC
))
4649 GtkShadowType gtkstyle
;
4651 if(wxstyle
& wxBORDER_RAISED
)
4652 gtkstyle
= GTK_SHADOW_OUT
;
4653 else if ((wxstyle
& wxBORDER_SUNKEN
) || (wxstyle
& wxBORDER_THEME
))
4654 gtkstyle
= GTK_SHADOW_IN
;
4657 else if (wxstyle
& wxBORDER_DOUBLE
)
4658 gtkstyle
= GTK_SHADOW_ETCHED_IN
;
4661 gtkstyle
= GTK_SHADOW_IN
;
4663 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w
),
4668 // Find the wxWindow at the current mouse position, also returning the mouse
4670 wxWindow
* wxFindWindowAtPointer(wxPoint
& pt
)
4672 pt
= wxGetMousePosition();
4673 wxWindow
* found
= wxFindWindowAtPoint(pt
);
4677 // Get the current mouse position.
4678 wxPoint
wxGetMousePosition()
4681 GdkModifierType unused
;
4682 GetMouseState(x
, y
, unused
);
4683 return wxPoint(x
, y
);
4686 GdkWindow
* wxWindowGTK::GTKGetDrawingWindow() const
4688 GdkWindow
* window
= NULL
;
4690 window
= gtk_widget_get_window(m_wxwindow
);
4694 // ----------------------------------------------------------------------------
4696 // ----------------------------------------------------------------------------
4698 void wxWindowGTK::GTKFreezeWidget(GtkWidget
* widget
)
4700 if (widget
&& gtk_widget_get_has_window(widget
))
4702 GdkWindow
* window
= gtk_widget_get_window(widget
);
4704 gdk_window_freeze_updates(window
);
4708 void wxWindowGTK::GTKThawWidget(GtkWidget
* widget
)
4710 if (widget
&& gtk_widget_get_has_window(widget
))
4712 GdkWindow
* window
= gtk_widget_get_window(widget
);
4714 gdk_window_thaw_updates(window
);
4718 void wxWindowGTK::DoFreeze()
4720 GtkWidget
* widget
= m_wxwindow
;
4723 GTKFreezeWidget(widget
);
4726 void wxWindowGTK::DoThaw()
4728 GtkWidget
* widget
= m_wxwindow
;
4731 GTKThawWidget(widget
);