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 //-----------------------------------------------------------------------------
230 // "expose_event"/"draw" from m_wxwindow
231 //-----------------------------------------------------------------------------
235 static gboolean
draw(GtkWidget
*, cairo_t
* cr
, wxWindow
* win
)
237 if (gtk_cairo_should_draw_window(cr
, win
->GTKGetDrawingWindow()))
238 win
->GTKSendPaintEvents(cr
);
243 static gboolean
expose_event(GtkWidget
*, GdkEventExpose
* gdk_event
, wxWindow
* win
)
245 if (gdk_event
->window
== win
->GTKGetDrawingWindow())
246 win
->GTKSendPaintEvents(gdk_event
->region
);
250 #endif // !__WXGTK3__
253 #ifndef __WXUNIVERSAL__
254 //-----------------------------------------------------------------------------
255 // "expose_event"/"draw" from m_wxwindow->parent, for drawing border
256 //-----------------------------------------------------------------------------
261 draw_border(GtkWidget
*, cairo_t
* cr
, wxWindow
* win
)
263 draw_border(GtkWidget
* widget
, GdkEventExpose
* gdk_event
, wxWindow
* win
)
267 if (!gtk_cairo_should_draw_window(cr
, gtk_widget_get_parent_window(win
->m_wxwindow
)))
269 if (gdk_event
->window
!= gtk_widget_get_parent_window(win
->m_wxwindow
))
277 gtk_widget_get_allocation(win
->m_wxwindow
, &alloc
);
278 const int x
= alloc
.x
;
279 const int y
= alloc
.y
;
280 const int w
= alloc
.width
;
281 const int h
= alloc
.height
;
283 if (w
<= 0 || h
<= 0)
286 if (win
->HasFlag(wxBORDER_SIMPLE
))
289 GtkStyleContext
* sc
= gtk_widget_get_style_context(win
->m_wxwindow
);
291 gtk_style_context_get_border_color(sc
, GTK_STATE_FLAG_NORMAL
, &c
);
292 gdk_cairo_set_source_rgba(cr
, &c
);
293 cairo_set_line_width(cr
, 1);
294 cairo_rectangle(cr
, x
+ 0.5, y
+ 0.5, w
- 1, h
- 1);
297 gdk_draw_rectangle(gdk_event
->window
,
298 gtk_widget_get_style(widget
)->black_gc
, false, x
, y
, w
- 1, h
- 1);
301 else if (win
->HasFlag(wxBORDER_RAISED
| wxBORDER_SUNKEN
| wxBORDER_THEME
))
304 //TODO: wxBORDER_RAISED/wxBORDER_SUNKEN
306 if (win
->HasFlag(wxHSCROLL
| wxVSCROLL
))
307 sc
= gtk_widget_get_style_context(wxGTKPrivate::GetTreeWidget());
309 sc
= gtk_widget_get_style_context(wxGTKPrivate::GetEntryWidget());
311 gtk_render_frame(sc
, cr
, x
, y
, w
, h
);
313 GtkShadowType shadow
= GTK_SHADOW_IN
;
314 if (win
->HasFlag(wxBORDER_RAISED
))
315 shadow
= GTK_SHADOW_OUT
;
319 if (win
->HasFlag(wxHSCROLL
| wxVSCROLL
))
321 style
= gtk_widget_get_style(wxGTKPrivate::GetTreeWidget());
326 style
= gtk_widget_get_style(wxGTKPrivate::GetEntryWidget());
330 // clip rect is required to avoid painting background
331 // over upper left (w,h) of parent window
332 GdkRectangle clipRect
= { x
, y
, w
, h
};
334 style
, gdk_event
->window
, GTK_STATE_NORMAL
,
335 shadow
, &clipRect
, widget
, detail
, x
, y
, w
, h
);
336 #endif // !__WXGTK3__
342 //-----------------------------------------------------------------------------
343 // "parent_set" from m_wxwindow
344 //-----------------------------------------------------------------------------
348 parent_set(GtkWidget
* widget
, GtkWidget
* old_parent
, wxWindow
* win
)
352 g_signal_handlers_disconnect_by_func(
353 old_parent
, (void*)draw_border
, win
);
355 GtkWidget
* parent
= gtk_widget_get_parent(widget
);
359 g_signal_connect_after(parent
, "draw", G_CALLBACK(draw_border
), win
);
361 g_signal_connect_after(parent
, "expose_event", G_CALLBACK(draw_border
), win
);
366 #endif // !__WXUNIVERSAL__
368 //-----------------------------------------------------------------------------
369 // "key_press_event" from any window
370 //-----------------------------------------------------------------------------
372 // set WXTRACE to this to see the key event codes on the console
373 #define TRACE_KEYS wxT("keyevent")
375 // translates an X key symbol to WXK_XXX value
377 // if isChar is true it means that the value returned will be used for EVT_CHAR
378 // event and then we choose the logical WXK_XXX, i.e. '/' for GDK_KP_Divide,
379 // for example, while if it is false it means that the value is going to be
380 // used for KEY_DOWN/UP events and then we translate GDK_KP_Divide to
382 static long wxTranslateKeySymToWXKey(KeySym keysym
, bool isChar
)
388 // Shift, Control and Alt don't generate the CHAR events at all
391 key_code
= isChar
? 0 : WXK_SHIFT
;
395 key_code
= isChar
? 0 : WXK_CONTROL
;
403 key_code
= isChar
? 0 : WXK_ALT
;
406 // neither do the toggle modifies
407 case GDK_Scroll_Lock
:
408 key_code
= isChar
? 0 : WXK_SCROLL
;
412 key_code
= isChar
? 0 : WXK_CAPITAL
;
416 key_code
= isChar
? 0 : WXK_NUMLOCK
;
420 // various other special keys
433 case GDK_ISO_Left_Tab
:
440 key_code
= WXK_RETURN
;
444 key_code
= WXK_CLEAR
;
448 key_code
= WXK_PAUSE
;
452 key_code
= WXK_SELECT
;
456 key_code
= WXK_PRINT
;
460 key_code
= WXK_EXECUTE
;
464 key_code
= WXK_ESCAPE
;
467 // cursor and other extended keyboard keys
469 key_code
= WXK_DELETE
;
485 key_code
= WXK_RIGHT
;
492 case GDK_Prior
: // == GDK_Page_Up
493 key_code
= WXK_PAGEUP
;
496 case GDK_Next
: // == GDK_Page_Down
497 key_code
= WXK_PAGEDOWN
;
509 key_code
= WXK_INSERT
;
524 key_code
= (isChar
? '0' : int(WXK_NUMPAD0
)) + keysym
- GDK_KP_0
;
528 key_code
= isChar
? ' ' : int(WXK_NUMPAD_SPACE
);
532 key_code
= isChar
? WXK_TAB
: WXK_NUMPAD_TAB
;
536 key_code
= isChar
? WXK_RETURN
: WXK_NUMPAD_ENTER
;
540 key_code
= isChar
? WXK_F1
: WXK_NUMPAD_F1
;
544 key_code
= isChar
? WXK_F2
: WXK_NUMPAD_F2
;
548 key_code
= isChar
? WXK_F3
: WXK_NUMPAD_F3
;
552 key_code
= isChar
? WXK_F4
: WXK_NUMPAD_F4
;
556 key_code
= isChar
? WXK_HOME
: WXK_NUMPAD_HOME
;
560 key_code
= isChar
? WXK_LEFT
: WXK_NUMPAD_LEFT
;
564 key_code
= isChar
? WXK_UP
: WXK_NUMPAD_UP
;
568 key_code
= isChar
? WXK_RIGHT
: WXK_NUMPAD_RIGHT
;
572 key_code
= isChar
? WXK_DOWN
: WXK_NUMPAD_DOWN
;
575 case GDK_KP_Prior
: // == GDK_KP_Page_Up
576 key_code
= isChar
? WXK_PAGEUP
: WXK_NUMPAD_PAGEUP
;
579 case GDK_KP_Next
: // == GDK_KP_Page_Down
580 key_code
= isChar
? WXK_PAGEDOWN
: WXK_NUMPAD_PAGEDOWN
;
584 key_code
= isChar
? WXK_END
: WXK_NUMPAD_END
;
588 key_code
= isChar
? WXK_HOME
: WXK_NUMPAD_BEGIN
;
592 key_code
= isChar
? WXK_INSERT
: WXK_NUMPAD_INSERT
;
596 key_code
= isChar
? WXK_DELETE
: WXK_NUMPAD_DELETE
;
600 key_code
= isChar
? '=' : int(WXK_NUMPAD_EQUAL
);
603 case GDK_KP_Multiply
:
604 key_code
= isChar
? '*' : int(WXK_NUMPAD_MULTIPLY
);
608 key_code
= isChar
? '+' : int(WXK_NUMPAD_ADD
);
611 case GDK_KP_Separator
:
612 // FIXME: what is this?
613 key_code
= isChar
? '.' : int(WXK_NUMPAD_SEPARATOR
);
616 case GDK_KP_Subtract
:
617 key_code
= isChar
? '-' : int(WXK_NUMPAD_SUBTRACT
);
621 key_code
= isChar
? '.' : int(WXK_NUMPAD_DECIMAL
);
625 key_code
= isChar
? '/' : int(WXK_NUMPAD_DIVIDE
);
642 key_code
= WXK_F1
+ keysym
- GDK_F1
;
652 static inline bool wxIsAsciiKeysym(KeySym ks
)
657 static void wxFillOtherKeyEventFields(wxKeyEvent
& event
,
659 GdkEventKey
*gdk_event
)
661 event
.SetTimestamp( gdk_event
->time
);
662 event
.SetId(win
->GetId());
664 event
.m_shiftDown
= (gdk_event
->state
& GDK_SHIFT_MASK
) != 0;
665 event
.m_controlDown
= (gdk_event
->state
& GDK_CONTROL_MASK
) != 0;
666 event
.m_altDown
= (gdk_event
->state
& GDK_MOD1_MASK
) != 0;
667 event
.m_metaDown
= (gdk_event
->state
& GDK_META_MASK
) != 0;
669 // Normally we take the state of modifiers directly from the low level GDK
670 // event but unfortunately GDK uses a different convention from MSW for the
671 // key events corresponding to the modifier keys themselves: in it, when
672 // e.g. Shift key is pressed, GDK_SHIFT_MASK is not set while it is set
673 // when Shift is released. Under MSW the situation is exactly reversed and
674 // the modifier corresponding to the key is set when it is pressed and
675 // unset when it is released. To ensure consistent behaviour between
676 // platforms (and because it seems to make slightly more sense, although
677 // arguably both behaviours are reasonable) we follow MSW here.
679 // Final notice: we set the flags to the desired value instead of just
680 // inverting them because they are not set correctly (i.e. in the same way
681 // as for the real events generated by the user) for wxUIActionSimulator-
682 // produced events and it seems better to keep that class code the same
683 // among all platforms and fix the discrepancy here instead of adding
684 // wxGTK-specific code to wxUIActionSimulator.
685 const bool isPress
= gdk_event
->type
== GDK_KEY_PRESS
;
686 switch ( gdk_event
->keyval
)
690 event
.m_shiftDown
= isPress
;
695 event
.m_controlDown
= isPress
;
700 event
.m_altDown
= isPress
;
707 event
.m_metaDown
= isPress
;
711 event
.m_rawCode
= (wxUint32
) gdk_event
->keyval
;
712 event
.m_rawFlags
= gdk_event
->hardware_keycode
;
714 event
.SetEventObject( win
);
719 wxTranslateGTKKeyEventToWx(wxKeyEvent
& event
,
721 GdkEventKey
*gdk_event
)
723 // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string
724 // but only event->keyval which is quite useless to us, so remember
725 // the last character from GDK_KEY_PRESS and reuse it as last resort
727 // NB: should be MT-safe as we're always called from the main thread only
732 } s_lastKeyPress
= { 0, 0 };
734 KeySym keysym
= gdk_event
->keyval
;
736 wxLogTrace(TRACE_KEYS
, wxT("Key %s event: keysym = %ld"),
737 event
.GetEventType() == wxEVT_KEY_UP
? wxT("release")
741 long key_code
= wxTranslateKeySymToWXKey(keysym
, false /* !isChar */);
745 // do we have the translation or is it a plain ASCII character?
746 if ( (gdk_event
->length
== 1) || wxIsAsciiKeysym(keysym
) )
748 // we should use keysym if it is ASCII as X does some translations
749 // like "I pressed while Control is down" => "Ctrl-I" == "TAB"
750 // which we don't want here (but which we do use for OnChar())
751 if ( !wxIsAsciiKeysym(keysym
) )
753 keysym
= (KeySym
)gdk_event
->string
[0];
756 #ifdef GDK_WINDOWING_X11
757 // we want to always get the same key code when the same key is
758 // pressed regardless of the state of the modifiers, i.e. on a
759 // standard US keyboard pressing '5' or '%' ('5' key with
760 // Shift) should result in the same key code in OnKeyDown():
761 // '5' (although OnChar() will get either '5' or '%').
763 // to do it we first translate keysym to keycode (== scan code)
764 // and then back but always using the lower register
765 Display
*dpy
= (Display
*)wxGetDisplay();
766 KeyCode keycode
= XKeysymToKeycode(dpy
, keysym
);
768 wxLogTrace(TRACE_KEYS
, wxT("\t-> keycode %d"), keycode
);
770 #ifdef HAVE_X11_XKBLIB_H
771 KeySym keysymNormalized
= XkbKeycodeToKeysym(dpy
, keycode
, 0, 0);
773 KeySym keysymNormalized
= XKeycodeToKeysym(dpy
, keycode
, 0);
776 // use the normalized, i.e. lower register, keysym if we've
778 key_code
= keysymNormalized
? keysymNormalized
: keysym
;
783 // as explained above, we want to have lower register key codes
784 // normally but for the letter keys we want to have the upper ones
786 // NB: don't use XConvertCase() here, we want to do it for letters
788 key_code
= toupper(key_code
);
790 else // non ASCII key, what to do?
792 // by default, ignore it
795 // but if we have cached information from the last KEY_PRESS
796 if ( gdk_event
->type
== GDK_KEY_RELEASE
)
799 if ( keysym
== s_lastKeyPress
.keysym
)
801 key_code
= s_lastKeyPress
.keycode
;
806 if ( gdk_event
->type
== GDK_KEY_PRESS
)
808 // remember it to be reused for KEY_UP event later
809 s_lastKeyPress
.keysym
= keysym
;
810 s_lastKeyPress
.keycode
= key_code
;
814 wxLogTrace(TRACE_KEYS
, wxT("\t-> wxKeyCode %ld"), key_code
);
816 // sending unknown key events doesn't really make sense
820 event
.m_keyCode
= key_code
;
823 event
.m_uniChar
= gdk_keyval_to_unicode(key_code
? key_code
: keysym
);
824 if ( !event
.m_uniChar
&& event
.m_keyCode
<= WXK_DELETE
)
826 // Set Unicode key code to the ASCII equivalent for compatibility. E.g.
827 // let RETURN generate the key event with both key and Unicode key
829 event
.m_uniChar
= event
.m_keyCode
;
831 #endif // wxUSE_UNICODE
833 // now fill all the other fields
834 wxFillOtherKeyEventFields(event
, win
, gdk_event
);
842 GtkIMContext
*context
;
843 GdkEventKey
*lastKeyEvent
;
847 context
= gtk_im_multicontext_new();
852 g_object_unref (context
);
859 // Send wxEVT_CHAR_HOOK event to the parent of the window and return true only
860 // if it was processed (and not skipped).
861 bool SendCharHookEvent(const wxKeyEvent
& event
, wxWindow
*win
)
863 // wxEVT_CHAR_HOOK must be sent to allow the parent windows (e.g. a dialog
864 // which typically closes when Esc key is pressed in any of its controls)
865 // to handle key events in all of its children unless the mouse is captured
866 // in which case we consider that the keyboard should be "captured" too.
867 if ( !g_captureWindow
)
869 wxKeyEvent
eventCharHook(wxEVT_CHAR_HOOK
, event
);
870 if ( win
->HandleWindowEvent(eventCharHook
)
871 && !event
.IsNextEventAllowed() )
878 // Adjust wxEVT_CHAR event key code fields. This function takes care of two
880 // (a) Ctrl-letter key presses generate key codes in range 1..26
881 // (b) Unicode key codes are same as key codes for the codes in 1..255 range
882 void AdjustCharEventKeyCodes(wxKeyEvent
& event
)
884 const int code
= event
.m_keyCode
;
886 // Check for (a) above.
887 if ( event
.ControlDown() )
889 // We intentionally don't use isupper/lower() here, we really need
890 // ASCII letters only as it doesn't make sense to translate any other
891 // ones into this range which has only 26 slots.
892 if ( code
>= 'a' && code
<= 'z' )
893 event
.m_keyCode
= code
- 'a' + 1;
894 else if ( code
>= 'A' && code
<= 'Z' )
895 event
.m_keyCode
= code
- 'A' + 1;
898 // Adjust the Unicode equivalent in the same way too.
899 if ( event
.m_keyCode
!= code
)
900 event
.m_uniChar
= event
.m_keyCode
;
901 #endif // wxUSE_UNICODE
905 // Check for (b) from above.
907 // FIXME: Should we do it for key codes up to 255?
908 if ( !event
.m_uniChar
&& code
< WXK_DELETE
)
909 event
.m_uniChar
= code
;
910 #endif // wxUSE_UNICODE
913 } // anonymous namespace
915 // If a widget does not handle a key or mouse event, GTK+ sends it up the
916 // parent chain until it is handled. These events are not supposed to propagate
917 // in wxWidgets, so this code avoids handling them in any parent wxWindow,
918 // while still allowing the event to propagate so things like native keyboard
919 // navigation will work.
920 #define wxPROCESS_EVENT_ONCE(EventType, event) \
921 static EventType eventPrev; \
922 if (memcmp(&eventPrev, event, sizeof(EventType)) == 0) \
928 gtk_window_key_press_callback( GtkWidget
*WXUNUSED(widget
),
929 GdkEventKey
*gdk_event
,
934 if (g_blockEventsOnDrag
)
937 wxPROCESS_EVENT_ONCE(GdkEventKey
, gdk_event
);
939 wxKeyEvent
event( wxEVT_KEY_DOWN
);
941 bool return_after_IM
= false;
943 if( wxTranslateGTKKeyEventToWx(event
, win
, gdk_event
) )
945 // Send the CHAR_HOOK event first
946 if ( SendCharHookEvent(event
, win
) )
948 // Don't do anything at all with this event any more.
952 // Next check for accelerators.
954 wxWindowGTK
*ancestor
= win
;
957 int command
= ancestor
->GetAcceleratorTable()->GetCommand( event
);
960 wxCommandEvent
menu_event( wxEVT_COMMAND_MENU_SELECTED
, command
);
961 ret
= ancestor
->HandleWindowEvent( menu_event
);
965 // if the accelerator wasn't handled as menu event, try
966 // it as button click (for compatibility with other
968 wxCommandEvent
button_event( wxEVT_COMMAND_BUTTON_CLICKED
, command
);
969 ret
= ancestor
->HandleWindowEvent( button_event
);
974 if (ancestor
->IsTopLevel())
976 ancestor
= ancestor
->GetParent();
978 #endif // wxUSE_ACCEL
980 // If not an accelerator, then emit KEY_DOWN event
982 ret
= win
->HandleWindowEvent( event
);
986 // Return after IM processing as we cannot do
987 // anything with it anyhow.
988 return_after_IM
= true;
991 if (!ret
&& win
->m_imData
)
993 win
->m_imData
->lastKeyEvent
= gdk_event
;
995 // We should let GTK+ IM filter key event first. According to GTK+ 2.0 API
996 // docs, if IM filter returns true, no further processing should be done.
997 // we should send the key_down event anyway.
998 bool intercepted_by_IM
=
999 gtk_im_context_filter_keypress(win
->m_imData
->context
, gdk_event
) != 0;
1000 win
->m_imData
->lastKeyEvent
= NULL
;
1001 if (intercepted_by_IM
)
1003 wxLogTrace(TRACE_KEYS
, wxT("Key event intercepted by IM"));
1008 if (return_after_IM
)
1011 // Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
1012 // will only be sent if it is not in an accelerator table.
1016 KeySym keysym
= gdk_event
->keyval
;
1017 // Find key code for EVT_CHAR and EVT_CHAR_HOOK events
1018 key_code
= wxTranslateKeySymToWXKey(keysym
, true /* isChar */);
1021 if ( wxIsAsciiKeysym(keysym
) )
1024 key_code
= (unsigned char)keysym
;
1026 // gdk_event->string is actually deprecated
1027 else if ( gdk_event
->length
== 1 )
1029 key_code
= (unsigned char)gdk_event
->string
[0];
1035 wxKeyEvent
eventChar(wxEVT_CHAR
, event
);
1037 wxLogTrace(TRACE_KEYS
, wxT("Char event: %ld"), key_code
);
1039 eventChar
.m_keyCode
= key_code
;
1041 AdjustCharEventKeyCodes(eventChar
);
1043 ret
= win
->HandleWindowEvent(eventChar
);
1053 gtk_wxwindow_commit_cb (GtkIMContext
* WXUNUSED(context
),
1057 wxKeyEvent
event( wxEVT_CHAR
);
1059 // take modifiers, cursor position, timestamp etc. from the last
1060 // key_press_event that was fed into Input Method:
1061 if (window
->m_imData
->lastKeyEvent
)
1063 wxFillOtherKeyEventFields(event
,
1064 window
, window
->m_imData
->lastKeyEvent
);
1068 event
.SetEventObject( window
);
1071 const wxString
data(wxGTK_CONV_BACK_SYS(str
));
1075 for( wxString::const_iterator pstr
= data
.begin(); pstr
!= data
.end(); ++pstr
)
1078 event
.m_uniChar
= *pstr
;
1079 // Backward compatible for ISO-8859-1
1080 event
.m_keyCode
= *pstr
< 256 ? event
.m_uniChar
: 0;
1081 wxLogTrace(TRACE_KEYS
, wxT("IM sent character '%c'"), event
.m_uniChar
);
1083 event
.m_keyCode
= (char)*pstr
;
1084 #endif // wxUSE_UNICODE
1086 AdjustCharEventKeyCodes(event
);
1088 window
->HandleWindowEvent(event
);
1094 //-----------------------------------------------------------------------------
1095 // "key_release_event" from any window
1096 //-----------------------------------------------------------------------------
1100 gtk_window_key_release_callback( GtkWidget
* WXUNUSED(widget
),
1101 GdkEventKey
*gdk_event
,
1107 if (g_blockEventsOnDrag
)
1110 wxPROCESS_EVENT_ONCE(GdkEventKey
, gdk_event
);
1112 wxKeyEvent
event( wxEVT_KEY_UP
);
1113 if ( !wxTranslateGTKKeyEventToWx(event
, win
, gdk_event
) )
1115 // unknown key pressed, ignore (the event would be useless anyhow)
1119 return win
->GTKProcessEvent(event
);
1123 // ============================================================================
1125 // ============================================================================
1127 // ----------------------------------------------------------------------------
1128 // mouse event processing helpers
1129 // ----------------------------------------------------------------------------
1131 static void AdjustEventButtonState(wxMouseEvent
& event
)
1133 // GDK reports the old state of the button for a button press event, but
1134 // for compatibility with MSW and common sense we want m_leftDown be TRUE
1135 // for a LEFT_DOWN event, not FALSE, so we will invert
1136 // left/right/middleDown for the corresponding click events
1138 if ((event
.GetEventType() == wxEVT_LEFT_DOWN
) ||
1139 (event
.GetEventType() == wxEVT_LEFT_DCLICK
) ||
1140 (event
.GetEventType() == wxEVT_LEFT_UP
))
1142 event
.m_leftDown
= !event
.m_leftDown
;
1146 if ((event
.GetEventType() == wxEVT_MIDDLE_DOWN
) ||
1147 (event
.GetEventType() == wxEVT_MIDDLE_DCLICK
) ||
1148 (event
.GetEventType() == wxEVT_MIDDLE_UP
))
1150 event
.m_middleDown
= !event
.m_middleDown
;
1154 if ((event
.GetEventType() == wxEVT_RIGHT_DOWN
) ||
1155 (event
.GetEventType() == wxEVT_RIGHT_DCLICK
) ||
1156 (event
.GetEventType() == wxEVT_RIGHT_UP
))
1158 event
.m_rightDown
= !event
.m_rightDown
;
1162 if ((event
.GetEventType() == wxEVT_AUX1_DOWN
) ||
1163 (event
.GetEventType() == wxEVT_AUX1_DCLICK
))
1165 event
.m_aux1Down
= true;
1169 if ((event
.GetEventType() == wxEVT_AUX2_DOWN
) ||
1170 (event
.GetEventType() == wxEVT_AUX2_DCLICK
))
1172 event
.m_aux2Down
= true;
1177 // find the window to send the mouse event to
1179 wxWindowGTK
*FindWindowForMouseEvent(wxWindowGTK
*win
, wxCoord
& x
, wxCoord
& y
)
1184 if (win
->m_wxwindow
)
1186 wxPizza
* pizza
= WX_PIZZA(win
->m_wxwindow
);
1187 xx
+= pizza
->m_scroll_x
;
1188 yy
+= pizza
->m_scroll_y
;
1191 wxWindowList::compatibility_iterator node
= win
->GetChildren().GetFirst();
1194 wxWindow
* child
= static_cast<wxWindow
*>(node
->GetData());
1196 node
= node
->GetNext();
1197 if (!child
->IsShown())
1200 if (child
->GTKIsTransparentForMouse())
1202 // wxStaticBox is transparent in the box itself
1203 int xx1
= child
->m_x
;
1204 int yy1
= child
->m_y
;
1205 int xx2
= child
->m_x
+ child
->m_width
;
1206 int yy2
= child
->m_y
+ child
->m_height
;
1209 if (((xx
>= xx1
) && (xx
<= xx1
+10) && (yy
>= yy1
) && (yy
<= yy2
)) ||
1211 ((xx
>= xx2
-10) && (xx
<= xx2
) && (yy
>= yy1
) && (yy
<= yy2
)) ||
1213 ((xx
>= xx1
) && (xx
<= xx2
) && (yy
>= yy1
) && (yy
<= yy1
+10)) ||
1215 ((xx
>= xx1
) && (xx
<= xx2
) && (yy
>= yy2
-1) && (yy
<= yy2
)))
1226 if ((child
->m_wxwindow
== NULL
) &&
1227 win
->IsClientAreaChild(child
) &&
1228 (child
->m_x
<= xx
) &&
1229 (child
->m_y
<= yy
) &&
1230 (child
->m_x
+child
->m_width
>= xx
) &&
1231 (child
->m_y
+child
->m_height
>= yy
))
1244 // ----------------------------------------------------------------------------
1245 // common event handlers helpers
1246 // ----------------------------------------------------------------------------
1248 bool wxWindowGTK::GTKProcessEvent(wxEvent
& event
) const
1250 // nothing special at this level
1251 return HandleWindowEvent(event
);
1254 bool wxWindowGTK::GTKShouldIgnoreEvent() const
1256 return !m_hasVMT
|| g_blockEventsOnDrag
;
1259 int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny
*event
) const
1263 if (g_blockEventsOnDrag
)
1265 if (g_blockEventsOnScroll
)
1268 if (!GTKIsOwnWindow(event
->window
))
1274 // overloads for all GDK event types we use here: we need to have this as
1275 // GdkEventXXX can't be implicitly cast to GdkEventAny even if it, in fact,
1276 // derives from it in the sense that the structs have the same layout
1277 #define wxDEFINE_COMMON_PROLOGUE_OVERLOAD(T) \
1278 static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win) \
1280 return win->GTKCallbackCommonPrologue((GdkEventAny *)event); \
1283 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventButton
)
1284 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion
)
1285 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing
)
1287 #undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD
1289 #define wxCOMMON_CALLBACK_PROLOGUE(event, win) \
1290 const int rc = wxGtkCallbackCommonPrologue(event, win); \
1294 // all event handlers must have C linkage as they're called from GTK+ C code
1298 //-----------------------------------------------------------------------------
1299 // "button_press_event"
1300 //-----------------------------------------------------------------------------
1303 gtk_window_button_press_callback( GtkWidget
* WXUNUSED_IN_GTK3(widget
),
1304 GdkEventButton
*gdk_event
,
1307 wxPROCESS_EVENT_ONCE(GdkEventButton
, gdk_event
);
1309 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1311 g_lastButtonNumber
= gdk_event
->button
;
1313 wxEventType event_type
;
1316 switch (gdk_event
->button
)
1319 down
= wxEVT_LEFT_DOWN
;
1320 dclick
= wxEVT_LEFT_DCLICK
;
1323 down
= wxEVT_MIDDLE_DOWN
;
1324 dclick
= wxEVT_MIDDLE_DCLICK
;
1327 down
= wxEVT_RIGHT_DOWN
;
1328 dclick
= wxEVT_RIGHT_DCLICK
;
1331 down
= wxEVT_AUX1_DOWN
;
1332 dclick
= wxEVT_AUX1_DCLICK
;
1335 down
= wxEVT_AUX2_DOWN
;
1336 dclick
= wxEVT_AUX2_DCLICK
;
1341 switch (gdk_event
->type
)
1343 case GDK_BUTTON_PRESS
:
1345 // GDK sends surplus button down events
1346 // before a double click event. We
1347 // need to filter these out.
1348 if (win
->m_wxwindow
)
1350 GdkEvent
* peek_event
= gdk_event_peek();
1353 const GdkEventType peek_event_type
= peek_event
->type
;
1354 gdk_event_free(peek_event
);
1355 if (peek_event_type
== GDK_2BUTTON_PRESS
||
1356 peek_event_type
== GDK_3BUTTON_PRESS
)
1363 case GDK_2BUTTON_PRESS
:
1364 event_type
= dclick
;
1366 if (gdk_event
->button
>= 1 && gdk_event
->button
<= 3)
1368 // Reset GDK internal timestamp variables in order to disable GDK
1369 // triple click events. GDK will then next time believe no button has
1370 // been clicked just before, and send a normal button click event.
1371 GdkDisplay
* display
= gtk_widget_get_display(widget
);
1372 display
->button_click_time
[1] = 0;
1373 display
->button_click_time
[0] = 0;
1375 #endif // !__WXGTK3__
1377 // we shouldn't get triple clicks at all for GTK2 because we
1378 // suppress them artificially using the code above but we still
1379 // should map them to something for GTK3 and not just ignore them
1380 // as this would lose clicks
1381 case GDK_3BUTTON_PRESS
:
1388 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1390 wxMouseEvent
event( event_type
);
1391 InitMouseEvent( win
, event
, gdk_event
);
1393 AdjustEventButtonState(event
);
1395 // find the correct window to send the event to: it may be a different one
1396 // from the one which got it at GTK+ level because some controls don't have
1397 // their own X window and thus cannot get any events.
1398 if ( !g_captureWindow
)
1399 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1401 // reset the event object and id in case win changed.
1402 event
.SetEventObject( win
);
1403 event
.SetId( win
->GetId() );
1405 bool ret
= win
->GTKProcessEvent( event
);
1406 g_lastMouseEvent
= NULL
;
1410 if ((event_type
== wxEVT_LEFT_DOWN
) && !win
->IsOfStandardClass() &&
1411 (gs_currentFocus
!= win
) /* && win->IsFocusable() */)
1416 if (event_type
== wxEVT_RIGHT_DOWN
)
1418 // generate a "context menu" event: this is similar to right mouse
1419 // click under many GUIs except that it is generated differently
1420 // (right up under MSW, ctrl-click under Mac, right down here) and
1422 // (a) it's a command event and so is propagated to the parent
1423 // (b) under some ports it can be generated from kbd too
1424 // (c) it uses screen coords (because of (a))
1425 wxContextMenuEvent
evtCtx(
1428 win
->ClientToScreen(event
.GetPosition()));
1429 evtCtx
.SetEventObject(win
);
1430 return win
->GTKProcessEvent(evtCtx
);
1436 //-----------------------------------------------------------------------------
1437 // "button_release_event"
1438 //-----------------------------------------------------------------------------
1441 gtk_window_button_release_callback( GtkWidget
*WXUNUSED(widget
),
1442 GdkEventButton
*gdk_event
,
1445 wxPROCESS_EVENT_ONCE(GdkEventButton
, gdk_event
);
1447 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1449 g_lastButtonNumber
= 0;
1451 wxEventType event_type
= wxEVT_NULL
;
1453 switch (gdk_event
->button
)
1456 event_type
= wxEVT_LEFT_UP
;
1460 event_type
= wxEVT_MIDDLE_UP
;
1464 event_type
= wxEVT_RIGHT_UP
;
1468 event_type
= wxEVT_AUX1_UP
;
1472 event_type
= wxEVT_AUX2_UP
;
1476 // unknown button, don't process
1480 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1482 wxMouseEvent
event( event_type
);
1483 InitMouseEvent( win
, event
, gdk_event
);
1485 AdjustEventButtonState(event
);
1487 if ( !g_captureWindow
)
1488 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1490 // reset the event object and id in case win changed.
1491 event
.SetEventObject( win
);
1492 event
.SetId( win
->GetId() );
1494 bool ret
= win
->GTKProcessEvent(event
);
1496 g_lastMouseEvent
= NULL
;
1501 //-----------------------------------------------------------------------------
1502 // "motion_notify_event"
1503 //-----------------------------------------------------------------------------
1506 gtk_window_motion_notify_callback( GtkWidget
* WXUNUSED(widget
),
1507 GdkEventMotion
*gdk_event
,
1510 wxPROCESS_EVENT_ONCE(GdkEventMotion
, gdk_event
);
1512 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1514 if (gdk_event
->is_hint
)
1518 GdkModifierType state
;
1519 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, &state
);
1524 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1526 wxMouseEvent
event( wxEVT_MOTION
);
1527 InitMouseEvent(win
, event
, gdk_event
);
1529 if ( g_captureWindow
)
1531 // synthesise a mouse enter or leave event if needed
1532 GdkWindow
*winUnderMouse
= gdk_window_at_pointer(NULL
, NULL
);
1533 // This seems to be necessary and actually been added to
1534 // GDK itself in version 2.0.X
1537 bool hasMouse
= winUnderMouse
== gdk_event
->window
;
1538 if ( hasMouse
!= g_captureWindowHasMouse
)
1540 // the mouse changed window
1541 g_captureWindowHasMouse
= hasMouse
;
1543 wxMouseEvent
eventM(g_captureWindowHasMouse
? wxEVT_ENTER_WINDOW
1544 : wxEVT_LEAVE_WINDOW
);
1545 InitMouseEvent(win
, eventM
, gdk_event
);
1546 eventM
.SetEventObject(win
);
1547 win
->GTKProcessEvent(eventM
);
1552 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1554 // reset the event object and id in case win changed.
1555 event
.SetEventObject( win
);
1556 event
.SetId( win
->GetId() );
1559 if ( !g_captureWindow
)
1561 wxSetCursorEvent
cevent( event
.m_x
, event
.m_y
);
1562 if (win
->GTKProcessEvent( cevent
))
1564 win
->SetCursor( cevent
.GetCursor() );
1568 bool ret
= win
->GTKProcessEvent(event
);
1570 g_lastMouseEvent
= NULL
;
1575 //-----------------------------------------------------------------------------
1576 // "scroll_event" (mouse wheel event)
1577 //-----------------------------------------------------------------------------
1580 window_scroll_event_hscrollbar(GtkWidget
*, GdkEventScroll
* gdk_event
, wxWindow
* win
)
1582 if (gdk_event
->direction
!= GDK_SCROLL_LEFT
&&
1583 gdk_event
->direction
!= GDK_SCROLL_RIGHT
)
1588 GtkRange
*range
= win
->m_scrollBar
[wxWindow::ScrollDir_Horz
];
1590 if (range
&& gtk_widget_get_visible(GTK_WIDGET(range
)))
1592 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
1593 double delta
= gtk_adjustment_get_step_increment(adj
) * 3;
1594 if (gdk_event
->direction
== GDK_SCROLL_LEFT
)
1597 gtk_range_set_value(range
, gtk_adjustment_get_value(adj
) + delta
);
1606 window_scroll_event(GtkWidget
*, GdkEventScroll
* gdk_event
, wxWindow
* win
)
1608 wxMouseEvent
event(wxEVT_MOUSEWHEEL
);
1609 InitMouseEvent(win
, event
, gdk_event
);
1611 // FIXME: Get these values from GTK or GDK
1612 event
.m_linesPerAction
= 3;
1613 event
.m_wheelDelta
= 120;
1615 // Determine the scroll direction.
1616 switch (gdk_event
->direction
)
1619 case GDK_SCROLL_RIGHT
:
1620 event
.m_wheelRotation
= 120;
1623 case GDK_SCROLL_DOWN
:
1624 case GDK_SCROLL_LEFT
:
1625 event
.m_wheelRotation
= -120;
1629 return false; // Unknown/unhandled direction
1632 // And the scroll axis.
1633 switch (gdk_event
->direction
)
1636 case GDK_SCROLL_DOWN
:
1637 event
.m_wheelAxis
= wxMOUSE_WHEEL_VERTICAL
;
1640 case GDK_SCROLL_LEFT
:
1641 case GDK_SCROLL_RIGHT
:
1642 event
.m_wheelAxis
= wxMOUSE_WHEEL_HORIZONTAL
;
1646 if (win
->GTKProcessEvent(event
))
1649 GtkRange
*range
= win
->m_scrollBar
[wxWindow::ScrollDir_Vert
];
1651 if (range
&& gtk_widget_get_visible(GTK_WIDGET(range
)))
1653 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
1654 double delta
= gtk_adjustment_get_step_increment(adj
) * 3;
1655 if (gdk_event
->direction
== GDK_SCROLL_UP
)
1658 gtk_range_set_value(range
, gtk_adjustment_get_value(adj
) + delta
);
1666 //-----------------------------------------------------------------------------
1668 //-----------------------------------------------------------------------------
1670 static gboolean
wxgtk_window_popup_menu_callback(GtkWidget
*, wxWindowGTK
* win
)
1672 wxContextMenuEvent
event(wxEVT_CONTEXT_MENU
, win
->GetId(), wxPoint(-1, -1));
1673 event
.SetEventObject(win
);
1674 return win
->GTKProcessEvent(event
);
1677 //-----------------------------------------------------------------------------
1679 //-----------------------------------------------------------------------------
1682 gtk_window_focus_in_callback( GtkWidget
* WXUNUSED(widget
),
1683 GdkEventFocus
*WXUNUSED(event
),
1686 return win
->GTKHandleFocusIn();
1689 //-----------------------------------------------------------------------------
1690 // "focus_out_event"
1691 //-----------------------------------------------------------------------------
1694 gtk_window_focus_out_callback( GtkWidget
* WXUNUSED(widget
),
1695 GdkEventFocus
* WXUNUSED(gdk_event
),
1698 return win
->GTKHandleFocusOut();
1701 //-----------------------------------------------------------------------------
1703 //-----------------------------------------------------------------------------
1706 wx_window_focus_callback(GtkWidget
*widget
,
1707 GtkDirectionType
WXUNUSED(direction
),
1710 // the default handler for focus signal in GtkScrolledWindow sets
1711 // focus to the window itself even if it doesn't accept focus, i.e. has no
1712 // GTK_CAN_FOCUS in its style -- work around this by forcibly preventing
1713 // the signal from reaching gtk_scrolled_window_focus() if we don't have
1714 // any children which might accept focus (we know we don't accept the focus
1715 // ourselves as this signal is only connected in this case)
1716 if ( win
->GetChildren().empty() )
1717 g_signal_stop_emission_by_name(widget
, "focus");
1719 // we didn't change the focus
1723 //-----------------------------------------------------------------------------
1724 // "enter_notify_event"
1725 //-----------------------------------------------------------------------------
1728 gtk_window_enter_callback( GtkWidget
*widget
,
1729 GdkEventCrossing
*gdk_event
,
1732 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1734 // Event was emitted after a grab
1735 if (gdk_event
->mode
!= GDK_CROSSING_NORMAL
) return FALSE
;
1739 GdkModifierType state
= (GdkModifierType
)0;
1741 gdk_window_get_pointer(gtk_widget_get_window(widget
), &x
, &y
, &state
);
1743 wxMouseEvent
event( wxEVT_ENTER_WINDOW
);
1744 InitMouseEvent(win
, event
, gdk_event
);
1745 wxPoint pt
= win
->GetClientAreaOrigin();
1746 event
.m_x
= x
+ pt
.x
;
1747 event
.m_y
= y
+ pt
.y
;
1749 if ( !g_captureWindow
)
1751 wxSetCursorEvent
cevent( event
.m_x
, event
.m_y
);
1752 if (win
->GTKProcessEvent( cevent
))
1754 win
->SetCursor( cevent
.GetCursor() );
1758 return win
->GTKProcessEvent(event
);
1761 //-----------------------------------------------------------------------------
1762 // "leave_notify_event"
1763 //-----------------------------------------------------------------------------
1766 gtk_window_leave_callback( GtkWidget
*widget
,
1767 GdkEventCrossing
*gdk_event
,
1770 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1772 // Event was emitted after an ungrab
1773 if (gdk_event
->mode
!= GDK_CROSSING_NORMAL
) return FALSE
;
1775 wxMouseEvent
event( wxEVT_LEAVE_WINDOW
);
1779 GdkModifierType state
= (GdkModifierType
)0;
1781 gdk_window_get_pointer(gtk_widget_get_window(widget
), &x
, &y
, &state
);
1783 InitMouseEvent(win
, event
, gdk_event
);
1785 return win
->GTKProcessEvent(event
);
1788 //-----------------------------------------------------------------------------
1789 // "value_changed" from scrollbar
1790 //-----------------------------------------------------------------------------
1793 gtk_scrollbar_value_changed(GtkRange
* range
, wxWindow
* win
)
1795 wxEventType eventType
= win
->GTKGetScrollEventType(range
);
1796 if (eventType
!= wxEVT_NULL
)
1798 // Convert scroll event type to scrollwin event type
1799 eventType
+= wxEVT_SCROLLWIN_TOP
- wxEVT_SCROLL_TOP
;
1801 // find the scrollbar which generated the event
1802 wxWindowGTK::ScrollDir dir
= win
->ScrollDirFromRange(range
);
1804 // generate the corresponding wx event
1805 const int orient
= wxWindow::OrientFromScrollDir(dir
);
1806 wxScrollWinEvent
event(eventType
, win
->GetScrollPos(orient
), orient
);
1807 event
.SetEventObject(win
);
1809 win
->GTKProcessEvent(event
);
1813 //-----------------------------------------------------------------------------
1814 // "button_press_event" from scrollbar
1815 //-----------------------------------------------------------------------------
1818 gtk_scrollbar_button_press_event(GtkRange
*, GdkEventButton
*, wxWindow
* win
)
1820 g_blockEventsOnScroll
= true;
1821 win
->m_mouseButtonDown
= true;
1826 //-----------------------------------------------------------------------------
1827 // "event_after" from scrollbar
1828 //-----------------------------------------------------------------------------
1831 gtk_scrollbar_event_after(GtkRange
* range
, GdkEvent
* event
, wxWindow
* win
)
1833 if (event
->type
== GDK_BUTTON_RELEASE
)
1835 g_signal_handlers_block_by_func(range
, (void*)gtk_scrollbar_event_after
, win
);
1837 const int orient
= wxWindow::OrientFromScrollDir(
1838 win
->ScrollDirFromRange(range
));
1839 wxScrollWinEvent
evt(wxEVT_SCROLLWIN_THUMBRELEASE
,
1840 win
->GetScrollPos(orient
), orient
);
1841 evt
.SetEventObject(win
);
1842 win
->GTKProcessEvent(evt
);
1846 //-----------------------------------------------------------------------------
1847 // "button_release_event" from scrollbar
1848 //-----------------------------------------------------------------------------
1851 gtk_scrollbar_button_release_event(GtkRange
* range
, GdkEventButton
*, wxWindow
* win
)
1853 g_blockEventsOnScroll
= false;
1854 win
->m_mouseButtonDown
= false;
1855 // If thumb tracking
1856 if (win
->m_isScrolling
)
1858 win
->m_isScrolling
= false;
1859 // Hook up handler to send thumb release event after this emission is finished.
1860 // To allow setting scroll position from event handler, sending event must
1861 // be deferred until after the GtkRange handler for this signal has run
1862 g_signal_handlers_unblock_by_func(range
, (void*)gtk_scrollbar_event_after
, win
);
1868 //-----------------------------------------------------------------------------
1869 // "realize" from m_widget
1870 //-----------------------------------------------------------------------------
1873 gtk_window_realized_callback(GtkWidget
* WXUNUSED(widget
), wxWindowGTK
* win
)
1875 win
->GTKHandleRealized();
1878 //-----------------------------------------------------------------------------
1879 // "size_allocate" from m_wxwindow or m_widget
1880 //-----------------------------------------------------------------------------
1883 size_allocate(GtkWidget
*, GtkAllocation
* alloc
, wxWindow
* win
)
1885 int w
= alloc
->width
;
1886 int h
= alloc
->height
;
1887 if (win
->m_wxwindow
)
1890 WX_PIZZA(win
->m_wxwindow
)->get_border(border
);
1891 w
-= border
.left
+ border
.right
;
1892 h
-= border
.top
+ border
.bottom
;
1896 if (win
->m_oldClientWidth
!= w
|| win
->m_oldClientHeight
!= h
)
1898 win
->m_oldClientWidth
= w
;
1899 win
->m_oldClientHeight
= h
;
1900 // this callback can be connected to m_wxwindow,
1901 // so always get size from m_widget->allocation
1903 gtk_widget_get_allocation(win
->m_widget
, &a
);
1904 win
->m_width
= a
.width
;
1905 win
->m_height
= a
.height
;
1906 if (!win
->m_nativeSizeEvent
)
1908 wxSizeEvent
event(win
->GetSize(), win
->GetId());
1909 event
.SetEventObject(win
);
1910 win
->GTKProcessEvent(event
);
1915 //-----------------------------------------------------------------------------
1917 //-----------------------------------------------------------------------------
1919 #if GTK_CHECK_VERSION(2, 8, 0)
1921 gtk_window_grab_broken( GtkWidget
*,
1922 GdkEventGrabBroken
*event
,
1925 // Mouse capture has been lost involuntarily, notify the application
1926 if(!event
->keyboard
&& wxWindow::GetCapture() == win
)
1928 wxMouseCaptureLostEvent
evt( win
->GetId() );
1929 evt
.SetEventObject( win
);
1930 win
->HandleWindowEvent( evt
);
1936 //-----------------------------------------------------------------------------
1937 // "style_set"/"style_updated"
1938 //-----------------------------------------------------------------------------
1941 static void style_updated(GtkWidget
*, wxWindow
* win
)
1943 static void style_updated(GtkWidget
*, GtkStyle
*, wxWindow
* win
)
1946 if (win
->IsTopLevel())
1948 wxSysColourChangedEvent event
;
1949 event
.SetEventObject(win
);
1950 win
->GTKProcessEvent(event
);
1954 // Border width could change, which will change client size.
1955 // Make sure size event occurs for this
1956 win
->m_oldClientWidth
= 0;
1960 //-----------------------------------------------------------------------------
1961 // "unrealize" from m_wxwindow
1962 //-----------------------------------------------------------------------------
1964 static void unrealize(GtkWidget
*, wxWindow
* win
)
1967 gtk_im_context_set_client_window(win
->m_imData
->context
, NULL
);
1969 g_signal_handlers_disconnect_by_func(
1970 win
->m_wxwindow
, (void*)style_updated
, win
);
1975 void wxWindowGTK::GTKHandleRealized()
1979 gtk_im_context_set_client_window
1982 m_wxwindow
? GTKGetDrawingWindow()
1983 : gtk_widget_get_window(m_widget
)
1987 // Use composited window if background is transparent, if supported.
1988 if (m_backgroundStyle
== wxBG_STYLE_TRANSPARENT
)
1990 #if wxGTK_HAS_COMPOSITING_SUPPORT
1991 if (IsTransparentBackgroundSupported())
1993 GdkWindow
* const window
= GTKGetDrawingWindow();
1995 gdk_window_set_composited(window
, true);
1998 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
2000 // We revert to erase mode if transparency is not supported
2001 m_backgroundStyle
= wxBG_STYLE_ERASE
;
2006 // We cannot set colours and fonts before the widget
2007 // been realized, so we do this directly after realization
2008 // or otherwise in idle time
2010 if (m_needsStyleChange
)
2012 SetBackgroundStyle(GetBackgroundStyle());
2013 m_needsStyleChange
= false;
2016 wxWindowCreateEvent
event(static_cast<wxWindow
*>(this));
2017 event
.SetEventObject( this );
2018 GTKProcessEvent( event
);
2020 GTKUpdateCursor(true, false);
2023 (IsTopLevel() || HasFlag(wxBORDER_RAISED
| wxBORDER_SUNKEN
| wxBORDER_THEME
)))
2025 // attaching to style changed signal after realization avoids initial
2026 // changes we don't care about
2027 const gchar
*detailed_signal
=
2033 g_signal_connect(m_wxwindow
,
2035 G_CALLBACK(style_updated
), this);
2039 // ----------------------------------------------------------------------------
2040 // this wxWindowBase function is implemented here (in platform-specific file)
2041 // because it is static and so couldn't be made virtual
2042 // ----------------------------------------------------------------------------
2044 wxWindow
*wxWindowBase::DoFindFocus()
2046 // For compatibility with wxMSW, pretend that showing a popup menu doesn't
2047 // change the focus and that it remains on the window showing it, even
2048 // though the real focus does change in GTK.
2049 extern wxMenu
*wxCurrentPopupMenu
;
2050 if ( wxCurrentPopupMenu
)
2051 return wxCurrentPopupMenu
->GetInvokingWindow();
2053 wxWindowGTK
*focus
= gs_pendingFocus
? gs_pendingFocus
: gs_currentFocus
;
2054 // the cast is necessary when we compile in wxUniversal mode
2055 return static_cast<wxWindow
*>(focus
);
2058 void wxWindowGTK::AddChildGTK(wxWindowGTK
* child
)
2060 wxASSERT_MSG(m_wxwindow
, "Cannot add a child to a window without a client area");
2062 // the window might have been scrolled already, we
2063 // have to adapt the position
2064 wxPizza
* pizza
= WX_PIZZA(m_wxwindow
);
2065 child
->m_x
+= pizza
->m_scroll_x
;
2066 child
->m_y
+= pizza
->m_scroll_y
;
2068 pizza
->put(child
->m_widget
,
2069 child
->m_x
, child
->m_y
, child
->m_width
, child
->m_height
);
2072 //-----------------------------------------------------------------------------
2074 //-----------------------------------------------------------------------------
2076 wxWindow
*wxGetActiveWindow()
2078 return wxWindow::FindFocus();
2082 // Under Unix this is implemented using X11 functions in utilsx11.cpp but we
2083 // need to have this function under Windows too, so provide at least a stub.
2084 #ifndef GDK_WINDOWING_X11
2085 bool wxGetKeyState(wxKeyCode
WXUNUSED(key
))
2087 wxFAIL_MSG(wxS("Not implemented under Windows"));
2090 #endif // __WINDOWS__
2092 static void GetMouseState(int& x
, int& y
, GdkModifierType
& mask
)
2094 wxWindow
* tlw
= NULL
;
2095 if (!wxTopLevelWindows
.empty())
2096 tlw
= wxTopLevelWindows
.front();
2097 GdkDisplay
* display
;
2098 if (tlw
&& tlw
->m_widget
)
2099 display
= gtk_widget_get_display(tlw
->m_widget
);
2101 display
= gdk_display_get_default();
2102 gdk_display_get_pointer(display
, NULL
, &x
, &y
, &mask
);
2105 wxMouseState
wxGetMouseState()
2111 GdkModifierType mask
;
2113 GetMouseState(x
, y
, mask
);
2117 ms
.SetLeftDown((mask
& GDK_BUTTON1_MASK
) != 0);
2118 ms
.SetMiddleDown((mask
& GDK_BUTTON2_MASK
) != 0);
2119 ms
.SetRightDown((mask
& GDK_BUTTON3_MASK
) != 0);
2120 // see the comment in InitMouseEvent()
2121 ms
.SetAux1Down((mask
& GDK_BUTTON4_MASK
) != 0);
2122 ms
.SetAux2Down((mask
& GDK_BUTTON5_MASK
) != 0);
2124 ms
.SetControlDown((mask
& GDK_CONTROL_MASK
) != 0);
2125 ms
.SetShiftDown((mask
& GDK_SHIFT_MASK
) != 0);
2126 ms
.SetAltDown((mask
& GDK_MOD1_MASK
) != 0);
2127 ms
.SetMetaDown((mask
& GDK_META_MASK
) != 0);
2132 //-----------------------------------------------------------------------------
2134 //-----------------------------------------------------------------------------
2136 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2138 #ifdef __WXUNIVERSAL__
2139 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK
, wxWindowBase
)
2140 #endif // __WXUNIVERSAL__
2142 void wxWindowGTK::Init()
2147 m_focusWidget
= NULL
;
2157 m_showOnIdle
= false;
2160 m_nativeSizeEvent
= false;
2162 m_paintContext
= NULL
;
2165 m_isScrolling
= false;
2166 m_mouseButtonDown
= false;
2168 // initialize scrolling stuff
2169 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
2171 m_scrollBar
[dir
] = NULL
;
2172 m_scrollPos
[dir
] = 0;
2176 m_oldClientHeight
= 0;
2178 m_clipPaintRegion
= false;
2180 m_needsStyleChange
= false;
2182 m_cursor
= *wxSTANDARD_CURSOR
;
2185 m_dirtyTabOrder
= false;
2188 wxWindowGTK::wxWindowGTK()
2193 wxWindowGTK::wxWindowGTK( wxWindow
*parent
,
2198 const wxString
&name
)
2202 Create( parent
, id
, pos
, size
, style
, name
);
2205 void wxWindowGTK::GTKCreateScrolledWindowWith(GtkWidget
* view
)
2207 wxASSERT_MSG( HasFlag(wxHSCROLL
) || HasFlag(wxVSCROLL
),
2208 wxS("Must not be called if scrolling is not needed.") );
2210 m_widget
= gtk_scrolled_window_new( NULL
, NULL
);
2212 GtkScrolledWindow
*scrolledWindow
= GTK_SCROLLED_WINDOW(m_widget
);
2214 // There is a conflict with default bindings at GTK+
2215 // level between scrolled windows and notebooks both of which want to use
2216 // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
2217 // direction and notebooks for changing pages -- we decide that if we don't
2218 // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
2219 // means we can get working keyboard navigation in notebooks
2220 if ( !HasFlag(wxHSCROLL
) )
2223 bindings
= gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget
));
2226 gtk_binding_entry_remove(bindings
, GDK_Page_Up
, GDK_CONTROL_MASK
);
2227 gtk_binding_entry_remove(bindings
, GDK_Page_Down
, GDK_CONTROL_MASK
);
2231 if (HasFlag(wxALWAYS_SHOW_SB
))
2233 gtk_scrolled_window_set_policy( scrolledWindow
, GTK_POLICY_ALWAYS
, GTK_POLICY_ALWAYS
);
2237 gtk_scrolled_window_set_policy( scrolledWindow
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2240 m_scrollBar
[ScrollDir_Horz
] = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(scrolledWindow
));
2241 m_scrollBar
[ScrollDir_Vert
] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(scrolledWindow
));
2242 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2243 gtk_range_set_inverted( m_scrollBar
[ScrollDir_Horz
], TRUE
);
2245 gtk_container_add( GTK_CONTAINER(m_widget
), view
);
2247 // connect various scroll-related events
2248 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
2250 // these handlers block mouse events to any window during scrolling
2251 // such as motion events and prevent GTK and wxWidgets from fighting
2252 // over where the slider should be
2253 g_signal_connect(m_scrollBar
[dir
], "button_press_event",
2254 G_CALLBACK(gtk_scrollbar_button_press_event
), this);
2255 g_signal_connect(m_scrollBar
[dir
], "button_release_event",
2256 G_CALLBACK(gtk_scrollbar_button_release_event
), this);
2258 gulong handler_id
= g_signal_connect(m_scrollBar
[dir
], "event_after",
2259 G_CALLBACK(gtk_scrollbar_event_after
), this);
2260 g_signal_handler_block(m_scrollBar
[dir
], handler_id
);
2262 // these handlers get notified when scrollbar slider moves
2263 g_signal_connect_after(m_scrollBar
[dir
], "value_changed",
2264 G_CALLBACK(gtk_scrollbar_value_changed
), this);
2267 gtk_widget_show( view
);
2270 bool wxWindowGTK::Create( wxWindow
*parent
,
2275 const wxString
&name
)
2277 // Get default border
2278 wxBorder border
= GetBorder(style
);
2280 style
&= ~wxBORDER_MASK
;
2283 if (!PreCreation( parent
, pos
, size
) ||
2284 !CreateBase( parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
))
2286 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2290 // We should accept the native look
2292 GtkScrolledWindowClass
*scroll_class
= GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget
) );
2293 scroll_class
->scrollbar_spacing
= 0;
2297 m_wxwindow
= wxPizza::New(m_windowStyle
);
2298 #ifndef __WXUNIVERSAL__
2299 if (HasFlag(wxPizza::BORDER_STYLES
))
2301 g_signal_connect(m_wxwindow
, "parent_set",
2302 G_CALLBACK(parent_set
), this);
2305 if (!HasFlag(wxHSCROLL
) && !HasFlag(wxVSCROLL
))
2306 m_widget
= m_wxwindow
;
2308 GTKCreateScrolledWindowWith(m_wxwindow
);
2309 g_object_ref(m_widget
);
2312 m_parent
->DoAddChild( this );
2314 m_focusWidget
= m_wxwindow
;
2316 SetCanFocus(AcceptsFocus());
2323 wxWindowGTK::~wxWindowGTK()
2327 if (gs_currentFocus
== this)
2328 gs_currentFocus
= NULL
;
2329 if (gs_pendingFocus
== this)
2330 gs_pendingFocus
= NULL
;
2332 if ( gs_deferredFocusOut
== this )
2333 gs_deferredFocusOut
= NULL
;
2337 // destroy children before destroying this window itself
2340 // unhook focus handlers to prevent stray events being
2341 // propagated to this (soon to be) dead object
2342 if (m_focusWidget
!= NULL
)
2344 g_signal_handlers_disconnect_by_func (m_focusWidget
,
2345 (gpointer
) gtk_window_focus_in_callback
,
2347 g_signal_handlers_disconnect_by_func (m_focusWidget
,
2348 (gpointer
) gtk_window_focus_out_callback
,
2355 // delete before the widgets to avoid a crash on solaris
2359 // avoid problem with GTK+ 2.18 where a frozen window causes the whole
2360 // TLW to be frozen, and if the window is then destroyed, nothing ever
2361 // gets painted again
2367 // Note that gtk_widget_destroy() does not destroy the widget, it just
2368 // emits the "destroy" signal. The widget is not actually destroyed
2369 // until its reference count drops to zero.
2370 gtk_widget_destroy(m_widget
);
2371 // Release our reference, should be the last one
2372 g_object_unref(m_widget
);
2378 bool wxWindowGTK::PreCreation( wxWindowGTK
*parent
, const wxPoint
&pos
, const wxSize
&size
)
2380 if ( GTKNeedsParent() )
2382 wxCHECK_MSG( parent
, false, wxT("Must have non-NULL parent") );
2385 // Use either the given size, or the default if -1 is given.
2386 // See wxWindowBase for these functions.
2387 m_width
= WidthDefault(size
.x
) ;
2388 m_height
= HeightDefault(size
.y
);
2390 if (pos
!= wxDefaultPosition
)
2399 void wxWindowGTK::PostCreation()
2401 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
2403 #if wxGTK_HAS_COMPOSITING_SUPPORT
2404 // Set RGBA visual as soon as possible to minimize the possibility that
2405 // somebody uses the wrong one.
2406 if ( m_backgroundStyle
== wxBG_STYLE_TRANSPARENT
&&
2407 IsTransparentBackgroundSupported() )
2409 GdkScreen
*screen
= gtk_widget_get_screen (m_widget
);
2411 gtk_widget_set_visual(m_widget
, gdk_screen_get_rgba_visual(screen
));
2413 GdkColormap
*rgba_colormap
= gdk_screen_get_rgba_colormap (screen
);
2416 gtk_widget_set_colormap(m_widget
, rgba_colormap
);
2419 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
2425 // these get reported to wxWidgets -> wxPaintEvent
2427 g_signal_connect(m_wxwindow
, "draw", G_CALLBACK(draw
), this);
2429 g_signal_connect(m_wxwindow
, "expose_event", G_CALLBACK(expose_event
), this);
2432 if (GetLayoutDirection() == wxLayout_LeftToRight
)
2433 gtk_widget_set_redraw_on_allocate(m_wxwindow
, HasFlag(wxFULL_REPAINT_ON_RESIZE
));
2436 // Create input method handler
2437 m_imData
= new wxGtkIMData
;
2439 // Cannot handle drawing preedited text yet
2440 gtk_im_context_set_use_preedit( m_imData
->context
, FALSE
);
2442 g_signal_connect (m_imData
->context
, "commit",
2443 G_CALLBACK (gtk_wxwindow_commit_cb
), this);
2444 g_signal_connect(m_wxwindow
, "unrealize", G_CALLBACK(unrealize
), this);
2449 if (!GTK_IS_WINDOW(m_widget
))
2451 if (m_focusWidget
== NULL
)
2452 m_focusWidget
= m_widget
;
2456 g_signal_connect (m_focusWidget
, "focus_in_event",
2457 G_CALLBACK (gtk_window_focus_in_callback
), this);
2458 g_signal_connect (m_focusWidget
, "focus_out_event",
2459 G_CALLBACK (gtk_window_focus_out_callback
), this);
2463 g_signal_connect_after (m_focusWidget
, "focus_in_event",
2464 G_CALLBACK (gtk_window_focus_in_callback
), this);
2465 g_signal_connect_after (m_focusWidget
, "focus_out_event",
2466 G_CALLBACK (gtk_window_focus_out_callback
), this);
2470 if ( !AcceptsFocusFromKeyboard() )
2474 g_signal_connect(m_widget
, "focus",
2475 G_CALLBACK(wx_window_focus_callback
), this);
2478 // connect to the various key and mouse handlers
2480 GtkWidget
*connect_widget
= GetConnectWidget();
2482 ConnectWidget( connect_widget
);
2484 // We cannot set colours, fonts and cursors before the widget has been
2485 // realized, so we do this directly after realization -- unless the widget
2486 // was in fact realized already.
2487 if ( gtk_widget_get_realized(connect_widget
) )
2489 gtk_window_realized_callback(connect_widget
, this);
2493 g_signal_connect (connect_widget
, "realize",
2494 G_CALLBACK (gtk_window_realized_callback
), this);
2499 g_signal_connect(m_wxwindow
? m_wxwindow
: m_widget
, "size_allocate",
2500 G_CALLBACK(size_allocate
), this);
2503 #if GTK_CHECK_VERSION(2, 8, 0)
2505 if ( gtk_check_version(2,8,0) == NULL
)
2508 // Make sure we can notify the app when mouse capture is lost
2511 g_signal_connect (m_wxwindow
, "grab_broken_event",
2512 G_CALLBACK (gtk_window_grab_broken
), this);
2515 if ( connect_widget
!= m_wxwindow
)
2517 g_signal_connect (connect_widget
, "grab_broken_event",
2518 G_CALLBACK (gtk_window_grab_broken
), this);
2521 #endif // GTK+ >= 2.8
2523 if (!WX_IS_PIZZA(gtk_widget_get_parent(m_widget
)) && !GTK_IS_WINDOW(m_widget
))
2524 gtk_widget_set_size_request(m_widget
, m_width
, m_height
);
2526 InheritAttributes();
2530 SetLayoutDirection(wxLayout_Default
);
2532 // unless the window was created initially hidden (i.e. Hide() had been
2533 // called before Create()), we should show it at GTK+ level as well
2535 gtk_widget_show( m_widget
);
2539 wxWindowGTK::GTKConnectWidget(const char *signal
, wxGTKCallback callback
)
2541 return g_signal_connect(m_widget
, signal
, callback
, this);
2544 void wxWindowGTK::ConnectWidget( GtkWidget
*widget
)
2546 g_signal_connect (widget
, "key_press_event",
2547 G_CALLBACK (gtk_window_key_press_callback
), this);
2548 g_signal_connect (widget
, "key_release_event",
2549 G_CALLBACK (gtk_window_key_release_callback
), this);
2550 g_signal_connect (widget
, "button_press_event",
2551 G_CALLBACK (gtk_window_button_press_callback
), this);
2552 g_signal_connect (widget
, "button_release_event",
2553 G_CALLBACK (gtk_window_button_release_callback
), this);
2554 g_signal_connect (widget
, "motion_notify_event",
2555 G_CALLBACK (gtk_window_motion_notify_callback
), this);
2557 g_signal_connect (widget
, "scroll_event",
2558 G_CALLBACK (window_scroll_event
), this);
2559 if (m_scrollBar
[ScrollDir_Horz
])
2560 g_signal_connect (m_scrollBar
[ScrollDir_Horz
], "scroll_event",
2561 G_CALLBACK (window_scroll_event_hscrollbar
), this);
2562 if (m_scrollBar
[ScrollDir_Vert
])
2563 g_signal_connect (m_scrollBar
[ScrollDir_Vert
], "scroll_event",
2564 G_CALLBACK (window_scroll_event
), this);
2566 g_signal_connect (widget
, "popup_menu",
2567 G_CALLBACK (wxgtk_window_popup_menu_callback
), this);
2568 g_signal_connect (widget
, "enter_notify_event",
2569 G_CALLBACK (gtk_window_enter_callback
), this);
2570 g_signal_connect (widget
, "leave_notify_event",
2571 G_CALLBACK (gtk_window_leave_callback
), this);
2574 bool wxWindowGTK::Destroy()
2578 return wxWindowBase::Destroy();
2581 static GSList
* gs_queueResizeList
;
2584 static gboolean
queue_resize(void*)
2586 gdk_threads_enter();
2587 for (GSList
* p
= gs_queueResizeList
; p
; p
= p
->next
)
2591 gtk_widget_queue_resize(GTK_WIDGET(p
->data
));
2592 g_object_remove_weak_pointer(G_OBJECT(p
->data
), &p
->data
);
2595 g_slist_free(gs_queueResizeList
);
2596 gs_queueResizeList
= NULL
;
2597 gdk_threads_leave();
2602 void wxWindowGTK::DoMoveWindow(int x
, int y
, int width
, int height
)
2604 gtk_widget_set_size_request(m_widget
, width
, height
);
2605 GtkWidget
* parent
= gtk_widget_get_parent(m_widget
);
2606 if (WX_IS_PIZZA(parent
))
2607 WX_PIZZA(parent
)->move(m_widget
, x
, y
, width
, height
);
2609 // With GTK3, gtk_widget_queue_resize() is ignored while a size-allocate
2610 // is in progress. This situation is common in wxWidgets, since
2611 // size-allocate can generate wxSizeEvent and size event handlers often
2612 // call SetSize(), directly or indirectly. Work around this by deferring
2613 // the queue-resize until after size-allocate processing is finished.
2614 if (g_slist_find(gs_queueResizeList
, m_widget
) == NULL
)
2616 if (gs_queueResizeList
== NULL
)
2617 g_idle_add_full(GTK_PRIORITY_RESIZE
, queue_resize
, NULL
, NULL
);
2618 gs_queueResizeList
= g_slist_prepend(gs_queueResizeList
, m_widget
);
2619 g_object_add_weak_pointer(G_OBJECT(m_widget
), &gs_queueResizeList
->data
);
2623 void wxWindowGTK::ConstrainSize()
2626 // GPE's window manager doesn't like size hints at all, esp. when the user
2627 // has to use the virtual keyboard, so don't constrain size there
2631 const wxSize minSize
= GetMinSize();
2632 const wxSize maxSize
= GetMaxSize();
2633 if (minSize
.x
> 0 && m_width
< minSize
.x
) m_width
= minSize
.x
;
2634 if (minSize
.y
> 0 && m_height
< minSize
.y
) m_height
= minSize
.y
;
2635 if (maxSize
.x
> 0 && m_width
> maxSize
.x
) m_width
= maxSize
.x
;
2636 if (maxSize
.y
> 0 && m_height
> maxSize
.y
) m_height
= maxSize
.y
;
2640 void wxWindowGTK::DoSetSize( int x
, int y
, int width
, int height
, int sizeFlags
)
2642 wxCHECK_RET(m_widget
, "invalid window");
2644 int scrollX
= 0, scrollY
= 0;
2645 GtkWidget
* parent
= gtk_widget_get_parent(m_widget
);
2646 if (WX_IS_PIZZA(parent
))
2648 wxPizza
* pizza
= WX_PIZZA(parent
);
2649 scrollX
= pizza
->m_scroll_x
;
2650 scrollY
= pizza
->m_scroll_y
;
2652 if (x
!= -1 || (sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
2656 if (y
!= -1 || (sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
2661 // calculate the best size if we should auto size the window
2662 if ( ((sizeFlags
& wxSIZE_AUTO_WIDTH
) && width
== -1) ||
2663 ((sizeFlags
& wxSIZE_AUTO_HEIGHT
) && height
== -1) )
2665 const wxSize sizeBest
= GetBestSize();
2666 if ( (sizeFlags
& wxSIZE_AUTO_WIDTH
) && width
== -1 )
2668 if ( (sizeFlags
& wxSIZE_AUTO_HEIGHT
) && height
== -1 )
2669 height
= sizeBest
.y
;
2677 const bool sizeChange
= m_width
!= width
|| m_height
!= height
;
2678 if (sizeChange
|| m_x
!= x
|| m_y
!= y
)
2685 /* the default button has a border around it */
2686 if (gtk_widget_get_can_default(m_widget
))
2688 GtkBorder
*default_border
= NULL
;
2689 gtk_widget_style_get( m_widget
, "default_border", &default_border
, NULL
);
2692 x
-= default_border
->left
;
2693 y
-= default_border
->top
;
2694 width
+= default_border
->left
+ default_border
->right
;
2695 height
+= default_border
->top
+ default_border
->bottom
;
2696 gtk_border_free( default_border
);
2700 DoMoveWindow(x
, y
, width
, height
);
2703 if ((sizeChange
&& !m_nativeSizeEvent
) || (sizeFlags
& wxSIZE_FORCE_EVENT
))
2705 // update these variables to keep size_allocate handler
2706 // from sending another size event for this change
2707 GetClientSize( &m_oldClientWidth
, &m_oldClientHeight
);
2709 wxSizeEvent
event( wxSize(m_width
,m_height
), GetId() );
2710 event
.SetEventObject( this );
2711 HandleWindowEvent( event
);
2715 bool wxWindowGTK::GTKShowFromOnIdle()
2717 if (IsShown() && m_showOnIdle
&& !gtk_widget_get_visible (m_widget
))
2719 GtkAllocation alloc
;
2722 alloc
.width
= m_width
;
2723 alloc
.height
= m_height
;
2724 gtk_widget_size_allocate( m_widget
, &alloc
);
2725 gtk_widget_show( m_widget
);
2726 wxShowEvent
eventShow(GetId(), true);
2727 eventShow
.SetEventObject(this);
2728 HandleWindowEvent(eventShow
);
2729 m_showOnIdle
= false;
2736 void wxWindowGTK::OnInternalIdle()
2738 if ( gs_deferredFocusOut
)
2739 GTKHandleDeferredFocusOut();
2741 // Check if we have to show window now
2742 if (GTKShowFromOnIdle()) return;
2744 if ( m_dirtyTabOrder
)
2746 m_dirtyTabOrder
= false;
2750 wxWindowBase::OnInternalIdle();
2753 void wxWindowGTK::DoGetSize( int *width
, int *height
) const
2755 if (width
) (*width
) = m_width
;
2756 if (height
) (*height
) = m_height
;
2759 void wxWindowGTK::DoSetClientSize( int width
, int height
)
2761 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2763 const wxSize size
= GetSize();
2764 const wxSize clientSize
= GetClientSize();
2765 SetSize(width
+ (size
.x
- clientSize
.x
), height
+ (size
.y
- clientSize
.y
));
2768 void wxWindowGTK::DoGetClientSize( int *width
, int *height
) const
2770 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2777 // if window is scrollable, account for scrollbars
2778 if ( GTK_IS_SCROLLED_WINDOW(m_widget
) )
2780 GtkPolicyType policy
[ScrollDir_Max
];
2781 gtk_scrolled_window_get_policy(GTK_SCROLLED_WINDOW(m_widget
),
2782 &policy
[ScrollDir_Horz
],
2783 &policy
[ScrollDir_Vert
]);
2784 const int scrollbar_spacing
=
2785 GTK_SCROLLED_WINDOW_GET_CLASS(m_widget
)->scrollbar_spacing
;
2787 for ( int i
= 0; i
< ScrollDir_Max
; i
++ )
2789 // don't account for the scrollbars we don't have
2790 GtkRange
* const range
= m_scrollBar
[i
];
2794 // nor for the ones we have but don't current show
2795 switch ( policy
[i
] )
2797 case GTK_POLICY_NEVER
:
2798 // never shown so doesn't take any place
2801 case GTK_POLICY_ALWAYS
:
2802 // no checks necessary
2805 case GTK_POLICY_AUTOMATIC
:
2806 // may be shown or not, check
2807 GtkAdjustment
*adj
= gtk_range_get_adjustment(range
);
2808 if (gtk_adjustment_get_upper(adj
) <= gtk_adjustment_get_page_size(adj
))
2814 GtkWidget
* widget
= GTK_WIDGET(range
);
2815 if (i
== ScrollDir_Horz
)
2819 gtk_widget_get_preferred_height(widget
, NULL
, &req
.height
);
2820 h
-= req
.height
+ scrollbar_spacing
;
2827 gtk_widget_get_preferred_width(widget
, NULL
, &req
.width
);
2828 w
-= req
.width
+ scrollbar_spacing
;
2831 #else // !__WXGTK3__
2832 gtk_widget_size_request(GTK_WIDGET(range
), &req
);
2833 if (i
== ScrollDir_Horz
)
2834 h
-= req
.height
+ scrollbar_spacing
;
2836 w
-= req
.width
+ scrollbar_spacing
;
2837 #endif // !__WXGTK3__
2841 const wxSize sizeBorders
= DoGetBorderSize();
2851 if (width
) *width
= w
;
2852 if (height
) *height
= h
;
2855 wxSize
wxWindowGTK::DoGetBorderSize() const
2858 return wxWindowBase::DoGetBorderSize();
2861 WX_PIZZA(m_wxwindow
)->get_border(border
);
2862 return wxSize(border
.left
+ border
.right
, border
.top
+ border
.bottom
);
2865 void wxWindowGTK::DoGetPosition( int *x
, int *y
) const
2869 GtkWidget
* parent
= NULL
;
2871 parent
= gtk_widget_get_parent(m_widget
);
2872 if (WX_IS_PIZZA(parent
))
2874 wxPizza
* pizza
= WX_PIZZA(parent
);
2875 dx
= pizza
->m_scroll_x
;
2876 dy
= pizza
->m_scroll_y
;
2878 if (x
) (*x
) = m_x
- dx
;
2879 if (y
) (*y
) = m_y
- dy
;
2882 void wxWindowGTK::DoClientToScreen( int *x
, int *y
) const
2884 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2886 if (gtk_widget_get_window(m_widget
) == NULL
) return;
2888 GdkWindow
*source
= NULL
;
2890 source
= gtk_widget_get_window(m_wxwindow
);
2892 source
= gtk_widget_get_window(m_widget
);
2896 gdk_window_get_origin( source
, &org_x
, &org_y
);
2900 if (!gtk_widget_get_has_window(m_widget
))
2903 gtk_widget_get_allocation(m_widget
, &a
);
2912 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2913 *x
= (GetClientSize().x
- *x
) + org_x
;
2921 void wxWindowGTK::DoScreenToClient( int *x
, int *y
) const
2923 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2925 if (!gtk_widget_get_realized(m_widget
)) 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
);
2950 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2951 *x
= (GetClientSize().x
- *x
) - org_x
;
2958 bool wxWindowGTK::Show( bool show
)
2960 if ( !wxWindowBase::Show(show
) )
2966 // notice that we may call Hide() before the window is created and this is
2967 // actually useful to create it hidden initially -- but we can't call
2968 // Show() before it is created
2971 wxASSERT_MSG( !show
, "can't show invalid window" );
2979 // defer until later
2983 gtk_widget_show(m_widget
);
2987 gtk_widget_hide(m_widget
);
2990 wxShowEvent
eventShow(GetId(), show
);
2991 eventShow
.SetEventObject(this);
2992 HandleWindowEvent(eventShow
);
2997 void wxWindowGTK::DoEnable( bool enable
)
2999 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3001 gtk_widget_set_sensitive( m_widget
, enable
);
3002 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
3003 gtk_widget_set_sensitive( m_wxwindow
, enable
);
3006 int wxWindowGTK::GetCharHeight() const
3008 wxCHECK_MSG( (m_widget
!= NULL
), 12, wxT("invalid window") );
3010 wxFont font
= GetFont();
3011 wxCHECK_MSG( font
.IsOk(), 12, wxT("invalid font") );
3013 PangoContext
* context
= gtk_widget_get_pango_context(m_widget
);
3018 PangoFontDescription
*desc
= font
.GetNativeFontInfo()->description
;
3019 PangoLayout
*layout
= pango_layout_new(context
);
3020 pango_layout_set_font_description(layout
, desc
);
3021 pango_layout_set_text(layout
, "H", 1);
3022 PangoLayoutLine
*line
= (PangoLayoutLine
*)pango_layout_get_lines(layout
)->data
;
3024 PangoRectangle rect
;
3025 pango_layout_line_get_extents(line
, NULL
, &rect
);
3027 g_object_unref (layout
);
3029 return (int) PANGO_PIXELS(rect
.height
);
3032 int wxWindowGTK::GetCharWidth() const
3034 wxCHECK_MSG( (m_widget
!= NULL
), 8, wxT("invalid window") );
3036 wxFont font
= GetFont();
3037 wxCHECK_MSG( font
.IsOk(), 8, wxT("invalid font") );
3039 PangoContext
* context
= gtk_widget_get_pango_context(m_widget
);
3044 PangoFontDescription
*desc
= font
.GetNativeFontInfo()->description
;
3045 PangoLayout
*layout
= pango_layout_new(context
);
3046 pango_layout_set_font_description(layout
, desc
);
3047 pango_layout_set_text(layout
, "g", 1);
3048 PangoLayoutLine
*line
= (PangoLayoutLine
*)pango_layout_get_lines(layout
)->data
;
3050 PangoRectangle rect
;
3051 pango_layout_line_get_extents(line
, NULL
, &rect
);
3053 g_object_unref (layout
);
3055 return (int) PANGO_PIXELS(rect
.width
);
3058 void wxWindowGTK::DoGetTextExtent( const wxString
& string
,
3062 int *externalLeading
,
3063 const wxFont
*theFont
) const
3065 wxFont fontToUse
= theFont
? *theFont
: GetFont();
3067 wxCHECK_RET( fontToUse
.IsOk(), wxT("invalid font") );
3076 PangoContext
*context
= NULL
;
3078 context
= gtk_widget_get_pango_context( m_widget
);
3087 PangoFontDescription
*desc
= fontToUse
.GetNativeFontInfo()->description
;
3088 PangoLayout
*layout
= pango_layout_new(context
);
3089 pango_layout_set_font_description(layout
, desc
);
3091 const wxCharBuffer data
= wxGTK_CONV( string
);
3093 pango_layout_set_text(layout
, data
, strlen(data
));
3096 PangoRectangle rect
;
3097 pango_layout_get_extents(layout
, NULL
, &rect
);
3099 if (x
) (*x
) = (wxCoord
) PANGO_PIXELS(rect
.width
);
3100 if (y
) (*y
) = (wxCoord
) PANGO_PIXELS(rect
.height
);
3103 PangoLayoutIter
*iter
= pango_layout_get_iter(layout
);
3104 int baseline
= pango_layout_iter_get_baseline(iter
);
3105 pango_layout_iter_free(iter
);
3106 *descent
= *y
- PANGO_PIXELS(baseline
);
3108 if (externalLeading
) (*externalLeading
) = 0; // ??
3110 g_object_unref (layout
);
3113 void wxWindowGTK::GTKDisableFocusOutEvent()
3115 g_signal_handlers_block_by_func( m_focusWidget
,
3116 (gpointer
) gtk_window_focus_out_callback
, this);
3119 void wxWindowGTK::GTKEnableFocusOutEvent()
3121 g_signal_handlers_unblock_by_func( m_focusWidget
,
3122 (gpointer
) gtk_window_focus_out_callback
, this);
3125 bool wxWindowGTK::GTKHandleFocusIn()
3127 // Disable default focus handling for custom windows since the default GTK+
3128 // handler issues a repaint
3129 const bool retval
= m_wxwindow
? true : false;
3132 // NB: if there's still unprocessed deferred focus-out event (see
3133 // GTKHandleFocusOut() for explanation), we need to process it first so
3134 // that the order of focus events -- focus-out first, then focus-in
3135 // elsewhere -- is preserved
3136 if ( gs_deferredFocusOut
)
3138 if ( GTKNeedsToFilterSameWindowFocus() &&
3139 gs_deferredFocusOut
== this )
3141 // GTK+ focus changed from this wxWindow back to itself, so don't
3142 // emit any events at all
3143 wxLogTrace(TRACE_FOCUS
,
3144 "filtered out spurious focus change within %s(%p, %s)",
3145 GetClassInfo()->GetClassName(), this, GetLabel());
3146 gs_deferredFocusOut
= NULL
;
3150 // otherwise we need to send focus-out first
3151 wxASSERT_MSG ( gs_deferredFocusOut
!= this,
3152 "GTKHandleFocusIn(GTKFocus_Normal) called even though focus changed back to itself - derived class should handle this" );
3153 GTKHandleDeferredFocusOut();
3157 wxLogTrace(TRACE_FOCUS
,
3158 "handling focus_in event for %s(%p, %s)",
3159 GetClassInfo()->GetClassName(), this, GetLabel());
3162 gtk_im_context_focus_in(m_imData
->context
);
3164 gs_currentFocus
= this;
3165 gs_pendingFocus
= NULL
;
3168 // caret needs to be informed about focus change
3169 wxCaret
*caret
= GetCaret();
3172 caret
->OnSetFocus();
3174 #endif // wxUSE_CARET
3176 // Notify the parent keeping track of focus for the kbd navigation
3177 // purposes that we got it.
3178 wxChildFocusEvent
eventChildFocus(static_cast<wxWindow
*>(this));
3179 GTKProcessEvent(eventChildFocus
);
3181 wxFocusEvent
eventFocus(wxEVT_SET_FOCUS
, GetId());
3182 eventFocus
.SetEventObject(this);
3183 GTKProcessEvent(eventFocus
);
3188 bool wxWindowGTK::GTKHandleFocusOut()
3190 // Disable default focus handling for custom windows since the default GTK+
3191 // handler issues a repaint
3192 const bool retval
= m_wxwindow
? true : false;
3195 // NB: If a control is composed of several GtkWidgets and when focus
3196 // changes from one of them to another within the same wxWindow, we get
3197 // a focus-out event followed by focus-in for another GtkWidget owned
3198 // by the same wx control. We don't want to generate two spurious
3199 // wxEVT_SET_FOCUS events in this case, so we defer sending wx events
3200 // from GTKHandleFocusOut() until we know for sure it's not coming back
3201 // (i.e. in GTKHandleFocusIn() or at idle time).
3202 if ( GTKNeedsToFilterSameWindowFocus() )
3204 wxASSERT_MSG( gs_deferredFocusOut
== NULL
,
3205 "deferred focus out event already pending" );
3206 wxLogTrace(TRACE_FOCUS
,
3207 "deferring focus_out event for %s(%p, %s)",
3208 GetClassInfo()->GetClassName(), this, GetLabel());
3209 gs_deferredFocusOut
= this;
3213 GTKHandleFocusOutNoDeferring();
3218 void wxWindowGTK::GTKHandleFocusOutNoDeferring()
3220 wxLogTrace(TRACE_FOCUS
,
3221 "handling focus_out event for %s(%p, %s)",
3222 GetClassInfo()->GetClassName(), this, GetLabel());
3225 gtk_im_context_focus_out(m_imData
->context
);
3227 if ( gs_currentFocus
!= this )
3229 // Something is terribly wrong, gs_currentFocus is out of sync with the
3230 // real focus. We will reset it to NULL anyway, because after this
3231 // focus-out event is handled, one of the following with happen:
3233 // * either focus will go out of the app altogether, in which case
3234 // gs_currentFocus _should_ be NULL
3236 // * or it goes to another control, in which case focus-in event will
3237 // follow immediately and it will set gs_currentFocus to the right
3239 wxLogDebug("window %s(%p, %s) lost focus even though it didn't have it",
3240 GetClassInfo()->GetClassName(), this, GetLabel());
3242 gs_currentFocus
= NULL
;
3245 // caret needs to be informed about focus change
3246 wxCaret
*caret
= GetCaret();
3249 caret
->OnKillFocus();
3251 #endif // wxUSE_CARET
3253 wxFocusEvent
event( wxEVT_KILL_FOCUS
, GetId() );
3254 event
.SetEventObject( this );
3255 event
.SetWindow( FindFocus() );
3256 GTKProcessEvent( event
);
3260 void wxWindowGTK::GTKHandleDeferredFocusOut()
3262 // NB: See GTKHandleFocusOut() for explanation. This function is called
3263 // from either GTKHandleFocusIn() or OnInternalIdle() to process
3265 if ( gs_deferredFocusOut
)
3267 wxWindowGTK
*win
= gs_deferredFocusOut
;
3268 gs_deferredFocusOut
= NULL
;
3270 wxLogTrace(TRACE_FOCUS
,
3271 "processing deferred focus_out event for %s(%p, %s)",
3272 win
->GetClassInfo()->GetClassName(), win
, win
->GetLabel());
3274 win
->GTKHandleFocusOutNoDeferring();
3278 void wxWindowGTK::SetFocus()
3280 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
3282 // Setting "physical" focus is not immediate in GTK+ and while
3283 // gtk_widget_is_focus ("determines if the widget is the focus widget
3284 // within its toplevel", i.e. returns true for one widget per TLW, not
3285 // globally) returns true immediately after grabbing focus,
3286 // GTK_WIDGET_HAS_FOCUS (which returns true only for the one widget that
3287 // has focus at the moment) takes effect only after the window is shown
3288 // (if it was hidden at the moment of the call) or at the next event loop
3291 // Because we want to FindFocus() call immediately following
3292 // foo->SetFocus() to return foo, we have to keep track of "pending" focus
3294 gs_pendingFocus
= this;
3296 GtkWidget
*widget
= m_wxwindow
? m_wxwindow
: m_focusWidget
;
3298 if ( GTK_IS_CONTAINER(widget
) &&
3299 !gtk_widget_get_can_focus(widget
) )
3301 wxLogTrace(TRACE_FOCUS
,
3302 wxT("Setting focus to a child of %s(%p, %s)"),
3303 GetClassInfo()->GetClassName(), this, GetLabel().c_str());
3304 gtk_widget_child_focus(widget
, GTK_DIR_TAB_FORWARD
);
3308 wxLogTrace(TRACE_FOCUS
,
3309 wxT("Setting focus to %s(%p, %s)"),
3310 GetClassInfo()->GetClassName(), this, GetLabel().c_str());
3311 gtk_widget_grab_focus(widget
);
3315 void wxWindowGTK::SetCanFocus(bool canFocus
)
3317 gtk_widget_set_can_focus(m_widget
, canFocus
);
3319 if ( m_wxwindow
&& (m_widget
!= m_wxwindow
) )
3321 gtk_widget_set_can_focus(m_wxwindow
, canFocus
);
3325 bool wxWindowGTK::Reparent( wxWindowBase
*newParentBase
)
3327 wxCHECK_MSG( (m_widget
!= NULL
), false, wxT("invalid window") );
3329 wxWindowGTK
* const newParent
= (wxWindowGTK
*)newParentBase
;
3331 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3333 if ( !wxWindowBase::Reparent(newParent
) )
3336 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3338 // Notice that old m_parent pointer might be non-NULL here but the widget
3339 // still not have any parent at GTK level if it's a notebook page that had
3340 // been removed from the notebook so test this at GTK level and not wx one.
3341 if ( GtkWidget
*parentGTK
= gtk_widget_get_parent(m_widget
) )
3342 gtk_container_remove(GTK_CONTAINER(parentGTK
), m_widget
);
3344 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3348 if (gtk_widget_get_visible (newParent
->m_widget
))
3350 m_showOnIdle
= true;
3351 gtk_widget_hide( m_widget
);
3353 /* insert GTK representation */
3354 newParent
->AddChildGTK(this);
3357 SetLayoutDirection(wxLayout_Default
);
3362 void wxWindowGTK::DoAddChild(wxWindowGTK
*child
)
3364 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
3365 wxASSERT_MSG( (child
!= NULL
), wxT("invalid child window") );
3370 /* insert GTK representation */
3374 void wxWindowGTK::AddChild(wxWindowBase
*child
)
3376 wxWindowBase::AddChild(child
);
3377 m_dirtyTabOrder
= true;
3378 wxTheApp
->WakeUpIdle();
3381 void wxWindowGTK::RemoveChild(wxWindowBase
*child
)
3383 wxWindowBase::RemoveChild(child
);
3384 m_dirtyTabOrder
= true;
3385 wxTheApp
->WakeUpIdle();
3389 wxLayoutDirection
wxWindowGTK::GTKGetLayout(GtkWidget
*widget
)
3391 return gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
3392 ? wxLayout_RightToLeft
3393 : wxLayout_LeftToRight
;
3397 void wxWindowGTK::GTKSetLayout(GtkWidget
*widget
, wxLayoutDirection dir
)
3399 wxASSERT_MSG( dir
!= wxLayout_Default
, wxT("invalid layout direction") );
3401 gtk_widget_set_direction(widget
,
3402 dir
== wxLayout_RightToLeft
? GTK_TEXT_DIR_RTL
3403 : GTK_TEXT_DIR_LTR
);
3406 wxLayoutDirection
wxWindowGTK::GetLayoutDirection() const
3408 return GTKGetLayout(m_widget
);
3411 void wxWindowGTK::SetLayoutDirection(wxLayoutDirection dir
)
3413 if ( dir
== wxLayout_Default
)
3415 const wxWindow
*const parent
= GetParent();
3418 // inherit layout from parent.
3419 dir
= parent
->GetLayoutDirection();
3421 else // no parent, use global default layout
3423 dir
= wxTheApp
->GetLayoutDirection();
3427 if ( dir
== wxLayout_Default
)
3430 GTKSetLayout(m_widget
, dir
);
3432 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
3433 GTKSetLayout(m_wxwindow
, dir
);
3437 wxWindowGTK::AdjustForLayoutDirection(wxCoord x
,
3438 wxCoord
WXUNUSED(width
),
3439 wxCoord
WXUNUSED(widthTotal
)) const
3441 // We now mirror the coordinates of RTL windows in wxPizza
3445 void wxWindowGTK::DoMoveInTabOrder(wxWindow
*win
, WindowOrder move
)
3447 wxWindowBase::DoMoveInTabOrder(win
, move
);
3448 m_dirtyTabOrder
= true;
3449 wxTheApp
->WakeUpIdle();
3452 bool wxWindowGTK::DoNavigateIn(int flags
)
3454 if ( flags
& wxNavigationKeyEvent::WinChange
)
3456 wxFAIL_MSG( wxT("not implemented") );
3460 else // navigate inside the container
3462 wxWindow
*parent
= wxGetTopLevelParent((wxWindow
*)this);
3463 wxCHECK_MSG( parent
, false, wxT("every window must have a TLW parent") );
3465 GtkDirectionType dir
;
3466 dir
= flags
& wxNavigationKeyEvent::IsForward
? GTK_DIR_TAB_FORWARD
3467 : GTK_DIR_TAB_BACKWARD
;
3470 g_signal_emit_by_name(parent
->m_widget
, "focus", dir
, &rc
);
3476 bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3478 // none needed by default
3482 void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget
* WXUNUSED(w
))
3484 // nothing to do by default since none is needed
3487 void wxWindowGTK::RealizeTabOrder()
3491 if ( !m_children
.empty() )
3493 // we don't only construct the correct focus chain but also use
3494 // this opportunity to update the mnemonic widgets for the widgets
3497 GList
*chain
= NULL
;
3498 wxWindowGTK
* mnemonicWindow
= NULL
;
3500 for ( wxWindowList::const_iterator i
= m_children
.begin();
3501 i
!= m_children
.end();
3504 wxWindowGTK
*win
= *i
;
3506 bool focusableFromKeyboard
= win
->AcceptsFocusFromKeyboard();
3508 if ( mnemonicWindow
)
3510 if ( focusableFromKeyboard
)
3512 // wxComboBox et al. needs to focus on on a different
3513 // widget than m_widget, so if the main widget isn't
3514 // focusable try the connect widget
3515 GtkWidget
* w
= win
->m_widget
;
3516 if ( !gtk_widget_get_can_focus(w
) )
3518 w
= win
->GetConnectWidget();
3519 if ( !gtk_widget_get_can_focus(w
) )
3525 mnemonicWindow
->GTKWidgetDoSetMnemonic(w
);
3526 mnemonicWindow
= NULL
;
3530 else if ( win
->GTKWidgetNeedsMnemonic() )
3532 mnemonicWindow
= win
;
3535 if ( focusableFromKeyboard
)
3536 chain
= g_list_prepend(chain
, win
->m_widget
);
3539 chain
= g_list_reverse(chain
);
3541 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow
), chain
);
3546 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow
));
3551 void wxWindowGTK::Raise()
3553 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3555 if (m_wxwindow
&& gtk_widget_get_window(m_wxwindow
))
3557 gdk_window_raise(gtk_widget_get_window(m_wxwindow
));
3559 else if (gtk_widget_get_window(m_widget
))
3561 gdk_window_raise(gtk_widget_get_window(m_widget
));
3565 void wxWindowGTK::Lower()
3567 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3569 if (m_wxwindow
&& gtk_widget_get_window(m_wxwindow
))
3571 gdk_window_lower(gtk_widget_get_window(m_wxwindow
));
3573 else if (gtk_widget_get_window(m_widget
))
3575 gdk_window_lower(gtk_widget_get_window(m_widget
));
3579 bool wxWindowGTK::SetCursor( const wxCursor
&cursor
)
3581 if ( !wxWindowBase::SetCursor(cursor
.IsOk() ? cursor
: *wxSTANDARD_CURSOR
) )
3589 void wxWindowGTK::GTKUpdateCursor(bool update_self
/*=true*/, bool recurse
/*=true*/)
3593 wxCursor
cursor(g_globalCursor
.IsOk() ? g_globalCursor
: GetCursor());
3594 if ( cursor
.IsOk() )
3596 wxArrayGdkWindows windowsThis
;
3597 GdkWindow
* window
= GTKGetWindow(windowsThis
);
3599 gdk_window_set_cursor( window
, cursor
.GetCursor() );
3602 const size_t count
= windowsThis
.size();
3603 for ( size_t n
= 0; n
< count
; n
++ )
3605 GdkWindow
*win
= windowsThis
[n
];
3606 // It can be zero if the window has not been realized yet.
3609 gdk_window_set_cursor(win
, cursor
.GetCursor());
3618 for (wxWindowList::iterator it
= GetChildren().begin(); it
!= GetChildren().end(); ++it
)
3620 (*it
)->GTKUpdateCursor( true );
3625 void wxWindowGTK::WarpPointer( int x
, int y
)
3627 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3629 ClientToScreen(&x
, &y
);
3630 GdkDisplay
* display
= gtk_widget_get_display(m_widget
);
3631 GdkScreen
* screen
= gtk_widget_get_screen(m_widget
);
3633 GdkDeviceManager
* manager
= gdk_display_get_device_manager(display
);
3634 gdk_device_warp(gdk_device_manager_get_client_pointer(manager
), screen
, x
, y
);
3636 #ifdef GDK_WINDOWING_X11
3637 XWarpPointer(GDK_DISPLAY_XDISPLAY(display
),
3639 GDK_WINDOW_XID(gdk_screen_get_root_window(screen
)),
3645 wxWindowGTK::ScrollDir
wxWindowGTK::ScrollDirFromRange(GtkRange
*range
) const
3647 // find the scrollbar which generated the event
3648 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
3650 if ( range
== m_scrollBar
[dir
] )
3651 return (ScrollDir
)dir
;
3654 wxFAIL_MSG( wxT("event from unknown scrollbar received") );
3656 return ScrollDir_Max
;
3659 bool wxWindowGTK::DoScrollByUnits(ScrollDir dir
, ScrollUnit unit
, int units
)
3661 bool changed
= false;
3662 GtkRange
* range
= m_scrollBar
[dir
];
3663 if ( range
&& units
)
3665 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
3666 double inc
= unit
== ScrollUnit_Line
? gtk_adjustment_get_step_increment(adj
)
3667 : gtk_adjustment_get_page_increment(adj
);
3669 const int posOld
= wxRound(gtk_adjustment_get_value(adj
));
3670 gtk_range_set_value(range
, posOld
+ units
*inc
);
3672 changed
= wxRound(gtk_adjustment_get_value(adj
)) != posOld
;
3678 bool wxWindowGTK::ScrollLines(int lines
)
3680 return DoScrollByUnits(ScrollDir_Vert
, ScrollUnit_Line
, lines
);
3683 bool wxWindowGTK::ScrollPages(int pages
)
3685 return DoScrollByUnits(ScrollDir_Vert
, ScrollUnit_Page
, pages
);
3688 void wxWindowGTK::Refresh(bool WXUNUSED(eraseBackground
),
3693 if (gtk_widget_get_mapped(m_wxwindow
))
3695 GdkWindow
* window
= gtk_widget_get_window(m_wxwindow
);
3698 GdkRectangle r
= { rect
->x
, rect
->y
, rect
->width
, rect
->height
};
3699 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3700 r
.x
= gdk_window_get_width(window
) - r
.x
- rect
->width
;
3701 gdk_window_invalidate_rect(window
, &r
, true);
3704 gdk_window_invalidate_rect(window
, NULL
, true);
3709 if (gtk_widget_get_mapped(m_widget
))
3712 gtk_widget_queue_draw_area(m_widget
, rect
->x
, rect
->y
, rect
->width
, rect
->height
);
3714 gtk_widget_queue_draw(m_widget
);
3719 void wxWindowGTK::Update()
3721 if (m_widget
&& gtk_widget_get_mapped(m_widget
))
3723 GdkDisplay
* display
= gtk_widget_get_display(m_widget
);
3724 // Flush everything out to the server, and wait for it to finish.
3725 // This ensures nothing will overwrite the drawing we are about to do.
3726 gdk_display_sync(display
);
3728 GdkWindow
* window
= GTKGetDrawingWindow();
3730 window
= gtk_widget_get_window(m_widget
);
3731 gdk_window_process_updates(window
, true);
3733 // Flush again, but no need to wait for it to finish
3734 gdk_display_flush(display
);
3738 bool wxWindowGTK::DoIsExposed( int x
, int y
) const
3740 return m_updateRegion
.Contains(x
, y
) != wxOutRegion
;
3743 bool wxWindowGTK::DoIsExposed( int x
, int y
, int w
, int h
) const
3745 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3746 return m_updateRegion
.Contains(x
-w
, y
, w
, h
) != wxOutRegion
;
3748 return m_updateRegion
.Contains(x
, y
, w
, h
) != wxOutRegion
;
3752 void wxWindowGTK::GTKSendPaintEvents(cairo_t
* cr
)
3754 void wxWindowGTK::GTKSendPaintEvents(const GdkRegion
* region
)
3758 m_paintContext
= cr
;
3759 double x1
, y1
, x2
, y2
;
3760 cairo_clip_extents(cr
, &x1
, &y1
, &x2
, &y2
);
3761 m_updateRegion
= wxRegion(int(x1
), int(y1
), int(x2
- x1
), int(y2
- y1
));
3762 #else // !__WXGTK3__
3763 m_updateRegion
= wxRegion(region
);
3764 #if wxGTK_HAS_COMPOSITING_SUPPORT
3767 #endif // !__WXGTK3__
3768 // Clip to paint region in wxClientDC
3769 m_clipPaintRegion
= true;
3771 m_nativeUpdateRegion
= m_updateRegion
;
3773 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3775 // Transform m_updateRegion under RTL
3776 m_updateRegion
.Clear();
3778 const int width
= gdk_window_get_width(GTKGetDrawingWindow());
3780 wxRegionIterator
upd( m_nativeUpdateRegion
);
3784 rect
.x
= upd
.GetX();
3785 rect
.y
= upd
.GetY();
3786 rect
.width
= upd
.GetWidth();
3787 rect
.height
= upd
.GetHeight();
3789 rect
.x
= width
- rect
.x
- rect
.width
;
3790 m_updateRegion
.Union( rect
);
3796 switch ( GetBackgroundStyle() )
3798 case wxBG_STYLE_TRANSPARENT
:
3799 #if wxGTK_HAS_COMPOSITING_SUPPORT
3800 if (IsTransparentBackgroundSupported())
3802 // Set a transparent background, so that overlaying in parent
3803 // might indeed let see through where this child did not
3804 // explicitly paint.
3805 // NB: it works also for top level windows (but this is the
3806 // windows manager which then does the compositing job)
3808 cr
= gdk_cairo_create(m_wxwindow
->window
);
3809 gdk_cairo_region(cr
, m_nativeUpdateRegion
.GetRegion());
3812 cairo_set_operator(cr
, CAIRO_OPERATOR_CLEAR
);
3814 cairo_set_operator(cr
, CAIRO_OPERATOR_OVER
);
3816 cairo_surface_flush(cairo_get_target(cr
));
3819 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
3822 case wxBG_STYLE_ERASE
:
3825 wxGTKCairoDC
dc(cr
);
3827 wxWindowDC
dc( (wxWindow
*)this );
3828 dc
.SetDeviceClippingRegion( m_updateRegion
);
3830 // Work around gtk-qt <= 0.60 bug whereby the window colour
3834 GetOptionInt("gtk.window.force-background-colour") )
3836 dc
.SetBackground(GetBackgroundColour());
3839 #endif // !__WXGTK3__
3840 wxEraseEvent
erase_event( GetId(), &dc
);
3841 erase_event
.SetEventObject( this );
3843 if ( HandleWindowEvent(erase_event
) )
3845 // background erased, don't do it again
3851 case wxBG_STYLE_SYSTEM
:
3852 if ( GetThemeEnabled() )
3854 GdkWindow
* gdkWindow
= GTKGetDrawingWindow();
3855 const int w
= gdk_window_get_width(gdkWindow
);
3856 const int h
= gdk_window_get_height(gdkWindow
);
3858 GtkStyleContext
* sc
= gtk_widget_get_style_context(m_wxwindow
);
3859 gtk_render_background(sc
, cr
, 0, 0, w
, h
);
3861 // find ancestor from which to steal background
3862 wxWindow
*parent
= wxGetTopLevelParent((wxWindow
*)this);
3864 parent
= (wxWindow
*)this;
3866 m_nativeUpdateRegion
.GetBox(rect
.x
, rect
.y
, rect
.width
, rect
.height
);
3867 gtk_paint_flat_box(gtk_widget_get_style(parent
->m_widget
),
3869 gtk_widget_get_state(m_wxwindow
),
3875 #endif // !__WXGTK3__
3879 case wxBG_STYLE_PAINT
:
3880 // nothing to do: window will be painted over in EVT_PAINT
3884 wxFAIL_MSG( "unsupported background style" );
3887 wxNcPaintEvent
nc_paint_event( GetId() );
3888 nc_paint_event
.SetEventObject( this );
3889 HandleWindowEvent( nc_paint_event
);
3891 wxPaintEvent
paint_event( GetId() );
3892 paint_event
.SetEventObject( this );
3893 HandleWindowEvent( paint_event
);
3895 #if wxGTK_HAS_COMPOSITING_SUPPORT
3896 if (IsTransparentBackgroundSupported())
3897 { // now composite children which need it
3898 // Overlay all our composite children on top of the painted area
3899 wxWindowList::compatibility_iterator node
;
3900 for ( node
= m_children
.GetFirst(); node
; node
= node
->GetNext() )
3902 wxWindow
*compositeChild
= node
->GetData();
3903 if (compositeChild
->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT
)
3908 cr
= gdk_cairo_create(m_wxwindow
->window
);
3909 gdk_cairo_region(cr
, m_nativeUpdateRegion
.GetRegion());
3912 #endif // !__WXGTK3__
3913 GtkWidget
*child
= compositeChild
->m_wxwindow
;
3914 GtkAllocation alloc
;
3915 gtk_widget_get_allocation(child
, &alloc
);
3917 // The source data is the (composited) child
3918 gdk_cairo_set_source_window(
3919 cr
, gtk_widget_get_window(child
), alloc
.x
, alloc
.y
);
3929 #endif // wxGTK_HAS_COMPOSITING_SUPPORT
3931 m_clipPaintRegion
= false;
3933 m_paintContext
= NULL
;
3935 m_updateRegion
.Clear();
3936 m_nativeUpdateRegion
.Clear();
3939 void wxWindowGTK::SetDoubleBuffered( bool on
)
3941 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3944 gtk_widget_set_double_buffered( m_wxwindow
, on
);
3947 bool wxWindowGTK::IsDoubleBuffered() const
3949 return gtk_widget_get_double_buffered( m_wxwindow
) != 0;
3952 void wxWindowGTK::ClearBackground()
3954 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
3958 void wxWindowGTK::DoSetToolTip( wxToolTip
*tip
)
3960 if (m_tooltip
!= tip
)
3962 wxWindowBase::DoSetToolTip(tip
);
3965 m_tooltip
->GTKSetWindow(static_cast<wxWindow
*>(this));
3967 GTKApplyToolTip(NULL
);
3971 void wxWindowGTK::GTKApplyToolTip(const char* tip
)
3973 wxToolTip::GTKApply(GetConnectWidget(), tip
);
3975 #endif // wxUSE_TOOLTIPS
3977 bool wxWindowGTK::SetBackgroundColour( const wxColour
&colour
)
3979 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
3981 if (!wxWindowBase::SetBackgroundColour(colour
))
3987 // We need the pixel value e.g. for background clearing.
3988 m_backgroundColour
.CalcPixel(gtk_widget_get_colormap(m_widget
));
3992 // apply style change (forceStyle=true so that new style is applied
3993 // even if the bg colour changed from valid to wxNullColour)
3994 GTKApplyWidgetStyle(true);
3999 bool wxWindowGTK::SetForegroundColour( const wxColour
&colour
)
4001 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
4003 if (!wxWindowBase::SetForegroundColour(colour
))
4011 // We need the pixel value e.g. for background clearing.
4012 m_foregroundColour
.CalcPixel(gtk_widget_get_colormap(m_widget
));
4016 // apply style change (forceStyle=true so that new style is applied
4017 // even if the bg colour changed from valid to wxNullColour):
4018 GTKApplyWidgetStyle(true);
4023 PangoContext
*wxWindowGTK::GTKGetPangoDefaultContext()
4025 return gtk_widget_get_pango_context( m_widget
);
4029 GtkRcStyle
*wxWindowGTK::GTKCreateWidgetStyle(bool forceStyle
)
4031 // do we need to apply any changes at all?
4034 !m_foregroundColour
.IsOk() && !m_backgroundColour
.IsOk() )
4039 GtkRcStyle
*style
= gtk_rc_style_new();
4041 if ( m_font
.IsOk() )
4044 pango_font_description_copy( m_font
.GetNativeFontInfo()->description
);
4047 int flagsNormal
= 0,
4050 flagsInsensitive
= 0;
4052 if ( m_foregroundColour
.IsOk() )
4054 const GdkColor
*fg
= m_foregroundColour
.GetColor();
4056 style
->fg
[GTK_STATE_NORMAL
] =
4057 style
->text
[GTK_STATE_NORMAL
] = *fg
;
4058 flagsNormal
|= GTK_RC_FG
| GTK_RC_TEXT
;
4060 style
->fg
[GTK_STATE_PRELIGHT
] =
4061 style
->text
[GTK_STATE_PRELIGHT
] = *fg
;
4062 flagsPrelight
|= GTK_RC_FG
| GTK_RC_TEXT
;
4064 style
->fg
[GTK_STATE_ACTIVE
] =
4065 style
->text
[GTK_STATE_ACTIVE
] = *fg
;
4066 flagsActive
|= GTK_RC_FG
| GTK_RC_TEXT
;
4069 if ( m_backgroundColour
.IsOk() )
4071 const GdkColor
*bg
= m_backgroundColour
.GetColor();
4073 style
->bg
[GTK_STATE_NORMAL
] =
4074 style
->base
[GTK_STATE_NORMAL
] = *bg
;
4075 flagsNormal
|= GTK_RC_BG
| GTK_RC_BASE
;
4077 style
->bg
[GTK_STATE_PRELIGHT
] =
4078 style
->base
[GTK_STATE_PRELIGHT
] = *bg
;
4079 flagsPrelight
|= GTK_RC_BG
| GTK_RC_BASE
;
4081 style
->bg
[GTK_STATE_ACTIVE
] =
4082 style
->base
[GTK_STATE_ACTIVE
] = *bg
;
4083 flagsActive
|= GTK_RC_BG
| GTK_RC_BASE
;
4085 style
->bg
[GTK_STATE_INSENSITIVE
] =
4086 style
->base
[GTK_STATE_INSENSITIVE
] = *bg
;
4087 flagsInsensitive
|= GTK_RC_BG
| GTK_RC_BASE
;
4090 style
->color_flags
[GTK_STATE_NORMAL
] = (GtkRcFlags
)flagsNormal
;
4091 style
->color_flags
[GTK_STATE_PRELIGHT
] = (GtkRcFlags
)flagsPrelight
;
4092 style
->color_flags
[GTK_STATE_ACTIVE
] = (GtkRcFlags
)flagsActive
;
4093 style
->color_flags
[GTK_STATE_INSENSITIVE
] = (GtkRcFlags
)flagsInsensitive
;
4097 #endif // !__WXGTK3__
4099 void wxWindowGTK::GTKApplyWidgetStyle(bool WXUNUSED_IN_GTK3(forceStyle
))
4102 DoApplyWidgetStyle(NULL
);
4104 GtkRcStyle
*style
= GTKCreateWidgetStyle(forceStyle
);
4107 DoApplyWidgetStyle(style
);
4108 g_object_unref(style
);
4112 // Style change may affect GTK+'s size calculation:
4113 InvalidateBestSize();
4116 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle
*style
)
4118 GtkWidget
* widget
= m_wxwindow
? m_wxwindow
: m_widget
;
4120 // block the signal temporarily to avoid sending
4121 // wxSysColourChangedEvents when we change the colours ourselves
4122 bool unblock
= false;
4123 if (m_wxwindow
&& IsTopLevel())
4126 g_signal_handlers_block_by_func(
4127 m_wxwindow
, (void*)style_updated
, this);
4130 GTKApplyStyle(widget
, style
);
4134 g_signal_handlers_unblock_by_func(
4135 m_wxwindow
, (void*)style_updated
, this);
4139 void wxWindowGTK::GTKApplyStyle(GtkWidget
* widget
, GtkRcStyle
* WXUNUSED_IN_GTK3(style
))
4142 const PangoFontDescription
* pfd
= NULL
;
4144 pfd
= pango_font_description_copy(m_font
.GetNativeFontInfo()->description
);
4145 gtk_widget_override_font(widget
, pfd
);
4146 gtk_widget_override_color(widget
, GTK_STATE_FLAG_NORMAL
, m_foregroundColour
);
4147 gtk_widget_override_background_color(widget
, GTK_STATE_FLAG_NORMAL
, m_backgroundColour
);
4149 gtk_widget_modify_style(widget
, style
);
4153 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style
)
4155 if (!wxWindowBase::SetBackgroundStyle(style
))
4162 window
= GTKGetDrawingWindow();
4166 GtkWidget
* const w
= GetConnectWidget();
4167 window
= w
? gtk_widget_get_window(w
) : NULL
;
4170 bool wantNoBackPixmap
= style
== wxBG_STYLE_PAINT
|| style
== wxBG_STYLE_TRANSPARENT
;
4172 if ( wantNoBackPixmap
)
4176 // Make sure GDK/X11 doesn't refresh the window
4178 gdk_window_set_back_pixmap( window
, NULL
, FALSE
);
4179 m_needsStyleChange
= false;
4181 else // window not realized yet
4183 // Do when window is realized
4184 m_needsStyleChange
= true;
4187 // Don't apply widget style, or we get a grey background
4191 // apply style change (forceStyle=true so that new style is applied
4192 // even if the bg colour changed from valid to wxNullColour):
4193 GTKApplyWidgetStyle(true);
4195 #endif // !__WXGTK3__
4200 bool wxWindowGTK::IsTransparentBackgroundSupported(wxString
* reason
) const
4202 #if wxGTK_HAS_COMPOSITING_SUPPORT
4204 if (gtk_check_version(wxGTK_VERSION_REQUIRED_FOR_COMPOSITING
) != NULL
)
4208 *reason
= _("GTK+ installed on this machine is too old to "
4209 "support screen compositing, please install "
4210 "GTK+ 2.12 or later.");
4215 #endif // !__WXGTK3__
4217 // NB: We don't check here if the particular kind of widget supports
4218 // transparency, we check only if it would be possible for a generic window
4220 wxCHECK_MSG ( m_widget
, false, "Window must be created first" );
4222 if (!gdk_screen_is_composited(gtk_widget_get_screen(m_widget
)))
4226 *reason
= _("Compositing not supported by this system, "
4227 "please enable it in your Window Manager.");
4237 *reason
= _("This program was compiled with a too old version of GTK+, "
4238 "please rebuild with GTK+ 2.12 or newer.");
4242 #endif // wxGTK_HAS_COMPOSITING_SUPPORT/!wxGTK_HAS_COMPOSITING_SUPPORT
4245 // ----------------------------------------------------------------------------
4246 // Pop-up menu stuff
4247 // ----------------------------------------------------------------------------
4249 #if wxUSE_MENUS_NATIVE
4253 void wxPopupMenuPositionCallback( GtkMenu
*menu
,
4255 gboolean
* WXUNUSED(whatever
),
4256 gpointer user_data
)
4258 // ensure that the menu appears entirely on screen
4260 gtk_widget_get_child_requisition(GTK_WIDGET(menu
), &req
);
4262 wxSize sizeScreen
= wxGetDisplaySize();
4263 wxPoint
*pos
= (wxPoint
*)user_data
;
4265 gint xmax
= sizeScreen
.x
- req
.width
,
4266 ymax
= sizeScreen
.y
- req
.height
;
4268 *x
= pos
->x
< xmax
? pos
->x
: xmax
;
4269 *y
= pos
->y
< ymax
? pos
->y
: ymax
;
4273 bool wxWindowGTK::DoPopupMenu( wxMenu
*menu
, int x
, int y
)
4275 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
4281 GtkMenuPositionFunc posfunc
;
4282 if ( x
== -1 && y
== -1 )
4284 // use GTK's default positioning algorithm
4290 pos
= ClientToScreen(wxPoint(x
, y
));
4292 posfunc
= wxPopupMenuPositionCallback
;
4295 menu
->m_popupShown
= true;
4297 GTK_MENU(menu
->m_menu
),
4298 NULL
, // parent menu shell
4299 NULL
, // parent menu item
4300 posfunc
, // function to position it
4301 userdata
, // client data
4302 0, // button used to activate it
4303 gtk_get_current_event_time()
4306 while (menu
->m_popupShown
)
4308 gtk_main_iteration();
4314 #endif // wxUSE_MENUS_NATIVE
4316 #if wxUSE_DRAG_AND_DROP
4318 void wxWindowGTK::SetDropTarget( wxDropTarget
*dropTarget
)
4320 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4322 GtkWidget
*dnd_widget
= GetConnectWidget();
4324 if (m_dropTarget
) m_dropTarget
->GtkUnregisterWidget( dnd_widget
);
4326 if (m_dropTarget
) delete m_dropTarget
;
4327 m_dropTarget
= dropTarget
;
4329 if (m_dropTarget
) m_dropTarget
->GtkRegisterWidget( dnd_widget
);
4332 #endif // wxUSE_DRAG_AND_DROP
4334 GtkWidget
* wxWindowGTK::GetConnectWidget()
4336 GtkWidget
*connect_widget
= m_widget
;
4337 if (m_wxwindow
) connect_widget
= m_wxwindow
;
4339 return connect_widget
;
4342 bool wxWindowGTK::GTKIsOwnWindow(GdkWindow
*window
) const
4344 wxArrayGdkWindows windowsThis
;
4345 GdkWindow
* const winThis
= GTKGetWindow(windowsThis
);
4347 return winThis
? window
== winThis
4348 : windowsThis
.Index(window
) != wxNOT_FOUND
;
4351 GdkWindow
*wxWindowGTK::GTKGetWindow(wxArrayGdkWindows
& WXUNUSED(windows
)) const
4353 return m_wxwindow
? GTKGetDrawingWindow() : gtk_widget_get_window(m_widget
);
4356 bool wxWindowGTK::SetFont( const wxFont
&font
)
4358 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
4360 if (!wxWindowBase::SetFont(font
))
4363 // apply style change (forceStyle=true so that new style is applied
4364 // even if the font changed from valid to wxNullFont):
4365 GTKApplyWidgetStyle(true);
4370 void wxWindowGTK::DoCaptureMouse()
4372 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4374 GdkWindow
*window
= NULL
;
4376 window
= GTKGetDrawingWindow();
4378 window
= gtk_widget_get_window(GetConnectWidget());
4380 wxCHECK_RET( window
, wxT("CaptureMouse() failed") );
4382 const wxCursor
* cursor
= &m_cursor
;
4383 if (!cursor
->IsOk())
4384 cursor
= wxSTANDARD_CURSOR
;
4386 gdk_pointer_grab( window
, FALSE
,
4388 (GDK_BUTTON_PRESS_MASK
|
4389 GDK_BUTTON_RELEASE_MASK
|
4390 GDK_POINTER_MOTION_HINT_MASK
|
4391 GDK_POINTER_MOTION_MASK
),
4393 cursor
->GetCursor(),
4394 (guint32
)GDK_CURRENT_TIME
);
4395 g_captureWindow
= this;
4396 g_captureWindowHasMouse
= true;
4399 void wxWindowGTK::DoReleaseMouse()
4401 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4403 wxCHECK_RET( g_captureWindow
, wxT("can't release mouse - not captured") );
4405 g_captureWindow
= NULL
;
4407 GdkWindow
*window
= NULL
;
4409 window
= GTKGetDrawingWindow();
4411 window
= gtk_widget_get_window(GetConnectWidget());
4416 gdk_pointer_ungrab ( (guint32
)GDK_CURRENT_TIME
);
4419 void wxWindowGTK::GTKReleaseMouseAndNotify()
4422 wxMouseCaptureLostEvent
evt(GetId());
4423 evt
.SetEventObject( this );
4424 HandleWindowEvent( evt
);
4428 wxWindow
*wxWindowBase::GetCapture()
4430 return (wxWindow
*)g_captureWindow
;
4433 bool wxWindowGTK::IsRetained() const
4438 void wxWindowGTK::SetScrollbar(int orient
,
4442 bool WXUNUSED(update
))
4444 const int dir
= ScrollDirFromOrient(orient
);
4445 GtkRange
* const sb
= m_scrollBar
[dir
];
4446 wxCHECK_RET( sb
, wxT("this window is not scrollable") );
4450 // GtkRange requires upper > lower
4455 g_signal_handlers_block_by_func(
4456 sb
, (void*)gtk_scrollbar_value_changed
, this);
4458 gtk_range_set_increments(sb
, 1, thumbVisible
);
4459 gtk_adjustment_set_page_size(gtk_range_get_adjustment(sb
), thumbVisible
);
4460 gtk_range_set_range(sb
, 0, range
);
4461 gtk_range_set_value(sb
, pos
);
4462 m_scrollPos
[dir
] = gtk_range_get_value(sb
);
4464 g_signal_handlers_unblock_by_func(
4465 sb
, (void*)gtk_scrollbar_value_changed
, this);
4468 void wxWindowGTK::SetScrollPos(int orient
, int pos
, bool WXUNUSED(refresh
))
4470 const int dir
= ScrollDirFromOrient(orient
);
4471 GtkRange
* const sb
= m_scrollBar
[dir
];
4472 wxCHECK_RET( sb
, wxT("this window is not scrollable") );
4474 // This check is more than an optimization. Without it, the slider
4475 // will not move smoothly while tracking when using wxScrollHelper.
4476 if (GetScrollPos(orient
) != pos
)
4478 g_signal_handlers_block_by_func(
4479 sb
, (void*)gtk_scrollbar_value_changed
, this);
4481 gtk_range_set_value(sb
, pos
);
4482 m_scrollPos
[dir
] = gtk_range_get_value(sb
);
4484 g_signal_handlers_unblock_by_func(
4485 sb
, (void*)gtk_scrollbar_value_changed
, this);
4489 int wxWindowGTK::GetScrollThumb(int orient
) const
4491 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4492 wxCHECK_MSG( sb
, 0, wxT("this window is not scrollable") );
4494 return wxRound(gtk_adjustment_get_page_size(gtk_range_get_adjustment(sb
)));
4497 int wxWindowGTK::GetScrollPos( int orient
) const
4499 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4500 wxCHECK_MSG( sb
, 0, wxT("this window is not scrollable") );
4502 return wxRound(gtk_range_get_value(sb
));
4505 int wxWindowGTK::GetScrollRange( int orient
) const
4507 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4508 wxCHECK_MSG( sb
, 0, wxT("this window is not scrollable") );
4510 return wxRound(gtk_adjustment_get_upper(gtk_range_get_adjustment(sb
)));
4513 // Determine if increment is the same as +/-x, allowing for some small
4514 // difference due to possible inexactness in floating point arithmetic
4515 static inline bool IsScrollIncrement(double increment
, double x
)
4517 wxASSERT(increment
> 0);
4518 const double tolerance
= 1.0 / 1024;
4519 return fabs(increment
- fabs(x
)) < tolerance
;
4522 wxEventType
wxWindowGTK::GTKGetScrollEventType(GtkRange
* range
)
4524 wxASSERT(range
== m_scrollBar
[0] || range
== m_scrollBar
[1]);
4526 const int barIndex
= range
== m_scrollBar
[1];
4528 const double value
= gtk_range_get_value(range
);
4530 // save previous position
4531 const double oldPos
= m_scrollPos
[barIndex
];
4532 // update current position
4533 m_scrollPos
[barIndex
] = value
;
4534 // If event should be ignored, or integral position has not changed
4535 if (!m_hasVMT
|| g_blockEventsOnDrag
|| wxRound(value
) == wxRound(oldPos
))
4540 wxEventType eventType
= wxEVT_SCROLL_THUMBTRACK
;
4543 // Difference from last change event
4544 const double diff
= value
- oldPos
;
4545 const bool isDown
= diff
> 0;
4547 GtkAdjustment
* adj
= gtk_range_get_adjustment(range
);
4548 if (IsScrollIncrement(gtk_adjustment_get_step_increment(adj
), diff
))
4550 eventType
= isDown
? wxEVT_SCROLL_LINEDOWN
: wxEVT_SCROLL_LINEUP
;
4552 else if (IsScrollIncrement(gtk_adjustment_get_page_increment(adj
), diff
))
4554 eventType
= isDown
? wxEVT_SCROLL_PAGEDOWN
: wxEVT_SCROLL_PAGEUP
;
4556 else if (m_mouseButtonDown
)
4558 // Assume track event
4559 m_isScrolling
= true;
4565 void wxWindowGTK::ScrollWindow( int dx
, int dy
, const wxRect
* WXUNUSED(rect
) )
4567 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4569 wxCHECK_RET( m_wxwindow
!= NULL
, wxT("window needs client area for scrolling") );
4571 // No scrolling requested.
4572 if ((dx
== 0) && (dy
== 0)) return;
4574 m_clipPaintRegion
= true;
4576 WX_PIZZA(m_wxwindow
)->scroll(dx
, dy
);
4578 m_clipPaintRegion
= false;
4581 bool restoreCaret
= (GetCaret() != NULL
&& GetCaret()->IsVisible());
4584 wxRect
caretRect(GetCaret()->GetPosition(), GetCaret()->GetSize());
4586 caretRect
.width
+= dx
;
4589 caretRect
.x
+= dx
; caretRect
.width
-= dx
;
4592 caretRect
.height
+= dy
;
4595 caretRect
.y
+= dy
; caretRect
.height
-= dy
;
4598 RefreshRect(caretRect
);
4600 #endif // wxUSE_CARET
4603 void wxWindowGTK::GTKScrolledWindowSetBorder(GtkWidget
* w
, int wxstyle
)
4605 //RN: Note that static controls usually have no border on gtk, so maybe
4606 //it makes sense to treat that as simply no border at the wx level
4608 if (!(wxstyle
& wxNO_BORDER
) && !(wxstyle
& wxBORDER_STATIC
))
4610 GtkShadowType gtkstyle
;
4612 if(wxstyle
& wxBORDER_RAISED
)
4613 gtkstyle
= GTK_SHADOW_OUT
;
4614 else if ((wxstyle
& wxBORDER_SUNKEN
) || (wxstyle
& wxBORDER_THEME
))
4615 gtkstyle
= GTK_SHADOW_IN
;
4618 else if (wxstyle
& wxBORDER_DOUBLE
)
4619 gtkstyle
= GTK_SHADOW_ETCHED_IN
;
4622 gtkstyle
= GTK_SHADOW_IN
;
4624 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w
),
4629 // Find the wxWindow at the current mouse position, also returning the mouse
4631 wxWindow
* wxFindWindowAtPointer(wxPoint
& pt
)
4633 pt
= wxGetMousePosition();
4634 wxWindow
* found
= wxFindWindowAtPoint(pt
);
4638 // Get the current mouse position.
4639 wxPoint
wxGetMousePosition()
4642 GdkModifierType unused
;
4643 GetMouseState(x
, y
, unused
);
4644 return wxPoint(x
, y
);
4647 GdkWindow
* wxWindowGTK::GTKGetDrawingWindow() const
4649 GdkWindow
* window
= NULL
;
4651 window
= gtk_widget_get_window(m_wxwindow
);
4655 // ----------------------------------------------------------------------------
4657 // ----------------------------------------------------------------------------
4662 // this is called if we attempted to freeze unrealized widget when it finally
4663 // is realized (and so can be frozen):
4664 static void wx_frozen_widget_realize(GtkWidget
* w
, wxWindowGTK
* win
)
4666 wxASSERT( w
&& gtk_widget_get_has_window(w
) );
4667 wxASSERT( gtk_widget_get_realized(w
) );
4669 g_signal_handlers_disconnect_by_func
4672 (void*)wx_frozen_widget_realize
,
4677 if (w
== win
->m_wxwindow
)
4678 window
= win
->GTKGetDrawingWindow();
4680 window
= gtk_widget_get_window(w
);
4681 gdk_window_freeze_updates(window
);
4686 void wxWindowGTK::GTKFreezeWidget(GtkWidget
*w
)
4688 if ( !w
|| !gtk_widget_get_has_window(w
) )
4689 return; // window-less widget, cannot be frozen
4691 GdkWindow
* window
= gtk_widget_get_window(w
);
4694 // we can't thaw unrealized widgets because they don't have GdkWindow,
4695 // so set it up to be done immediately after realization:
4696 g_signal_connect_after
4700 G_CALLBACK(wx_frozen_widget_realize
),
4706 if (w
== m_wxwindow
)
4707 window
= GTKGetDrawingWindow();
4708 gdk_window_freeze_updates(window
);
4711 void wxWindowGTK::GTKThawWidget(GtkWidget
*w
)
4713 if ( !w
|| !gtk_widget_get_has_window(w
) )
4714 return; // window-less widget, cannot be frozen
4716 GdkWindow
* window
= gtk_widget_get_window(w
);
4719 // the widget wasn't realized yet, no need to thaw
4720 g_signal_handlers_disconnect_by_func
4723 (void*)wx_frozen_widget_realize
,
4729 if (w
== m_wxwindow
)
4730 window
= GTKGetDrawingWindow();
4731 gdk_window_thaw_updates(window
);
4734 void wxWindowGTK::DoFreeze()
4736 GTKFreezeWidget(m_widget
);
4737 if ( m_wxwindow
&& m_widget
!= m_wxwindow
)
4738 GTKFreezeWidget(m_wxwindow
);
4741 void wxWindowGTK::DoThaw()
4743 GTKThawWidget(m_widget
);
4744 if ( m_wxwindow
&& m_widget
!= m_wxwindow
)
4745 GTKThawWidget(m_wxwindow
);