1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/window.cpp
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"
23 #include "wx/dcclient.h"
25 #include "wx/settings.h"
26 #include "wx/msgdlg.h"
27 #include "wx/textctrl.h"
28 #include "wx/radiobut.h"
29 #include "wx/toolbar.h"
30 #include "wx/combobox.h"
31 #include "wx/layout.h"
36 #include "wx/tooltip.h"
38 #include "wx/fontutil.h"
41 #include "wx/thread.h"
46 // FIXME: Due to a hack we use GtkCombo in here, which is deprecated since gtk2.3.0
47 #include <gtk/gtkversion.h>
48 #if defined(GTK_DISABLE_DEPRECATED) && GTK_CHECK_VERSION(2,3,0)
49 #undef GTK_DISABLE_DEPRECATED
50 #include <gtk/gtkcombo.h>
51 #define GTK_DISABLE_DEPRECATED
54 #include "wx/gtk/private.h"
55 #include "wx/gtk/win_gtk.h"
56 #include <gdk/gdkkeysyms.h>
59 //-----------------------------------------------------------------------------
60 // documentation on internals
61 //-----------------------------------------------------------------------------
64 I have been asked several times about writing some documentation about
65 the GTK port of wxWidgets, especially its internal structures. Obviously,
66 you cannot understand wxGTK without knowing a little about the GTK, but
67 some more information about what the wxWindow, which is the base class
68 for all other window classes, does seems required as well.
72 What does wxWindow do? It contains the common interface for the following
73 jobs of its descendants:
75 1) Define the rudimentary behaviour common to all window classes, such as
76 resizing, intercepting user input (so as to make it possible to use these
77 events for special purposes in a derived class), window names etc.
79 2) Provide the possibility to contain and manage children, if the derived
80 class is allowed to contain children, which holds true for those window
81 classes which do not display a native GTK widget. To name them, these
82 classes are wxPanel, wxScrolledWindow, wxDialog, wxFrame. The MDI frame-
83 work classes are a special case and are handled a bit differently from
84 the rest. The same holds true for the wxNotebook class.
86 3) Provide the possibility to draw into a client area of a window. This,
87 too, only holds true for classes that do not display a native GTK widget
90 4) Provide the entire mechanism for scrolling widgets. This actual inter-
91 face for this is usually in wxScrolledWindow, but the GTK implementation
94 5) A multitude of helper or extra methods for special purposes, such as
95 Drag'n'Drop, managing validators etc.
97 6) Display a border (sunken, raised, simple or none).
99 Normally one might expect, that one wxWidgets window would always correspond
100 to one GTK widget. Under GTK, there is no such all-round widget that has all
101 the functionality. Moreover, the GTK defines a client area as a different
102 widget from the actual widget you are handling. Last but not least some
103 special classes (e.g. wxFrame) handle different categories of widgets and
104 still have the possibility to draw something in the client area.
105 It was therefore required to write a special purpose GTK widget, that would
106 represent a client area in the sense of wxWidgets capable to do the jobs
107 2), 3) and 4). I have written this class and it resides in win_gtk.c of
110 All windows must have a widget, with which they interact with other under-
111 lying GTK widgets. It is this widget, e.g. that has to be resized etc and
112 the wxWindow class has a member variable called m_widget which holds a
113 pointer to this widget. When the window class represents a GTK native widget,
114 this is (in most cases) the only GTK widget the class manages. E.g. the
115 wxStaticText class handles only a GtkLabel widget a pointer to which you
116 can find in m_widget (defined in wxWindow)
118 When the class has a client area for drawing into and for containing children
119 it has to handle the client area widget (of the type GtkPizza, defined in
120 win_gtk.c), but there could be any number of widgets, handled by a class
121 The common rule for all windows is only, that the widget that interacts with
122 the rest of GTK must be referenced in m_widget and all other widgets must be
123 children of this widget on the GTK level. The top-most widget, which also
124 represents the client area, must be in the m_wxwindow field and must be of
127 As I said, the window classes that display a GTK native widget only have
128 one widget, so in the case of e.g. the wxButton class m_widget holds a
129 pointer to a GtkButton widget. But windows with client areas (for drawing
130 and children) have a m_widget field that is a pointer to a GtkScrolled-
131 Window and a m_wxwindow field that is pointer to a GtkPizza and this
132 one is (in the GTK sense) a child of the GtkScrolledWindow.
134 If the m_wxwindow field is set, then all input to this widget is inter-
135 cepted and sent to the wxWidgets class. If not, all input to the widget
136 that gets pointed to by m_widget gets intercepted and sent to the class.
140 The design of scrolling in wxWidgets is markedly different from that offered
141 by the GTK itself and therefore we cannot simply take it as it is. In GTK,
142 clicking on a scrollbar belonging to scrolled window will inevitably move
143 the window. In wxWidgets, the scrollbar will only emit an event, send this
144 to (normally) a wxScrolledWindow and that class will call ScrollWindow()
145 which actually moves the window and its sub-windows. Note that GtkPizza
146 memorizes how much it has been scrolled but that wxWidgets forgets this
147 so that the two coordinates systems have to be kept in synch. This is done
148 in various places using the pizza->xoffset and pizza->yoffset values.
152 Singularly the most broken code in GTK is the code that is supposed to
153 inform subwindows (child windows) about new positions. Very often, duplicate
154 events are sent without changes in size or position, equally often no
155 events are sent at all (All this is due to a bug in the GtkContainer code
156 which got fixed in GTK 1.2.6). For that reason, wxGTK completely ignores
157 GTK's own system and it simply waits for size events for toplevel windows
158 and then iterates down the respective size events to all window. This has
159 the disadvantage that windows might get size events before the GTK widget
160 actually has the reported size. This doesn't normally pose any problem, but
161 the OpenGL drawing routines rely on correct behaviour. Therefore, I have
162 added the m_nativeSizeEvents flag, which is true only for the OpenGL canvas,
163 i.e. the wxGLCanvas will emit a size event, when (and not before) the X11
164 window that is used for OpenGL output really has that size (as reported by
169 If someone at some point of time feels the immense desire to have a look at,
170 change or attempt to optimise the Refresh() logic, this person will need an
171 intimate understanding of what "draw" and "expose" events are and what
172 they are used for, in particular when used in connection with GTK's
173 own windowless widgets. Beware.
177 Cursors, too, have been a constant source of pleasure. The main difficulty
178 is that a GdkWindow inherits a cursor if the programmer sets a new cursor
179 for the parent. To prevent this from doing too much harm, I use idle time
180 to set the cursor over and over again, starting from the toplevel windows
181 and ending with the youngest generation (speaking of parent and child windows).
182 Also don't forget that cursors (like much else) are connected to GdkWindows,
183 not GtkWidgets and that the "window" field of a GtkWidget might very well
184 point to the GdkWindow of the parent widget (-> "window-less widget") and
185 that the two obviously have very different meanings.
189 //-----------------------------------------------------------------------------
191 //-----------------------------------------------------------------------------
193 // Don't allow event propagation during drag
194 bool g_blockEventsOnDrag
;
195 // Don't allow mouse event propagation during scroll
196 bool g_blockEventsOnScroll
;
197 extern wxCursor g_globalCursor
;
199 // mouse capture state: the window which has it and if the mouse is currently
201 static wxWindowGTK
*g_captureWindow
= (wxWindowGTK
*) NULL
;
202 static bool g_captureWindowHasMouse
= false;
204 wxWindowGTK
*g_focusWindow
= (wxWindowGTK
*) NULL
;
206 // the last window which had the focus - this is normally never NULL (except
207 // if we never had focus at all) as even when g_focusWindow is NULL it still
208 // keeps its previous value
209 wxWindowGTK
*g_focusWindowLast
= (wxWindowGTK
*) NULL
;
211 // If a window get the focus set but has not been realized
212 // yet, defer setting the focus to idle time.
213 wxWindowGTK
*g_delayedFocus
= (wxWindowGTK
*) NULL
;
215 // global variables because GTK+ DnD want to have the
216 // mouse event that caused it
217 GdkEvent
*g_lastMouseEvent
= (GdkEvent
*) NULL
;
218 int g_lastButtonNumber
= 0;
220 extern bool g_mainThreadLocked
;
222 //-----------------------------------------------------------------------------
224 //-----------------------------------------------------------------------------
229 # define DEBUG_MAIN_THREAD if (wxThread::IsMain() && g_mainThreadLocked) printf("gui reentrance");
231 # define DEBUG_MAIN_THREAD
234 #define DEBUG_MAIN_THREAD
237 // the trace mask used for the focus debugging messages
238 #define TRACE_FOCUS _T("focus")
240 //-----------------------------------------------------------------------------
241 // missing gdk functions
242 //-----------------------------------------------------------------------------
245 gdk_window_warp_pointer (GdkWindow
*window
,
250 window
= gdk_get_default_root_window();
252 if (!GDK_WINDOW_DESTROYED(window
))
254 XWarpPointer (GDK_WINDOW_XDISPLAY(window
),
255 None
, /* not source window -> move from anywhere */
256 GDK_WINDOW_XID(window
), /* dest window */
257 0, 0, 0, 0, /* not source window -> move from anywhere */
262 //-----------------------------------------------------------------------------
263 // local code (see below)
264 //-----------------------------------------------------------------------------
266 // returns the child of win which currently has focus or NULL if not found
268 // Note: can't be static, needed by textctrl.cpp.
269 wxWindow
*wxFindFocusedChild(wxWindowGTK
*win
)
271 wxWindow
*winFocus
= wxWindowGTK::FindFocus();
273 return (wxWindow
*)NULL
;
275 if ( winFocus
== win
)
276 return (wxWindow
*)win
;
278 for ( wxWindowList::compatibility_iterator node
= win
->GetChildren().GetFirst();
280 node
= node
->GetNext() )
282 wxWindow
*child
= wxFindFocusedChild(node
->GetData());
287 return (wxWindow
*)NULL
;
290 static void GetScrollbarWidth(GtkWidget
* widget
, int& w
, int& h
)
292 GtkScrolledWindow
* scroll_window
= GTK_SCROLLED_WINDOW(widget
);
293 GtkScrolledWindowClass
* scroll_class
= GTK_SCROLLED_WINDOW_CLASS(GTK_OBJECT_GET_CLASS(scroll_window
));
294 GtkRequisition scroll_req
;
297 if (scroll_window
->vscrollbar_visible
)
299 scroll_req
.width
= 2;
300 scroll_req
.height
= 2;
301 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window
->vscrollbar
) )->size_request
)
302 (scroll_window
->vscrollbar
, &scroll_req
);
303 w
= scroll_req
.width
+
304 scroll_class
->scrollbar_spacing
;
308 if (scroll_window
->hscrollbar_visible
)
310 scroll_req
.width
= 2;
311 scroll_req
.height
= 2;
312 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(scroll_window
->hscrollbar
) )->size_request
)
313 (scroll_window
->hscrollbar
, &scroll_req
);
314 h
= scroll_req
.height
+
315 scroll_class
->scrollbar_spacing
;
319 static void draw_frame( GtkWidget
*widget
, wxWindowGTK
*win
)
321 // wxUniversal widgets draw the borders and scrollbars themselves
322 #ifndef __WXUNIVERSAL__
328 if (GTK_WIDGET_NO_WINDOW (widget
))
330 dx
+= widget
->allocation
.x
;
331 dy
+= widget
->allocation
.y
;
339 if (win
->m_hasScrolling
)
341 GetScrollbarWidth(widget
, dw
, dh
);
343 if (win
->GetLayoutDirection() == wxLayout_RightToLeft
)
345 // This is actually wrong for old GTK+ version
346 // which do not display the scrollbar on the
352 int w
= widget
->allocation
.width
-dw
;
353 int h
= widget
->allocation
.height
-dh
;
355 if (win
->HasFlag(wxRAISED_BORDER
))
357 gtk_paint_shadow (widget
->style
,
361 NULL
, NULL
, NULL
, // FIXME: No clipping?
366 if (win
->HasFlag(wxSUNKEN_BORDER
))
368 gtk_paint_shadow (widget
->style
,
372 NULL
, NULL
, NULL
, // FIXME: No clipping?
377 if (win
->HasFlag(wxSIMPLE_BORDER
))
380 gc
= gdk_gc_new( widget
->window
);
381 gdk_gc_set_foreground( gc
, &widget
->style
->black
);
382 gdk_draw_rectangle( widget
->window
, gc
, FALSE
, x
, y
, w
-1, h
-1 );
386 #endif // __WXUNIVERSAL__
389 //-----------------------------------------------------------------------------
390 // "expose_event" of m_widget
391 //-----------------------------------------------------------------------------
395 gtk_window_own_expose_callback( GtkWidget
*widget
,
396 GdkEventExpose
*gdk_event
,
399 if (gdk_event
->count
== 0)
400 draw_frame(widget
, win
);
405 //-----------------------------------------------------------------------------
406 // "size_request" of m_widget
407 //-----------------------------------------------------------------------------
409 // make it extern because wxStaticText needs to disconnect this one
411 void wxgtk_window_size_request_callback(GtkWidget
*widget
,
412 GtkRequisition
*requisition
,
416 win
->GetSize( &w
, &h
);
422 requisition
->height
= h
;
423 requisition
->width
= w
;
431 void wxgtk_combo_size_request_callback(GtkWidget
*widget
,
432 GtkRequisition
*requisition
,
435 // This callback is actually hooked into the text entry
436 // of the combo box, not the GtkHBox.
439 win
->GetSize( &w
, &h
);
445 GtkCombo
*gcombo
= GTK_COMBO(win
->m_widget
);
447 GtkRequisition entry_req
;
449 entry_req
.height
= 2;
450 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(gcombo
->entry
) )->size_request
)
451 (gcombo
->entry
, &entry_req
);
453 GtkRequisition button_req
;
454 button_req
.width
= 2;
455 button_req
.height
= 2;
456 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(gcombo
->button
) )->size_request
)
457 (gcombo
->button
, &button_req
);
459 requisition
->width
= w
- button_req
.width
;
460 requisition
->height
= entry_req
.height
;
464 #endif // wxUSE_COMBOBOX
466 //-----------------------------------------------------------------------------
467 // "expose_event" of m_wxwindow
468 //-----------------------------------------------------------------------------
472 gtk_window_expose_callback( GtkWidget
*widget
,
473 GdkEventExpose
*gdk_event
,
478 // This callback gets called in drawing-idle time under
479 // GTK 2.0, so we don't need to defer anything to idle
482 GtkPizza
*pizza
= GTK_PIZZA( widget
);
483 if (gdk_event
->window
!= pizza
->bin_window
)
485 // block expose events on GTK_WIDGET(pizza)->window,
486 // all drawing is done on pizza->bin_window
494 wxPrintf( wxT("OnExpose from ") );
495 if (win
->GetClassInfo() && win
->GetClassInfo()->GetClassName())
496 wxPrintf( win
->GetClassInfo()->GetClassName() );
497 wxPrintf( wxT(" %d %d %d %d\n"), (int)gdk_event
->area
.x
,
498 (int)gdk_event
->area
.y
,
499 (int)gdk_event
->area
.width
,
500 (int)gdk_event
->area
.height
);
505 win
->m_wxwindow
->style
,
509 (GdkRectangle
*) NULL
,
511 (char *)"button", // const_cast
516 win
->GetUpdateRegion() = wxRegion( gdk_event
->region
);
518 win
->GtkSendPaintEvents();
520 // Let parent window draw window-less widgets
525 //-----------------------------------------------------------------------------
526 // "key_press_event" from any window
527 //-----------------------------------------------------------------------------
529 // These are used when transforming Ctrl-alpha to ascii values 1-26
530 inline bool wxIsLowerChar(int code
)
532 return (code
>= 'a' && code
<= 'z' );
535 inline bool wxIsUpperChar(int code
)
537 return (code
>= 'A' && code
<= 'Z' );
541 // set WXTRACE to this to see the key event codes on the console
542 #define TRACE_KEYS _T("keyevent")
544 // translates an X key symbol to WXK_XXX value
546 // if isChar is true it means that the value returned will be used for EVT_CHAR
547 // event and then we choose the logical WXK_XXX, i.e. '/' for GDK_KP_Divide,
548 // for example, while if it is false it means that the value is going to be
549 // used for KEY_DOWN/UP events and then we translate GDK_KP_Divide to
551 static long wxTranslateKeySymToWXKey(KeySym keysym
, bool isChar
)
557 // Shift, Control and Alt don't generate the CHAR events at all
560 key_code
= isChar
? 0 : WXK_SHIFT
;
564 key_code
= isChar
? 0 : WXK_CONTROL
;
572 key_code
= isChar
? 0 : WXK_ALT
;
575 // neither do the toggle modifies
576 case GDK_Scroll_Lock
:
577 key_code
= isChar
? 0 : WXK_SCROLL
;
581 key_code
= isChar
? 0 : WXK_CAPITAL
;
585 key_code
= isChar
? 0 : WXK_NUMLOCK
;
589 // various other special keys
602 case GDK_ISO_Left_Tab
:
609 key_code
= WXK_RETURN
;
613 key_code
= WXK_CLEAR
;
617 key_code
= WXK_PAUSE
;
621 key_code
= WXK_SELECT
;
625 key_code
= WXK_PRINT
;
629 key_code
= WXK_EXECUTE
;
633 key_code
= WXK_ESCAPE
;
636 // cursor and other extended keyboard keys
638 key_code
= WXK_DELETE
;
654 key_code
= WXK_RIGHT
;
661 case GDK_Prior
: // == GDK_Page_Up
662 key_code
= WXK_PAGEUP
;
665 case GDK_Next
: // == GDK_Page_Down
666 key_code
= WXK_PAGEDOWN
;
678 key_code
= WXK_INSERT
;
693 key_code
= (isChar
? '0' : WXK_NUMPAD0
) + keysym
- GDK_KP_0
;
697 key_code
= isChar
? ' ' : WXK_NUMPAD_SPACE
;
701 key_code
= isChar
? WXK_TAB
: WXK_NUMPAD_TAB
;
705 key_code
= isChar
? WXK_RETURN
: WXK_NUMPAD_ENTER
;
709 key_code
= isChar
? WXK_F1
: WXK_NUMPAD_F1
;
713 key_code
= isChar
? WXK_F2
: WXK_NUMPAD_F2
;
717 key_code
= isChar
? WXK_F3
: WXK_NUMPAD_F3
;
721 key_code
= isChar
? WXK_F4
: WXK_NUMPAD_F4
;
725 key_code
= isChar
? WXK_HOME
: WXK_NUMPAD_HOME
;
729 key_code
= isChar
? WXK_LEFT
: WXK_NUMPAD_LEFT
;
733 key_code
= isChar
? WXK_UP
: WXK_NUMPAD_UP
;
737 key_code
= isChar
? WXK_RIGHT
: WXK_NUMPAD_RIGHT
;
741 key_code
= isChar
? WXK_DOWN
: WXK_NUMPAD_DOWN
;
744 case GDK_KP_Prior
: // == GDK_KP_Page_Up
745 key_code
= isChar
? WXK_PAGEUP
: WXK_NUMPAD_PAGEUP
;
748 case GDK_KP_Next
: // == GDK_KP_Page_Down
749 key_code
= isChar
? WXK_PAGEDOWN
: WXK_NUMPAD_PAGEDOWN
;
753 key_code
= isChar
? WXK_END
: WXK_NUMPAD_END
;
757 key_code
= isChar
? WXK_HOME
: WXK_NUMPAD_BEGIN
;
761 key_code
= isChar
? WXK_INSERT
: WXK_NUMPAD_INSERT
;
765 key_code
= isChar
? WXK_DELETE
: WXK_NUMPAD_DELETE
;
769 key_code
= isChar
? '=' : WXK_NUMPAD_EQUAL
;
772 case GDK_KP_Multiply
:
773 key_code
= isChar
? '*' : WXK_NUMPAD_MULTIPLY
;
777 key_code
= isChar
? '+' : WXK_NUMPAD_ADD
;
780 case GDK_KP_Separator
:
781 // FIXME: what is this?
782 key_code
= isChar
? '.' : WXK_NUMPAD_SEPARATOR
;
785 case GDK_KP_Subtract
:
786 key_code
= isChar
? '-' : WXK_NUMPAD_SUBTRACT
;
790 key_code
= isChar
? '.' : WXK_NUMPAD_DECIMAL
;
794 key_code
= isChar
? '/' : WXK_NUMPAD_DIVIDE
;
811 key_code
= WXK_F1
+ keysym
- GDK_F1
;
821 static inline bool wxIsAsciiKeysym(KeySym ks
)
826 static void wxFillOtherKeyEventFields(wxKeyEvent
& event
,
828 GdkEventKey
*gdk_event
)
832 GdkModifierType state
;
833 if (gdk_event
->window
)
834 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, &state
);
836 event
.SetTimestamp( gdk_event
->time
);
837 event
.SetId(win
->GetId());
838 event
.m_shiftDown
= (gdk_event
->state
& GDK_SHIFT_MASK
) != 0;
839 event
.m_controlDown
= (gdk_event
->state
& GDK_CONTROL_MASK
) != 0;
840 event
.m_altDown
= (gdk_event
->state
& GDK_MOD1_MASK
) != 0;
841 event
.m_metaDown
= (gdk_event
->state
& GDK_MOD2_MASK
) != 0;
842 event
.m_scanCode
= gdk_event
->keyval
;
843 event
.m_rawCode
= (wxUint32
) gdk_event
->keyval
;
844 event
.m_rawFlags
= 0;
846 event
.m_uniChar
= gdk_keyval_to_unicode(gdk_event
->keyval
);
848 wxGetMousePosition( &x
, &y
);
849 win
->ScreenToClient( &x
, &y
);
852 event
.SetEventObject( win
);
857 wxTranslateGTKKeyEventToWx(wxKeyEvent
& event
,
859 GdkEventKey
*gdk_event
)
861 // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string
862 // but only event->keyval which is quite useless to us, so remember
863 // the last character from GDK_KEY_PRESS and reuse it as last resort
865 // NB: should be MT-safe as we're always called from the main thread only
870 } s_lastKeyPress
= { 0, 0 };
872 KeySym keysym
= gdk_event
->keyval
;
874 wxLogTrace(TRACE_KEYS
, _T("Key %s event: keysym = %ld"),
875 event
.GetEventType() == wxEVT_KEY_UP
? _T("release")
879 long key_code
= wxTranslateKeySymToWXKey(keysym
, false /* !isChar */);
883 // do we have the translation or is it a plain ASCII character?
884 if ( (gdk_event
->length
== 1) || wxIsAsciiKeysym(keysym
) )
886 // we should use keysym if it is ASCII as X does some translations
887 // like "I pressed while Control is down" => "Ctrl-I" == "TAB"
888 // which we don't want here (but which we do use for OnChar())
889 if ( !wxIsAsciiKeysym(keysym
) )
891 keysym
= (KeySym
)gdk_event
->string
[0];
894 // we want to always get the same key code when the same key is
895 // pressed regardless of the state of the modifiers, i.e. on a
896 // standard US keyboard pressing '5' or '%' ('5' key with
897 // Shift) should result in the same key code in OnKeyDown():
898 // '5' (although OnChar() will get either '5' or '%').
900 // to do it we first translate keysym to keycode (== scan code)
901 // and then back but always using the lower register
902 Display
*dpy
= (Display
*)wxGetDisplay();
903 KeyCode keycode
= XKeysymToKeycode(dpy
, keysym
);
905 wxLogTrace(TRACE_KEYS
, _T("\t-> keycode %d"), keycode
);
907 KeySym keysymNormalized
= XKeycodeToKeysym(dpy
, keycode
, 0);
909 // use the normalized, i.e. lower register, keysym if we've
911 key_code
= keysymNormalized
? keysymNormalized
: keysym
;
913 // as explained above, we want to have lower register key codes
914 // normally but for the letter keys we want to have the upper ones
916 // NB: don't use XConvertCase() here, we want to do it for letters
918 key_code
= toupper(key_code
);
920 else // non ASCII key, what to do?
922 // by default, ignore it
925 // but if we have cached information from the last KEY_PRESS
926 if ( gdk_event
->type
== GDK_KEY_RELEASE
)
929 if ( keysym
== s_lastKeyPress
.keysym
)
931 key_code
= s_lastKeyPress
.keycode
;
936 if ( gdk_event
->type
== GDK_KEY_PRESS
)
938 // remember it to be reused for KEY_UP event later
939 s_lastKeyPress
.keysym
= keysym
;
940 s_lastKeyPress
.keycode
= key_code
;
944 wxLogTrace(TRACE_KEYS
, _T("\t-> wxKeyCode %ld"), key_code
);
946 // sending unknown key events doesn't really make sense
950 // now fill all the other fields
951 wxFillOtherKeyEventFields(event
, win
, gdk_event
);
953 event
.m_keyCode
= key_code
;
955 if ( gdk_event
->type
== GDK_KEY_PRESS
|| gdk_event
->type
== GDK_KEY_RELEASE
)
957 event
.m_uniChar
= key_code
;
967 GtkIMContext
*context
;
968 GdkEventKey
*lastKeyEvent
;
972 context
= gtk_im_multicontext_new();
977 g_object_unref (context
);
983 gtk_window_key_press_callback( GtkWidget
*widget
,
984 GdkEventKey
*gdk_event
,
991 if (g_blockEventsOnDrag
)
994 // GTK+ sends keypress events to the focus widget and then
995 // to all its parent and grandparent widget. We only want
996 // the key events from the focus widget.
997 if (!GTK_WIDGET_HAS_FOCUS(widget
))
1000 wxKeyEvent
event( wxEVT_KEY_DOWN
);
1002 bool return_after_IM
= false;
1004 if( wxTranslateGTKKeyEventToWx(event
, win
, gdk_event
) )
1006 // Emit KEY_DOWN event
1007 ret
= win
->GetEventHandler()->ProcessEvent( event
);
1011 // Return after IM processing as we cannot do
1012 // anything with it anyhow.
1013 return_after_IM
= true;
1016 if ((!ret
) && (win
->m_imData
!= NULL
))
1018 // We should let GTK+ IM filter key event first. According to GTK+ 2.0 API
1019 // docs, if IM filter returns true, no further processing should be done.
1020 // we should send the key_down event anyway.
1021 bool intercepted_by_IM
= gtk_im_context_filter_keypress(win
->m_imData
->context
, gdk_event
);
1022 win
->m_imData
->lastKeyEvent
= NULL
;
1023 if (intercepted_by_IM
)
1025 wxLogTrace(TRACE_KEYS
, _T("Key event intercepted by IM"));
1030 if (return_after_IM
)
1036 wxWindowGTK
*ancestor
= win
;
1039 int command
= ancestor
->GetAcceleratorTable()->GetCommand( event
);
1042 wxCommandEvent
command_event( wxEVT_COMMAND_MENU_SELECTED
, command
);
1043 ret
= ancestor
->GetEventHandler()->ProcessEvent( command_event
);
1046 if (ancestor
->IsTopLevel())
1048 ancestor
= ancestor
->GetParent();
1051 #endif // wxUSE_ACCEL
1053 // Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
1054 // will only be sent if it is not in an accelerator table.
1058 KeySym keysym
= gdk_event
->keyval
;
1059 // Find key code for EVT_CHAR and EVT_CHAR_HOOK events
1060 key_code
= wxTranslateKeySymToWXKey(keysym
, true /* isChar */);
1063 if ( wxIsAsciiKeysym(keysym
) )
1066 key_code
= (unsigned char)keysym
;
1068 // gdk_event->string is actually deprecated
1069 else if ( gdk_event
->length
== 1 )
1071 key_code
= (unsigned char)gdk_event
->string
[0];
1077 wxLogTrace(TRACE_KEYS
, _T("Char event: %ld"), key_code
);
1079 event
.m_keyCode
= key_code
;
1081 // To conform to the docs we need to translate Ctrl-alpha
1082 // characters to values in the range 1-26.
1083 if ( event
.ControlDown() &&
1084 ( wxIsLowerChar(key_code
) || wxIsUpperChar(key_code
) ))
1086 if ( wxIsLowerChar(key_code
) )
1087 event
.m_keyCode
= key_code
- 'a' + 1;
1088 if ( wxIsUpperChar(key_code
) )
1089 event
.m_keyCode
= key_code
- 'A' + 1;
1091 event
.m_uniChar
= event
.m_keyCode
;
1095 // Implement OnCharHook by checking ancestor top level windows
1096 wxWindow
*parent
= win
;
1097 while (parent
&& !parent
->IsTopLevel())
1098 parent
= parent
->GetParent();
1101 event
.SetEventType( wxEVT_CHAR_HOOK
);
1102 ret
= parent
->GetEventHandler()->ProcessEvent( event
);
1107 event
.SetEventType(wxEVT_CHAR
);
1108 ret
= win
->GetEventHandler()->ProcessEvent( event
);
1119 gtk_wxwindow_commit_cb (GtkIMContext
*context
,
1123 wxKeyEvent
event( wxEVT_KEY_DOWN
);
1125 // take modifiers, cursor position, timestamp etc. from the last
1126 // key_press_event that was fed into Input Method:
1127 if (window
->m_imData
->lastKeyEvent
)
1129 wxFillOtherKeyEventFields(event
,
1130 window
, window
->m_imData
->lastKeyEvent
);
1134 event
.SetEventObject( window
);
1137 const wxString
data(wxGTK_CONV_BACK_SYS(str
));
1143 // Implement OnCharHook by checking ancestor top level windows
1144 wxWindow
*parent
= window
;
1145 while (parent
&& !parent
->IsTopLevel())
1146 parent
= parent
->GetParent();
1148 for( wxString::const_iterator pstr
= data
.begin(); pstr
!= data
.end(); ++pstr
)
1151 event
.m_uniChar
= *pstr
;
1152 // Backward compatible for ISO-8859-1
1153 event
.m_keyCode
= *pstr
< 256 ? event
.m_uniChar
: 0;
1154 wxLogTrace(TRACE_KEYS
, _T("IM sent character '%c'"), event
.m_uniChar
);
1156 event
.m_keyCode
= (char)*pstr
;
1157 #endif // wxUSE_UNICODE
1159 // To conform to the docs we need to translate Ctrl-alpha
1160 // characters to values in the range 1-26.
1161 if ( event
.ControlDown() &&
1162 ( wxIsLowerChar(*pstr
) || wxIsUpperChar(*pstr
) ))
1164 if ( wxIsLowerChar(*pstr
) )
1165 event
.m_keyCode
= *pstr
- 'a' + 1;
1166 if ( wxIsUpperChar(*pstr
) )
1167 event
.m_keyCode
= *pstr
- 'A' + 1;
1169 event
.m_keyCode
= *pstr
- 'a' + 1;
1171 event
.m_uniChar
= event
.m_keyCode
;
1177 event
.SetEventType( wxEVT_CHAR_HOOK
);
1178 ret
= parent
->GetEventHandler()->ProcessEvent( event
);
1183 event
.SetEventType(wxEVT_CHAR
);
1184 ret
= window
->GetEventHandler()->ProcessEvent( event
);
1191 //-----------------------------------------------------------------------------
1192 // "key_release_event" from any window
1193 //-----------------------------------------------------------------------------
1197 gtk_window_key_release_callback( GtkWidget
*widget
,
1198 GdkEventKey
*gdk_event
,
1206 if (g_blockEventsOnDrag
)
1209 wxKeyEvent
event( wxEVT_KEY_UP
);
1210 if ( !wxTranslateGTKKeyEventToWx(event
, win
, gdk_event
) )
1212 // unknown key pressed, ignore (the event would be useless anyhow)
1216 return win
->GTKProcessEvent(event
);
1220 // ============================================================================
1222 // ============================================================================
1224 // ----------------------------------------------------------------------------
1225 // mouse event processing helpers
1226 // ----------------------------------------------------------------------------
1228 // init wxMouseEvent with the info from GdkEventXXX struct
1229 template<typename T
> void InitMouseEvent(wxWindowGTK
*win
,
1230 wxMouseEvent
& event
,
1233 event
.SetTimestamp( gdk_event
->time
);
1234 event
.m_shiftDown
= gdk_event
->state
& GDK_SHIFT_MASK
;
1235 event
.m_controlDown
= gdk_event
->state
& GDK_CONTROL_MASK
;
1236 event
.m_altDown
= gdk_event
->state
& GDK_MOD1_MASK
;
1237 event
.m_metaDown
= gdk_event
->state
& GDK_MOD2_MASK
;
1238 event
.m_leftDown
= gdk_event
->state
& GDK_BUTTON1_MASK
;
1239 event
.m_middleDown
= gdk_event
->state
& GDK_BUTTON2_MASK
;
1240 event
.m_rightDown
= gdk_event
->state
& GDK_BUTTON3_MASK
;
1241 event
.m_aux1Down
= gdk_event
->state
& GDK_BUTTON4_MASK
;
1242 event
.m_aux2Down
= gdk_event
->state
& GDK_BUTTON5_MASK
;
1244 wxPoint pt
= win
->GetClientAreaOrigin();
1245 event
.m_x
= (wxCoord
)gdk_event
->x
- pt
.x
;
1246 event
.m_y
= (wxCoord
)gdk_event
->y
- pt
.y
;
1248 if ((win
->m_wxwindow
) && (win
->GetLayoutDirection() == wxLayout_RightToLeft
))
1250 // origin in the upper right corner
1251 int window_width
= gtk_pizza_get_rtl_offset( GTK_PIZZA(win
->m_wxwindow
) );
1252 event
.m_x
= window_width
- event
.m_x
;
1255 event
.SetEventObject( win
);
1256 event
.SetId( win
->GetId() );
1257 event
.SetTimestamp( gdk_event
->time
);
1260 static void AdjustEventButtonState(wxMouseEvent
& event
)
1262 // GDK reports the old state of the button for a button press event, but
1263 // for compatibility with MSW and common sense we want m_leftDown be TRUE
1264 // for a LEFT_DOWN event, not FALSE, so we will invert
1265 // left/right/middleDown for the corresponding click events
1267 if ((event
.GetEventType() == wxEVT_LEFT_DOWN
) ||
1268 (event
.GetEventType() == wxEVT_LEFT_DCLICK
) ||
1269 (event
.GetEventType() == wxEVT_LEFT_UP
))
1271 event
.m_leftDown
= !event
.m_leftDown
;
1275 if ((event
.GetEventType() == wxEVT_MIDDLE_DOWN
) ||
1276 (event
.GetEventType() == wxEVT_MIDDLE_DCLICK
) ||
1277 (event
.GetEventType() == wxEVT_MIDDLE_UP
))
1279 event
.m_middleDown
= !event
.m_middleDown
;
1283 if ((event
.GetEventType() == wxEVT_RIGHT_DOWN
) ||
1284 (event
.GetEventType() == wxEVT_RIGHT_DCLICK
) ||
1285 (event
.GetEventType() == wxEVT_RIGHT_UP
))
1287 event
.m_rightDown
= !event
.m_rightDown
;
1292 // find the window to send the mouse event too
1294 wxWindowGTK
*FindWindowForMouseEvent(wxWindowGTK
*win
, wxCoord
& x
, wxCoord
& y
)
1299 if (win
->m_wxwindow
)
1301 GtkPizza
*pizza
= GTK_PIZZA(win
->m_wxwindow
);
1302 xx
+= gtk_pizza_get_xoffset( pizza
);
1303 yy
+= gtk_pizza_get_yoffset( pizza
);
1306 wxWindowList::compatibility_iterator node
= win
->GetChildren().GetFirst();
1309 wxWindowGTK
*child
= node
->GetData();
1311 node
= node
->GetNext();
1312 if (!child
->IsShown())
1315 if (child
->IsTransparentForMouse())
1317 // wxStaticBox is transparent in the box itself
1318 int xx1
= child
->m_x
;
1319 int yy1
= child
->m_y
;
1320 int xx2
= child
->m_x
+ child
->m_width
;
1321 int yy2
= child
->m_y
+ child
->m_height
;
1324 if (((xx
>= xx1
) && (xx
<= xx1
+10) && (yy
>= yy1
) && (yy
<= yy2
)) ||
1326 ((xx
>= xx2
-10) && (xx
<= xx2
) && (yy
>= yy1
) && (yy
<= yy2
)) ||
1328 ((xx
>= xx1
) && (xx
<= xx2
) && (yy
>= yy1
) && (yy
<= yy1
+10)) ||
1330 ((xx
>= xx1
) && (xx
<= xx2
) && (yy
>= yy2
-1) && (yy
<= yy2
)))
1341 if ((child
->m_wxwindow
== (GtkWidget
*) NULL
) &&
1342 (child
->m_x
<= xx
) &&
1343 (child
->m_y
<= yy
) &&
1344 (child
->m_x
+child
->m_width
>= xx
) &&
1345 (child
->m_y
+child
->m_height
>= yy
))
1358 // ----------------------------------------------------------------------------
1359 // common event handlers helpers
1360 // ----------------------------------------------------------------------------
1362 bool wxWindowGTK::GTKProcessEvent(wxEvent
& event
) const
1364 // nothing special at this level
1365 return GetEventHandler()->ProcessEvent(event
);
1368 int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny
*event
) const
1374 if (g_blockEventsOnDrag
)
1376 if (g_blockEventsOnScroll
)
1379 if (!GTKIsOwnWindow(event
->window
))
1385 // overloads for all GDK event types we use here: we need to have this as
1386 // GdkEventXXX can't be implicitly cast to GdkEventAny even if it, in fact,
1387 // derives from it in the sense that the structs have the same layout
1388 #define wxDEFINE_COMMON_PROLOGUE_OVERLOAD(T) \
1389 static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win) \
1391 return win->GTKCallbackCommonPrologue((GdkEventAny *)event); \
1394 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventButton
)
1395 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion
)
1396 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing
)
1398 #undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD
1400 #define wxCOMMON_CALLBACK_PROLOGUE(event, win) \
1401 const int rc = wxGtkCallbackCommonPrologue(event, win); \
1405 // send the wxChildFocusEvent and wxFocusEvent, common code of
1406 // gtk_window_focus_in_callback() and SetFocus()
1407 static bool DoSendFocusEvents(wxWindow
*win
)
1409 // Notify the parent keeping track of focus for the kbd navigation
1410 // purposes that we got it.
1411 wxChildFocusEvent
eventChildFocus(win
);
1412 (void)win
->GetEventHandler()->ProcessEvent(eventChildFocus
);
1414 wxFocusEvent
eventFocus(wxEVT_SET_FOCUS
, win
->GetId());
1415 eventFocus
.SetEventObject(win
);
1417 return win
->GetEventHandler()->ProcessEvent(eventFocus
);
1420 // all event handlers must have C linkage as they're called from GTK+ C code
1424 //-----------------------------------------------------------------------------
1425 // "button_press_event"
1426 //-----------------------------------------------------------------------------
1429 gtk_window_button_press_callback( GtkWidget
*widget
,
1430 GdkEventButton
*gdk_event
,
1433 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1435 g_lastButtonNumber
= gdk_event
->button
;
1437 // GDK sends surplus button down events
1438 // before a double click event. We
1439 // need to filter these out.
1440 if ((gdk_event
->type
== GDK_BUTTON_PRESS
) && (win
->m_wxwindow
))
1442 GdkEvent
*peek_event
= gdk_event_peek();
1445 if ((peek_event
->type
== GDK_2BUTTON_PRESS
) ||
1446 (peek_event
->type
== GDK_3BUTTON_PRESS
))
1448 gdk_event_free( peek_event
);
1453 gdk_event_free( peek_event
);
1458 wxEventType event_type
= wxEVT_NULL
;
1460 // GdkDisplay is a GTK+ 2.2.0 thing
1461 #if defined(__WXGTK20__) && GTK_CHECK_VERSION(2, 2, 0)
1462 if ( gdk_event
->type
== GDK_2BUTTON_PRESS
&&
1463 !gtk_check_version(2,2,0) &&
1464 gdk_event
->button
>= 1 && gdk_event
->button
<= 3 )
1466 // Reset GDK internal timestamp variables in order to disable GDK
1467 // triple click events. GDK will then next time believe no button has
1468 // been clicked just before, and send a normal button click event.
1469 GdkDisplay
* display
= gtk_widget_get_display (widget
);
1470 display
->button_click_time
[1] = 0;
1471 display
->button_click_time
[0] = 0;
1475 if (gdk_event
->button
== 1)
1477 // note that GDK generates triple click events which are not supported
1478 // by wxWidgets but still have to be passed to the app as otherwise
1479 // clicks would simply go missing
1480 switch (gdk_event
->type
)
1482 // we shouldn't get triple clicks at all for GTK2 because we
1483 // suppress them artificially using the code above but we still
1484 // should map them to something for GTK1 and not just ignore them
1485 // as this would lose clicks
1486 case GDK_3BUTTON_PRESS
: // we could also map this to DCLICK...
1487 case GDK_BUTTON_PRESS
:
1488 event_type
= wxEVT_LEFT_DOWN
;
1491 case GDK_2BUTTON_PRESS
:
1492 event_type
= wxEVT_LEFT_DCLICK
;
1496 // just to silence gcc warnings
1500 else if (gdk_event
->button
== 2)
1502 switch (gdk_event
->type
)
1504 case GDK_3BUTTON_PRESS
:
1505 case GDK_BUTTON_PRESS
:
1506 event_type
= wxEVT_MIDDLE_DOWN
;
1509 case GDK_2BUTTON_PRESS
:
1510 event_type
= wxEVT_MIDDLE_DCLICK
;
1517 else if (gdk_event
->button
== 3)
1519 switch (gdk_event
->type
)
1521 case GDK_3BUTTON_PRESS
:
1522 case GDK_BUTTON_PRESS
:
1523 event_type
= wxEVT_RIGHT_DOWN
;
1526 case GDK_2BUTTON_PRESS
:
1527 event_type
= wxEVT_RIGHT_DCLICK
;
1535 if ( event_type
== wxEVT_NULL
)
1537 // unknown mouse button or click type
1541 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1543 wxMouseEvent
event( event_type
);
1544 InitMouseEvent( win
, event
, gdk_event
);
1546 AdjustEventButtonState(event
);
1548 // wxListBox actually gets mouse events from the item, so we need to give it
1549 // a chance to correct this
1550 win
->FixUpMouseEvent(widget
, event
.m_x
, event
.m_y
);
1552 // find the correct window to send the event to: it may be a different one
1553 // from the one which got it at GTK+ level because some controls don't have
1554 // their own X window and thus cannot get any events.
1555 if ( !g_captureWindow
)
1556 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1558 // reset the event object and id in case win changed.
1559 event
.SetEventObject( win
);
1560 event
.SetId( win
->GetId() );
1562 bool ret
= win
->GTKProcessEvent( event
);
1563 g_lastMouseEvent
= NULL
;
1567 if ((event_type
== wxEVT_LEFT_DOWN
) && !win
->IsOfStandardClass() &&
1568 (g_focusWindow
!= win
) /* && win->IsFocusable() */)
1573 if (event_type
== wxEVT_RIGHT_DOWN
)
1575 // generate a "context menu" event: this is similar to right mouse
1576 // click under many GUIs except that it is generated differently
1577 // (right up under MSW, ctrl-click under Mac, right down here) and
1579 // (a) it's a command event and so is propagated to the parent
1580 // (b) under some ports it can be generated from kbd too
1581 // (c) it uses screen coords (because of (a))
1582 wxContextMenuEvent
evtCtx(
1585 win
->ClientToScreen(event
.GetPosition()));
1586 evtCtx
.SetEventObject(win
);
1587 return win
->GTKProcessEvent(evtCtx
);
1593 //-----------------------------------------------------------------------------
1594 // "button_release_event"
1595 //-----------------------------------------------------------------------------
1598 gtk_window_button_release_callback( GtkWidget
*widget
,
1599 GdkEventButton
*gdk_event
,
1602 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1604 g_lastButtonNumber
= 0;
1606 wxEventType event_type
= wxEVT_NULL
;
1608 switch (gdk_event
->button
)
1611 event_type
= wxEVT_LEFT_UP
;
1615 event_type
= wxEVT_MIDDLE_UP
;
1619 event_type
= wxEVT_RIGHT_UP
;
1623 // unknown button, don't process
1627 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1629 wxMouseEvent
event( event_type
);
1630 InitMouseEvent( win
, event
, gdk_event
);
1632 AdjustEventButtonState(event
);
1634 // same wxListBox hack as above
1635 win
->FixUpMouseEvent(widget
, event
.m_x
, event
.m_y
);
1637 if ( !g_captureWindow
)
1638 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1640 // reset the event object and id in case win changed.
1641 event
.SetEventObject( win
);
1642 event
.SetId( win
->GetId() );
1644 bool ret
= win
->GTKProcessEvent(event
);
1646 g_lastMouseEvent
= NULL
;
1651 //-----------------------------------------------------------------------------
1652 // "motion_notify_event"
1653 //-----------------------------------------------------------------------------
1656 gtk_window_motion_notify_callback( GtkWidget
*widget
,
1657 GdkEventMotion
*gdk_event
,
1660 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1662 if (gdk_event
->is_hint
)
1666 GdkModifierType state
;
1667 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, &state
);
1672 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1674 wxMouseEvent
event( wxEVT_MOTION
);
1675 InitMouseEvent(win
, event
, gdk_event
);
1677 if ( g_captureWindow
)
1679 // synthesise a mouse enter or leave event if needed
1680 GdkWindow
*winUnderMouse
= gdk_window_at_pointer(NULL
, NULL
);
1681 // This seems to be necessary and actually been added to
1682 // GDK itself in version 2.0.X
1685 bool hasMouse
= winUnderMouse
== gdk_event
->window
;
1686 if ( hasMouse
!= g_captureWindowHasMouse
)
1688 // the mouse changed window
1689 g_captureWindowHasMouse
= hasMouse
;
1691 wxMouseEvent
eventM(g_captureWindowHasMouse
? wxEVT_ENTER_WINDOW
1692 : wxEVT_LEAVE_WINDOW
);
1693 InitMouseEvent(win
, eventM
, gdk_event
);
1694 eventM
.SetEventObject(win
);
1695 win
->GTKProcessEvent(eventM
);
1700 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1702 // reset the event object and id in case win changed.
1703 event
.SetEventObject( win
);
1704 event
.SetId( win
->GetId() );
1707 if ( !g_captureWindow
)
1709 wxSetCursorEvent
cevent( event
.m_x
, event
.m_y
);
1710 if (win
->GTKProcessEvent( cevent
))
1712 win
->SetCursor( cevent
.GetCursor() );
1716 bool ret
= win
->GTKProcessEvent(event
);
1718 g_lastMouseEvent
= NULL
;
1723 //-----------------------------------------------------------------------------
1724 // "scroll_event" (mouse wheel event)
1725 //-----------------------------------------------------------------------------
1728 window_scroll_event(GtkWidget
*, GdkEventScroll
* gdk_event
, wxWindow
* win
)
1732 if (gdk_event
->direction
!= GDK_SCROLL_UP
&&
1733 gdk_event
->direction
!= GDK_SCROLL_DOWN
)
1738 wxMouseEvent
event(wxEVT_MOUSEWHEEL
);
1739 InitMouseEvent(win
, event
, gdk_event
);
1740 event
.m_linesPerAction
= 3;
1741 event
.m_wheelDelta
= 120;
1742 if (gdk_event
->direction
== GDK_SCROLL_UP
)
1743 event
.m_wheelRotation
= 120;
1745 event
.m_wheelRotation
= -120;
1747 return win
->GTKProcessEvent(event
);
1750 //-----------------------------------------------------------------------------
1752 //-----------------------------------------------------------------------------
1754 static gboolean
wxgtk_window_popup_menu_callback(GtkWidget
*, wxWindowGTK
* win
)
1756 wxContextMenuEvent
event(wxEVT_CONTEXT_MENU
, win
->GetId(), wxPoint(-1, -1));
1757 event
.SetEventObject(win
);
1758 return win
->GTKProcessEvent(event
);
1761 //-----------------------------------------------------------------------------
1763 //-----------------------------------------------------------------------------
1766 gtk_window_focus_in_callback( GtkWidget
*widget
,
1767 GdkEventFocus
*WXUNUSED(event
),
1773 gtk_im_context_focus_in(win
->m_imData
->context
);
1776 g_focusWindow
= win
;
1778 wxLogTrace(TRACE_FOCUS
,
1779 _T("%s: focus in"), win
->GetName().c_str());
1782 // caret needs to be informed about focus change
1783 wxCaret
*caret
= win
->GetCaret();
1786 caret
->OnSetFocus();
1788 #endif // wxUSE_CARET
1790 gboolean ret
= FALSE
;
1792 // does the window itself think that it has the focus?
1793 if ( !win
->m_hasFocus
)
1795 // not yet, notify it
1796 win
->m_hasFocus
= true;
1798 (void)DoSendFocusEvents(win
);
1803 // Disable default focus handling for custom windows
1804 // since the default GTK+ handler issues a repaint
1805 if (win
->m_wxwindow
)
1811 //-----------------------------------------------------------------------------
1812 // "focus_out_event"
1813 //-----------------------------------------------------------------------------
1816 gtk_window_focus_out_callback( GtkWidget
*widget
,
1817 GdkEventFocus
*gdk_event
,
1823 gtk_im_context_focus_out(win
->m_imData
->context
);
1825 wxLogTrace( TRACE_FOCUS
,
1826 _T("%s: focus out"), win
->GetName().c_str() );
1829 wxWindowGTK
*winFocus
= wxFindFocusedChild(win
);
1833 g_focusWindow
= (wxWindowGTK
*)NULL
;
1836 // caret needs to be informed about focus change
1837 wxCaret
*caret
= win
->GetCaret();
1840 caret
->OnKillFocus();
1842 #endif // wxUSE_CARET
1844 // don't send the window a kill focus event if it thinks that it doesn't
1845 // have focus already
1846 if ( win
->m_hasFocus
)
1848 // the event handler might delete the window when it loses focus, so
1849 // check whether this is a custom window before calling it
1850 const bool has_wxwindow
= win
->m_wxwindow
!= NULL
;
1852 win
->m_hasFocus
= false;
1854 wxFocusEvent
event( wxEVT_KILL_FOCUS
, win
->GetId() );
1855 event
.SetEventObject( win
);
1857 (void)win
->GTKProcessEvent( event
);
1859 // Disable default focus handling for custom windows
1860 // since the default GTK+ handler issues a repaint
1867 // continue with normal processing
1872 wx_window_focus_callback(GtkWidget
*widget
,
1873 GtkDirectionType direction
,
1876 // the default handler for focus signal in GtkPizza (or, rather, in
1877 // GtkScrolledWindow from which GtkPizza inherits this behaviour) sets
1878 // focus to the window itself even if it doesn't accept focus, i.e. has no
1879 // GTK_CAN_FOCUS in its style -- work around this by forcibly preventing
1880 // the signal from reaching gtk_scrolled_window_focus() if we don't have
1881 // any children which might accept focus (we know we don't accept the focus
1882 // ourselves as this signal is only connected in this case)
1883 if ( win
->GetChildren().empty() )
1884 g_signal_stop_emission_by_name(widget
, "focus");
1886 // we didn't change the focus
1890 //-----------------------------------------------------------------------------
1891 // "enter_notify_event"
1892 //-----------------------------------------------------------------------------
1895 gtk_window_enter_callback( GtkWidget
*widget
,
1896 GdkEventCrossing
*gdk_event
,
1899 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1901 // Event was emitted after a grab
1902 if (gdk_event
->mode
!= GDK_CROSSING_NORMAL
) return FALSE
;
1906 GdkModifierType state
= (GdkModifierType
)0;
1908 gdk_window_get_pointer( widget
->window
, &x
, &y
, &state
);
1910 wxMouseEvent
event( wxEVT_ENTER_WINDOW
);
1911 InitMouseEvent(win
, event
, gdk_event
);
1912 wxPoint pt
= win
->GetClientAreaOrigin();
1913 event
.m_x
= x
+ pt
.x
;
1914 event
.m_y
= y
+ pt
.y
;
1916 if ( !g_captureWindow
)
1918 wxSetCursorEvent
cevent( event
.m_x
, event
.m_y
);
1919 if (win
->GTKProcessEvent( cevent
))
1921 win
->SetCursor( cevent
.GetCursor() );
1925 return win
->GTKProcessEvent(event
);
1928 //-----------------------------------------------------------------------------
1929 // "leave_notify_event"
1930 //-----------------------------------------------------------------------------
1933 gtk_window_leave_callback( GtkWidget
*widget
,
1934 GdkEventCrossing
*gdk_event
,
1937 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1939 // Event was emitted after an ungrab
1940 if (gdk_event
->mode
!= GDK_CROSSING_NORMAL
) return FALSE
;
1942 wxMouseEvent
event( wxEVT_LEAVE_WINDOW
);
1946 GdkModifierType state
= (GdkModifierType
)0;
1948 gdk_window_get_pointer( widget
->window
, &x
, &y
, &state
);
1950 InitMouseEvent(win
, event
, gdk_event
);
1952 return win
->GTKProcessEvent(event
);
1955 //-----------------------------------------------------------------------------
1956 // "value_changed" from scrollbar
1957 //-----------------------------------------------------------------------------
1960 gtk_scrollbar_value_changed(GtkRange
* range
, wxWindow
* win
)
1962 wxEventType eventType
= win
->GetScrollEventType(range
);
1963 if (eventType
!= wxEVT_NULL
)
1965 // Convert scroll event type to scrollwin event type
1966 eventType
+= wxEVT_SCROLLWIN_TOP
- wxEVT_SCROLL_TOP
;
1968 // find the scrollbar which generated the event
1969 wxWindowGTK::ScrollDir dir
= win
->ScrollDirFromRange(range
);
1971 // generate the corresponding wx event
1972 const int orient
= wxWindow::OrientFromScrollDir(dir
);
1973 wxScrollWinEvent
event(eventType
, win
->GetScrollPos(orient
), orient
);
1974 event
.SetEventObject(win
);
1976 win
->GTKProcessEvent(event
);
1980 //-----------------------------------------------------------------------------
1981 // "button_press_event" from scrollbar
1982 //-----------------------------------------------------------------------------
1985 gtk_scrollbar_button_press_event(GtkRange
*, GdkEventButton
*, wxWindow
* win
)
1989 g_blockEventsOnScroll
= true;
1990 win
->m_mouseButtonDown
= true;
1995 //-----------------------------------------------------------------------------
1996 // "event_after" from scrollbar
1997 //-----------------------------------------------------------------------------
2000 gtk_scrollbar_event_after(GtkRange
* range
, GdkEvent
* event
, wxWindow
* win
)
2002 if (event
->type
== GDK_BUTTON_RELEASE
)
2004 g_signal_handlers_block_by_func(range
, (void*)gtk_scrollbar_event_after
, win
);
2006 const int orient
= wxWindow::OrientFromScrollDir(
2007 win
->ScrollDirFromRange(range
));
2008 wxScrollWinEvent
event(wxEVT_SCROLLWIN_THUMBRELEASE
, win
->GetScrollPos(orient
), orient
);
2009 event
.SetEventObject(win
);
2010 win
->GTKProcessEvent(event
);
2014 //-----------------------------------------------------------------------------
2015 // "button_release_event" from scrollbar
2016 //-----------------------------------------------------------------------------
2019 gtk_scrollbar_button_release_event(GtkRange
* range
, GdkEventButton
*, wxWindow
* win
)
2023 g_blockEventsOnScroll
= false;
2024 win
->m_mouseButtonDown
= false;
2025 // If thumb tracking
2026 if (win
->m_isScrolling
)
2028 win
->m_isScrolling
= false;
2029 // Hook up handler to send thumb release event after this emission is finished.
2030 // To allow setting scroll position from event handler, sending event must
2031 // be deferred until after the GtkRange handler for this signal has run
2032 g_signal_handlers_unblock_by_func(range
, (void*)gtk_scrollbar_event_after
, win
);
2038 //-----------------------------------------------------------------------------
2039 // "realize" from m_widget
2040 //-----------------------------------------------------------------------------
2043 gtk_window_realized_callback( GtkWidget
*m_widget
, wxWindow
*win
)
2049 GtkPizza
*pizza
= GTK_PIZZA( m_widget
);
2050 gtk_im_context_set_client_window( win
->m_imData
->context
,
2051 pizza
->bin_window
);
2054 // We cannot set colours and fonts before the widget
2055 // been realized, so we do this directly after realization
2056 // or otherwise in idle time
2058 if (win
->m_needsStyleChange
)
2060 win
->SetBackgroundStyle(win
->GetBackgroundStyle());
2061 win
->m_needsStyleChange
= false;
2064 wxWindowCreateEvent
event( win
);
2065 event
.SetEventObject( win
);
2066 win
->GTKProcessEvent( event
);
2069 //-----------------------------------------------------------------------------
2071 //-----------------------------------------------------------------------------
2074 void gtk_window_size_callback( GtkWidget
*WXUNUSED(widget
),
2075 GtkAllocation
*alloc
,
2078 int client_width
= 0;
2079 int client_height
= 0;
2080 win
->GetClientSize( &client_width
, &client_height
);
2081 if ((client_width
== win
->m_oldClientWidth
) && (client_height
== win
->m_oldClientHeight
))
2084 if ( !client_width
&& !client_height
)
2086 // the window is currently unmapped, don't generate size events
2090 win
->m_oldClientWidth
= client_width
;
2091 win
->m_oldClientHeight
= client_height
;
2093 if (!win
->m_nativeSizeEvent
)
2095 wxSizeEvent
event( win
->GetSize(), win
->GetId() );
2096 event
.SetEventObject( win
);
2097 win
->GTKProcessEvent( event
);
2101 //-----------------------------------------------------------------------------
2103 //-----------------------------------------------------------------------------
2107 gtk_window_grab_broken( GtkWidget
*m_widget
,
2108 GdkEventGrabBroken
*event
,
2111 // Mouse capture has been lost involuntarily, notify the application
2112 if( !event
->keyboard
&& win
&& wxWindow::GetCapture() == win
)
2114 wxMouseCaptureLostEvent
evt( win
->GetId() );
2115 evt
.SetEventObject( win
);
2116 win
->GetEventHandler()->ProcessEvent( evt
);
2124 // ----------------------------------------------------------------------------
2125 // this wxWindowBase function is implemented here (in platform-specific file)
2126 // because it is static and so couldn't be made virtual
2127 // ----------------------------------------------------------------------------
2129 wxWindow
*wxWindowBase::DoFindFocus()
2131 // the cast is necessary when we compile in wxUniversal mode
2132 return (wxWindow
*)g_focusWindow
;
2135 //-----------------------------------------------------------------------------
2136 // InsertChild for wxWindowGTK.
2137 //-----------------------------------------------------------------------------
2139 /* Callback for wxWindowGTK. This very strange beast has to be used because
2140 * C++ has no virtual methods in a constructor. We have to emulate a
2141 * virtual function here as wxNotebook requires a different way to insert
2142 * a child in it. I had opted for creating a wxNotebookPage window class
2143 * which would have made this superfluous (such in the MDI window system),
2144 * but no-one was listening to me... */
2146 static void wxInsertChildInWindow( wxWindowGTK
* parent
, wxWindowGTK
* child
)
2148 /* the window might have been scrolled already, do we
2149 have to adapt the position */
2150 GtkPizza
*pizza
= GTK_PIZZA(parent
->m_wxwindow
);
2151 child
->m_x
+= gtk_pizza_get_xoffset( pizza
);
2152 child
->m_y
+= gtk_pizza_get_yoffset( pizza
);
2154 gtk_pizza_put( GTK_PIZZA(parent
->m_wxwindow
),
2162 //-----------------------------------------------------------------------------
2164 //-----------------------------------------------------------------------------
2166 wxWindow
*wxGetActiveWindow()
2168 return wxWindow::FindFocus();
2172 wxMouseState
wxGetMouseState()
2178 GdkModifierType mask
;
2180 gdk_window_get_pointer(NULL
, &x
, &y
, &mask
);
2184 ms
.SetLeftDown(mask
& GDK_BUTTON1_MASK
);
2185 ms
.SetMiddleDown(mask
& GDK_BUTTON2_MASK
);
2186 ms
.SetRightDown(mask
& GDK_BUTTON3_MASK
);
2187 ms
.SetAux1Down(mask
& GDK_BUTTON4_MASK
);
2188 ms
.SetAux2Down(mask
& GDK_BUTTON5_MASK
);
2190 ms
.SetControlDown(mask
& GDK_CONTROL_MASK
);
2191 ms
.SetShiftDown(mask
& GDK_SHIFT_MASK
);
2192 ms
.SetAltDown(mask
& GDK_MOD1_MASK
);
2193 ms
.SetMetaDown(mask
& GDK_MOD2_MASK
);
2198 //-----------------------------------------------------------------------------
2200 //-----------------------------------------------------------------------------
2202 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2204 #ifdef __WXUNIVERSAL__
2205 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK
, wxWindowBase
)
2207 IMPLEMENT_DYNAMIC_CLASS(wxWindow
, wxWindowBase
)
2208 #endif // __WXUNIVERSAL__/__WXGTK__
2210 void wxWindowGTK::Init()
2213 m_widget
= (GtkWidget
*) NULL
;
2214 m_wxwindow
= (GtkWidget
*) NULL
;
2215 m_focusWidget
= (GtkWidget
*) NULL
;
2224 m_isBeingDeleted
= false;
2226 m_showOnIdle
= false;
2229 m_nativeSizeEvent
= false;
2231 m_hasScrolling
= false;
2232 m_isScrolling
= false;
2233 m_mouseButtonDown
= false;
2235 // initialize scrolling stuff
2236 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
2238 m_scrollBar
[dir
] = NULL
;
2239 m_scrollPos
[dir
] = 0;
2243 m_oldClientHeight
= 0;
2247 m_insertCallback
= wxInsertChildInWindow
;
2251 m_clipPaintRegion
= false;
2253 m_needsStyleChange
= false;
2255 m_cursor
= *wxSTANDARD_CURSOR
;
2258 m_dirtyTabOrder
= false;
2261 wxWindowGTK::wxWindowGTK()
2266 wxWindowGTK::wxWindowGTK( wxWindow
*parent
,
2271 const wxString
&name
)
2275 Create( parent
, id
, pos
, size
, style
, name
);
2278 bool wxWindowGTK::Create( wxWindow
*parent
,
2283 const wxString
&name
)
2285 if (!PreCreation( parent
, pos
, size
) ||
2286 !CreateBase( parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
))
2288 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2292 if (!HasFlag(wxHSCROLL
) && !HasFlag(wxVSCROLL
))
2294 m_wxwindow
= gtk_pizza_new_no_scroll();
2296 #ifndef __WXUNIVERSAL__
2297 if (HasFlag(wxSIMPLE_BORDER
))
2298 gtk_container_set_border_width((GtkContainer
*)m_wxwindow
, 1);
2299 else if (HasFlag(wxRAISED_BORDER
) || HasFlag(wxSUNKEN_BORDER
))
2300 gtk_container_set_border_width((GtkContainer
*)m_wxwindow
, 2);
2301 #endif // __WXUNIVERSAL__
2303 m_widget
= m_wxwindow
;
2307 m_wxwindow
= gtk_pizza_new();
2309 #ifndef __WXUNIVERSAL__
2310 if (HasFlag(wxSIMPLE_BORDER
))
2311 gtk_container_set_border_width((GtkContainer
*)m_wxwindow
, 1);
2312 else if (HasFlag(wxRAISED_BORDER
) || HasFlag(wxSUNKEN_BORDER
))
2313 gtk_container_set_border_width((GtkContainer
*)m_wxwindow
, 2);
2314 #endif // __WXUNIVERSAL__
2316 m_widget
= gtk_scrolled_window_new( (GtkAdjustment
*) NULL
, (GtkAdjustment
*) NULL
);
2318 GtkScrolledWindow
*scrolledWindow
= GTK_SCROLLED_WINDOW(m_widget
);
2320 GtkScrolledWindowClass
*scroll_class
= GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget
) );
2321 scroll_class
->scrollbar_spacing
= 0;
2323 // There is a conflict with default bindings at GTK+
2324 // level between scrolled windows and notebooks both of which want to use
2325 // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
2326 // direction and notebooks for changing pages -- we decide that if we don't
2327 // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
2328 // means we can get working keyboard navigation in notebooks
2329 if ( !HasFlag(wxHSCROLL
) )
2332 bindings
= gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget
));
2335 gtk_binding_entry_remove(bindings
, GDK_Page_Up
, GDK_CONTROL_MASK
);
2336 gtk_binding_entry_remove(bindings
, GDK_Page_Down
, GDK_CONTROL_MASK
);
2340 if (HasFlag(wxALWAYS_SHOW_SB
))
2342 gtk_scrolled_window_set_policy( scrolledWindow
, GTK_POLICY_ALWAYS
, GTK_POLICY_ALWAYS
);
2344 scrolledWindow
->hscrollbar_visible
= TRUE
;
2345 scrolledWindow
->vscrollbar_visible
= TRUE
;
2349 gtk_scrolled_window_set_policy( scrolledWindow
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2352 m_scrollBar
[ScrollDir_Horz
] = GTK_RANGE(scrolledWindow
->hscrollbar
);
2353 m_scrollBar
[ScrollDir_Vert
] = GTK_RANGE(scrolledWindow
->vscrollbar
);
2354 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2355 gtk_range_set_inverted( m_scrollBar
[ScrollDir_Horz
], TRUE
);
2357 gtk_container_add( GTK_CONTAINER(m_widget
), m_wxwindow
);
2359 // connect various scroll-related events
2360 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
2362 // these handlers block mouse events to any window during scrolling
2363 // such as motion events and prevent GTK and wxWidgets from fighting
2364 // over where the slider should be
2365 g_signal_connect(m_scrollBar
[dir
], "button_press_event",
2366 G_CALLBACK(gtk_scrollbar_button_press_event
), this);
2367 g_signal_connect(m_scrollBar
[dir
], "button_release_event",
2368 G_CALLBACK(gtk_scrollbar_button_release_event
), this);
2370 gulong handler_id
= g_signal_connect(m_scrollBar
[dir
], "event_after",
2371 G_CALLBACK(gtk_scrollbar_event_after
), this);
2372 g_signal_handler_block(m_scrollBar
[dir
], handler_id
);
2374 // these handlers get notified when scrollbar slider moves
2375 g_signal_connect_after(m_scrollBar
[dir
], "value_changed",
2376 G_CALLBACK(gtk_scrollbar_value_changed
), this);
2379 gtk_widget_show( m_wxwindow
);
2383 m_parent
->DoAddChild( this );
2385 m_focusWidget
= m_wxwindow
;
2392 wxWindowGTK::~wxWindowGTK()
2396 if (g_focusWindow
== this)
2397 g_focusWindow
= NULL
;
2399 if ( g_delayedFocus
== this )
2400 g_delayedFocus
= NULL
;
2402 m_isBeingDeleted
= true;
2405 // destroy children before destroying this window itself
2408 // unhook focus handlers to prevent stray events being
2409 // propagated to this (soon to be) dead object
2410 if (m_focusWidget
!= NULL
)
2412 g_signal_handlers_disconnect_by_func (m_focusWidget
,
2413 (gpointer
) gtk_window_focus_in_callback
,
2415 g_signal_handlers_disconnect_by_func (m_focusWidget
,
2416 (gpointer
) gtk_window_focus_out_callback
,
2423 // delete before the widgets to avoid a crash on solaris
2426 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
2428 gtk_widget_destroy( m_wxwindow
);
2429 m_wxwindow
= (GtkWidget
*) NULL
;
2434 gtk_widget_destroy( m_widget
);
2435 m_widget
= (GtkWidget
*) NULL
;
2439 bool wxWindowGTK::PreCreation( wxWindowGTK
*parent
, const wxPoint
&pos
, const wxSize
&size
)
2441 if ( GTKNeedsParent() )
2443 wxCHECK_MSG( parent
, false, wxT("Must have non-NULL parent") );
2446 // Use either the given size, or the default if -1 is given.
2447 // See wxWindowBase for these functions.
2448 m_width
= WidthDefault(size
.x
) ;
2449 m_height
= HeightDefault(size
.y
);
2457 void wxWindowGTK::PostCreation()
2459 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
2465 // these get reported to wxWidgets -> wxPaintEvent
2467 g_signal_connect (m_wxwindow
, "expose_event",
2468 G_CALLBACK (gtk_window_expose_callback
), this);
2470 if (GetLayoutDirection() == wxLayout_LeftToRight
)
2471 gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow
), HasFlag( wxFULL_REPAINT_ON_RESIZE
) );
2474 // Create input method handler
2475 m_imData
= new wxGtkIMData
;
2477 // Cannot handle drawing preedited text yet
2478 gtk_im_context_set_use_preedit( m_imData
->context
, FALSE
);
2480 g_signal_connect (m_imData
->context
, "commit",
2481 G_CALLBACK (gtk_wxwindow_commit_cb
), this);
2483 // these are called when the "sunken" or "raised" borders are drawn
2484 g_signal_connect (m_widget
, "expose_event",
2485 G_CALLBACK (gtk_window_own_expose_callback
), this);
2490 if (!GTK_IS_WINDOW(m_widget
))
2492 if (m_focusWidget
== NULL
)
2493 m_focusWidget
= m_widget
;
2497 g_signal_connect (m_focusWidget
, "focus_in_event",
2498 G_CALLBACK (gtk_window_focus_in_callback
), this);
2499 g_signal_connect (m_focusWidget
, "focus_out_event",
2500 G_CALLBACK (gtk_window_focus_out_callback
), this);
2504 g_signal_connect_after (m_focusWidget
, "focus_in_event",
2505 G_CALLBACK (gtk_window_focus_in_callback
), this);
2506 g_signal_connect_after (m_focusWidget
, "focus_out_event",
2507 G_CALLBACK (gtk_window_focus_out_callback
), this);
2511 if ( !AcceptsFocusFromKeyboard() )
2515 g_signal_connect(m_widget
, "focus",
2516 G_CALLBACK(wx_window_focus_callback
), this);
2519 // connect to the various key and mouse handlers
2521 GtkWidget
*connect_widget
= GetConnectWidget();
2523 ConnectWidget( connect_widget
);
2525 /* We cannot set colours, fonts and cursors before the widget has
2526 been realized, so we do this directly after realization */
2527 g_signal_connect (connect_widget
, "realize",
2528 G_CALLBACK (gtk_window_realized_callback
), this);
2532 // Catch native resize events
2533 g_signal_connect (m_wxwindow
, "size_allocate",
2534 G_CALLBACK (gtk_window_size_callback
), this);
2536 if (!gtk_check_version(2,8,0))
2538 // Make sure we can notify the app when mouse capture is lost
2539 g_signal_connect (m_wxwindow
, "grab_broken_event",
2540 G_CALLBACK (gtk_window_grab_broken
), this);
2545 if ( connect_widget
!= m_wxwindow
)
2548 if (!gtk_check_version(2,8,0))
2550 // Make sure we can notify app code when mouse capture is lost
2551 g_signal_connect (connect_widget
, "grab_broken_event",
2552 G_CALLBACK (gtk_window_grab_broken
), this);
2558 if (GTK_IS_COMBO(m_widget
))
2560 GtkCombo
*gcombo
= GTK_COMBO(m_widget
);
2562 g_signal_connect (gcombo
->entry
, "size_request",
2563 G_CALLBACK (wxgtk_combo_size_request_callback
),
2566 #endif // wxUSE_COMBOBOX
2567 #ifdef GTK_IS_FILE_CHOOSER_BUTTON
2568 if (!gtk_check_version(2,6,0) && GTK_IS_FILE_CHOOSER_BUTTON(m_widget
))
2570 // If we connect to the "size_request" signal of a GtkFileChooserButton
2571 // then that control won't be sized properly when placed inside sizers
2572 // (this can be tested removing this elseif and running XRC or WIDGETS samples)
2573 // FIXME: what should be done here ?
2576 if ( !IsTopLevel() ) // top level windows use their own callback
2578 // This is needed if we want to add our windows into native
2579 // GTK controls, such as the toolbar. With this callback, the
2580 // toolbar gets to know the correct size (the one set by the
2581 // programmer). Sadly, it misbehaves for wxComboBox.
2582 g_signal_connect (m_widget
, "size_request",
2583 G_CALLBACK (wxgtk_window_size_request_callback
),
2587 InheritAttributes();
2591 SetLayoutDirection(wxLayout_Default
);
2593 // unless the window was created initially hidden (i.e. Hide() had been
2594 // called before Create()), we should show it at GTK+ level as well
2596 gtk_widget_show( m_widget
);
2599 void wxWindowGTK::ConnectWidget( GtkWidget
*widget
)
2601 g_signal_connect (widget
, "key_press_event",
2602 G_CALLBACK (gtk_window_key_press_callback
), this);
2603 g_signal_connect (widget
, "key_release_event",
2604 G_CALLBACK (gtk_window_key_release_callback
), this);
2605 g_signal_connect (widget
, "button_press_event",
2606 G_CALLBACK (gtk_window_button_press_callback
), this);
2607 g_signal_connect (widget
, "button_release_event",
2608 G_CALLBACK (gtk_window_button_release_callback
), this);
2609 g_signal_connect (widget
, "motion_notify_event",
2610 G_CALLBACK (gtk_window_motion_notify_callback
), this);
2611 g_signal_connect (widget
, "scroll_event",
2612 G_CALLBACK (window_scroll_event
), this);
2613 g_signal_connect (widget
, "popup_menu",
2614 G_CALLBACK (wxgtk_window_popup_menu_callback
), this);
2615 g_signal_connect (widget
, "enter_notify_event",
2616 G_CALLBACK (gtk_window_enter_callback
), this);
2617 g_signal_connect (widget
, "leave_notify_event",
2618 G_CALLBACK (gtk_window_leave_callback
), this);
2621 bool wxWindowGTK::Destroy()
2623 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
2627 return wxWindowBase::Destroy();
2630 void wxWindowGTK::DoMoveWindow(int x
, int y
, int width
, int height
)
2632 // inform the parent to perform the move
2633 gtk_pizza_set_size( GTK_PIZZA(m_parent
->m_wxwindow
), m_widget
, x
, y
, width
, height
);
2637 void wxWindowGTK::ConstrainSize()
2640 // GPE's window manager doesn't like size hints at all, esp. when the user
2641 // has to use the virtual keyboard, so don't constrain size there
2645 const wxSize minSize
= GetMinSize();
2646 const wxSize maxSize
= GetMaxSize();
2647 if (minSize
.x
> 0 && m_width
< minSize
.x
) m_width
= minSize
.x
;
2648 if (minSize
.y
> 0 && m_height
< minSize
.y
) m_height
= minSize
.y
;
2649 if (maxSize
.x
> 0 && m_width
> maxSize
.x
) m_width
= maxSize
.x
;
2650 if (maxSize
.y
> 0 && m_height
> maxSize
.y
) m_height
= maxSize
.y
;
2654 void wxWindowGTK::DoSetSize( int x
, int y
, int width
, int height
, int sizeFlags
)
2656 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
2657 wxASSERT_MSG( (m_parent
!= NULL
), wxT("wxWindowGTK::SetSize requires parent.\n") );
2659 if (m_resizing
) return; /* I don't like recursions */
2662 int currentX
, currentY
;
2663 GetPosition(¤tX
, ¤tY
);
2664 if (x
== -1 && !(sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
2666 if (y
== -1 && !(sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
2668 AdjustForParentClientOrigin(x
, y
, sizeFlags
);
2670 // calculate the best size if we should auto size the window
2671 if ( ((sizeFlags
& wxSIZE_AUTO_WIDTH
) && width
== -1) ||
2672 ((sizeFlags
& wxSIZE_AUTO_HEIGHT
) && height
== -1) )
2674 const wxSize sizeBest
= GetBestSize();
2675 if ( (sizeFlags
& wxSIZE_AUTO_WIDTH
) && width
== -1 )
2677 if ( (sizeFlags
& wxSIZE_AUTO_HEIGHT
) && height
== -1 )
2678 height
= sizeBest
.y
;
2688 #if wxUSE_TOOLBAR_NATIVE
2689 if (wxDynamicCast(GetParent(), wxToolBar
))
2691 // don't take the x,y values, they're wrong because toolbar sets them
2692 GtkWidget
*widget
= m_widget
;
2693 gtk_widget_set_size_request (widget
, m_width
, m_height
);
2697 if (m_parent
->m_wxwindow
== NULL
) // i.e. wxNotebook
2699 // don't set the size for children of wxNotebook, just take the values.
2707 GtkPizza
*pizza
= GTK_PIZZA(m_parent
->m_wxwindow
);
2708 if ((sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
) == 0)
2710 if (x
!= -1) m_x
= x
+ gtk_pizza_get_xoffset( pizza
);
2711 if (y
!= -1) m_y
= y
+ gtk_pizza_get_yoffset( pizza
);
2715 m_x
= x
+ gtk_pizza_get_xoffset( pizza
);
2716 m_y
= y
+ gtk_pizza_get_yoffset( pizza
);
2719 int left_border
= 0;
2720 int right_border
= 0;
2722 int bottom_border
= 0;
2724 /* the default button has a border around it */
2725 if (GTK_WIDGET_CAN_DEFAULT(m_widget
))
2727 GtkBorder
*default_border
= NULL
;
2728 gtk_widget_style_get( m_widget
, "default_border", &default_border
, NULL
);
2731 left_border
+= default_border
->left
;
2732 right_border
+= default_border
->right
;
2733 top_border
+= default_border
->top
;
2734 bottom_border
+= default_border
->bottom
;
2735 gtk_border_free( default_border
);
2739 DoMoveWindow( m_x
- left_border
,
2741 m_width
+left_border
+right_border
,
2742 m_height
+top_border
+bottom_border
);
2747 /* Sometimes the client area changes size without the
2748 whole windows's size changing, but if the whole
2749 windows's size doesn't change, no wxSizeEvent will
2750 normally be sent. Here we add an extra test if
2751 the client test has been changed and this will
2753 GetClientSize( &m_oldClientWidth
, &m_oldClientHeight
);
2757 wxPrintf( "OnSize sent from " );
2758 if (GetClassInfo() && GetClassInfo()->GetClassName())
2759 wxPrintf( GetClassInfo()->GetClassName() );
2760 wxPrintf( " %d %d %d %d\n", (int)m_x, (int)m_y, (int)m_width, (int)m_height );
2763 if (!m_nativeSizeEvent
)
2765 wxSizeEvent
event( wxSize(m_width
,m_height
), GetId() );
2766 event
.SetEventObject( this );
2767 GetEventHandler()->ProcessEvent( event
);
2773 bool wxWindowGTK::GtkShowFromOnIdle()
2775 if (IsShown() && m_showOnIdle
&& !GTK_WIDGET_VISIBLE (m_widget
))
2777 GtkAllocation alloc
;
2780 alloc
.width
= m_width
;
2781 alloc
.height
= m_height
;
2782 gtk_widget_size_allocate( m_widget
, &alloc
);
2783 gtk_widget_show( m_widget
);
2784 wxShowEvent
eventShow(GetId(), true);
2785 eventShow
.SetEventObject(this);
2786 GetEventHandler()->ProcessEvent(eventShow
);
2787 m_showOnIdle
= false;
2794 void wxWindowGTK::OnInternalIdle()
2796 // Check if we have to show window now
2797 if (GtkShowFromOnIdle()) return;
2799 if ( m_dirtyTabOrder
)
2801 m_dirtyTabOrder
= false;
2805 // Update style if the window was not yet realized
2806 // and SetBackgroundStyle(wxBG_STYLE_CUSTOM) was called
2807 if (m_needsStyleChange
)
2809 SetBackgroundStyle(GetBackgroundStyle());
2810 m_needsStyleChange
= false;
2813 wxCursor cursor
= m_cursor
;
2814 if (g_globalCursor
.Ok()) cursor
= g_globalCursor
;
2818 /* I now set the cursor anew in every OnInternalIdle call
2819 as setting the cursor in a parent window also effects the
2820 windows above so that checking for the current cursor is
2823 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
2825 GdkWindow
*window
= GTK_PIZZA(m_wxwindow
)->bin_window
;
2827 gdk_window_set_cursor( window
, cursor
.GetCursor() );
2829 if (!g_globalCursor
.Ok())
2830 cursor
= *wxSTANDARD_CURSOR
;
2832 window
= m_widget
->window
;
2833 if ((window
) && !(GTK_WIDGET_NO_WINDOW(m_widget
)))
2834 gdk_window_set_cursor( window
, cursor
.GetCursor() );
2837 else if ( m_widget
)
2839 GdkWindow
*window
= m_widget
->window
;
2840 if ( window
&& !GTK_WIDGET_NO_WINDOW(m_widget
) )
2841 gdk_window_set_cursor( window
, cursor
.GetCursor() );
2845 if (wxUpdateUIEvent::CanUpdate(this))
2846 UpdateWindowUI(wxUPDATE_UI_FROMIDLE
);
2849 void wxWindowGTK::DoGetSize( int *width
, int *height
) const
2851 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2853 if (width
) (*width
) = m_width
;
2854 if (height
) (*height
) = m_height
;
2857 void wxWindowGTK::DoSetClientSize( int width
, int height
)
2859 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2861 const wxSize size
= GetSize();
2862 const wxSize clientSize
= GetClientSize();
2863 SetSize(width
+ (size
.x
- clientSize
.x
), height
+ (size
.y
- clientSize
.y
));
2866 void wxWindowGTK::DoGetClientSize( int *width
, int *height
) const
2868 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2879 GetScrollbarWidth(m_widget
, dw
, dh
);
2881 const int border
= GTK_CONTAINER(m_wxwindow
)->border_width
;
2893 if (width
) *width
= w
;
2894 if (height
) *height
= h
;
2897 void wxWindowGTK::DoGetPosition( int *x
, int *y
) const
2899 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2903 if (m_parent
&& m_parent
->m_wxwindow
)
2905 GtkPizza
*pizza
= GTK_PIZZA(m_parent
->m_wxwindow
);
2906 dx
= gtk_pizza_get_xoffset( pizza
);
2907 dy
= gtk_pizza_get_yoffset( pizza
);
2910 if (m_x
== -1 && m_y
== -1)
2912 GdkWindow
*source
= (GdkWindow
*) NULL
;
2914 source
= GTK_PIZZA(m_wxwindow
)->bin_window
;
2916 source
= m_widget
->window
;
2922 gdk_window_get_origin( source
, &org_x
, &org_y
);
2925 m_parent
->ScreenToClient(&org_x
, &org_y
);
2927 wx_const_cast(wxWindowGTK
*, this)->m_x
= org_x
;
2928 wx_const_cast(wxWindowGTK
*, this)->m_y
= org_y
;
2932 if (x
) (*x
) = m_x
- dx
;
2933 if (y
) (*y
) = m_y
- dy
;
2936 void wxWindowGTK::DoClientToScreen( int *x
, int *y
) const
2938 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2940 if (!m_widget
->window
) return;
2942 GdkWindow
*source
= (GdkWindow
*) NULL
;
2944 source
= GTK_PIZZA(m_wxwindow
)->bin_window
;
2946 source
= m_widget
->window
;
2950 gdk_window_get_origin( source
, &org_x
, &org_y
);
2954 if (GTK_WIDGET_NO_WINDOW (m_widget
))
2956 org_x
+= m_widget
->allocation
.x
;
2957 org_y
+= m_widget
->allocation
.y
;
2964 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2965 *x
= (GetClientSize().x
- *x
) + org_x
;
2973 void wxWindowGTK::DoScreenToClient( int *x
, int *y
) const
2975 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2977 if (!m_widget
->window
) return;
2979 GdkWindow
*source
= (GdkWindow
*) NULL
;
2981 source
= GTK_PIZZA(m_wxwindow
)->bin_window
;
2983 source
= m_widget
->window
;
2987 gdk_window_get_origin( source
, &org_x
, &org_y
);
2991 if (GTK_WIDGET_NO_WINDOW (m_widget
))
2993 org_x
+= m_widget
->allocation
.x
;
2994 org_y
+= m_widget
->allocation
.y
;
3000 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3001 *x
= (GetClientSize().x
- *x
) - org_x
;
3008 bool wxWindowGTK::Show( bool show
)
3010 wxCHECK_MSG( (m_widget
!= NULL
), false, wxT("invalid window") );
3012 if (!wxWindowBase::Show(show
))
3022 gtk_widget_show( m_widget
);
3023 wxShowEvent
eventShow(GetId(), show
);
3024 eventShow
.SetEventObject(this);
3025 GetEventHandler()->ProcessEvent(eventShow
);
3030 gtk_widget_hide( m_widget
);
3031 wxShowEvent
eventShow(GetId(), show
);
3032 eventShow
.SetEventObject(this);
3033 GetEventHandler()->ProcessEvent(eventShow
);
3039 void wxWindowGTK::DoEnable( bool enable
)
3041 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3043 gtk_widget_set_sensitive( m_widget
, enable
);
3044 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
3045 gtk_widget_set_sensitive( m_wxwindow
, enable
);
3048 int wxWindowGTK::GetCharHeight() const
3050 wxCHECK_MSG( (m_widget
!= NULL
), 12, wxT("invalid window") );
3052 wxFont font
= GetFont();
3053 wxCHECK_MSG( font
.Ok(), 12, wxT("invalid font") );
3055 PangoContext
*context
= NULL
;
3057 context
= gtk_widget_get_pango_context( m_widget
);
3062 PangoFontDescription
*desc
= font
.GetNativeFontInfo()->description
;
3063 PangoLayout
*layout
= pango_layout_new(context
);
3064 pango_layout_set_font_description(layout
, desc
);
3065 pango_layout_set_text(layout
, "H", 1);
3066 PangoLayoutLine
*line
= (PangoLayoutLine
*)pango_layout_get_lines(layout
)->data
;
3068 PangoRectangle rect
;
3069 pango_layout_line_get_extents(line
, NULL
, &rect
);
3071 g_object_unref (layout
);
3073 return (int) PANGO_PIXELS(rect
.height
);
3076 int wxWindowGTK::GetCharWidth() const
3078 wxCHECK_MSG( (m_widget
!= NULL
), 8, wxT("invalid window") );
3080 wxFont font
= GetFont();
3081 wxCHECK_MSG( font
.Ok(), 8, wxT("invalid font") );
3083 PangoContext
*context
= NULL
;
3085 context
= gtk_widget_get_pango_context( m_widget
);
3090 PangoFontDescription
*desc
= font
.GetNativeFontInfo()->description
;
3091 PangoLayout
*layout
= pango_layout_new(context
);
3092 pango_layout_set_font_description(layout
, desc
);
3093 pango_layout_set_text(layout
, "g", 1);
3094 PangoLayoutLine
*line
= (PangoLayoutLine
*)pango_layout_get_lines(layout
)->data
;
3096 PangoRectangle rect
;
3097 pango_layout_line_get_extents(line
, NULL
, &rect
);
3099 g_object_unref (layout
);
3101 return (int) PANGO_PIXELS(rect
.width
);
3104 void wxWindowGTK::GetTextExtent( const wxString
& string
,
3108 int *externalLeading
,
3109 const wxFont
*theFont
) const
3111 wxFont fontToUse
= theFont
? *theFont
: GetFont();
3113 wxCHECK_RET( fontToUse
.Ok(), wxT("invalid font") );
3122 PangoContext
*context
= NULL
;
3124 context
= gtk_widget_get_pango_context( m_widget
);
3133 PangoFontDescription
*desc
= fontToUse
.GetNativeFontInfo()->description
;
3134 PangoLayout
*layout
= pango_layout_new(context
);
3135 pango_layout_set_font_description(layout
, desc
);
3137 const wxCharBuffer data
= wxGTK_CONV( string
);
3139 pango_layout_set_text(layout
, data
, strlen(data
));
3142 PangoRectangle rect
;
3143 pango_layout_get_extents(layout
, NULL
, &rect
);
3145 if (x
) (*x
) = (wxCoord
) PANGO_PIXELS(rect
.width
);
3146 if (y
) (*y
) = (wxCoord
) PANGO_PIXELS(rect
.height
);
3149 PangoLayoutIter
*iter
= pango_layout_get_iter(layout
);
3150 int baseline
= pango_layout_iter_get_baseline(iter
);
3151 pango_layout_iter_free(iter
);
3152 *descent
= *y
- PANGO_PIXELS(baseline
);
3154 if (externalLeading
) (*externalLeading
) = 0; // ??
3156 g_object_unref (layout
);
3159 bool wxWindowGTK::GTKSetDelayedFocusIfNeeded()
3161 if ( g_delayedFocus
== this )
3163 if ( GTK_WIDGET_REALIZED(m_widget
) )
3165 gtk_widget_grab_focus(m_widget
);
3166 g_delayedFocus
= NULL
;
3175 void wxWindowGTK::SetFocus()
3177 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
3180 // don't do anything if we already have focus
3186 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow
))
3188 gtk_widget_grab_focus (m_wxwindow
);
3193 if (GTK_IS_CONTAINER(m_widget
))
3196 if (IsKindOf(CLASSINFO(wxRadioButton
)))
3198 gtk_widget_grab_focus (m_widget
);
3201 #endif // wxUSE_RADIOBTN
3203 gtk_widget_child_focus( m_widget
, GTK_DIR_TAB_FORWARD
);
3206 if (GTK_WIDGET_CAN_FOCUS(m_widget
) && !GTK_WIDGET_HAS_FOCUS (m_widget
) )
3209 if (!GTK_WIDGET_REALIZED(m_widget
))
3211 // we can't set the focus to the widget now so we remember that
3212 // it should be focused and will do it later, during the idle
3213 // time, as soon as we can
3214 wxLogTrace(TRACE_FOCUS
,
3215 _T("Delaying setting focus to %s(%s)"),
3216 GetClassInfo()->GetClassName(), GetLabel().c_str());
3218 g_delayedFocus
= this;
3222 wxLogTrace(TRACE_FOCUS
,
3223 _T("Setting focus to %s(%s)"),
3224 GetClassInfo()->GetClassName(), GetLabel().c_str());
3226 gtk_widget_grab_focus (m_widget
);
3231 wxLogTrace(TRACE_FOCUS
,
3232 _T("Can't set focus to %s(%s)"),
3233 GetClassInfo()->GetClassName(), GetLabel().c_str());
3238 void wxWindowGTK::SetCanFocus(bool canFocus
)
3241 GTK_WIDGET_SET_FLAGS(m_widget
, GTK_CAN_FOCUS
);
3243 GTK_WIDGET_UNSET_FLAGS(m_widget
, GTK_CAN_FOCUS
);
3245 if ( m_wxwindow
&& (m_widget
!= m_wxwindow
) )
3248 GTK_WIDGET_SET_FLAGS(m_wxwindow
, GTK_CAN_FOCUS
);
3250 GTK_WIDGET_UNSET_FLAGS(m_wxwindow
, GTK_CAN_FOCUS
);
3254 bool wxWindowGTK::Reparent( wxWindowBase
*newParentBase
)
3256 wxCHECK_MSG( (m_widget
!= NULL
), false, wxT("invalid window") );
3258 wxWindowGTK
*oldParent
= m_parent
,
3259 *newParent
= (wxWindowGTK
*)newParentBase
;
3261 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3263 if ( !wxWindowBase::Reparent(newParent
) )
3266 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3268 /* prevent GTK from deleting the widget arbitrarily */
3269 gtk_widget_ref( m_widget
);
3273 gtk_container_remove( GTK_CONTAINER(m_widget
->parent
), m_widget
);
3276 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3280 if (GTK_WIDGET_VISIBLE (newParent
->m_widget
))
3282 m_showOnIdle
= true;
3283 gtk_widget_hide( m_widget
);
3286 /* insert GTK representation */
3287 (*(newParent
->m_insertCallback
))(newParent
, this);
3290 /* reverse: prevent GTK from deleting the widget arbitrarily */
3291 gtk_widget_unref( m_widget
);
3293 SetLayoutDirection(wxLayout_Default
);
3298 void wxWindowGTK::DoAddChild(wxWindowGTK
*child
)
3300 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
3301 wxASSERT_MSG( (child
!= NULL
), wxT("invalid child window") );
3306 /* insert GTK representation */
3307 (*m_insertCallback
)(this, child
);
3310 void wxWindowGTK::AddChild(wxWindowBase
*child
)
3312 wxWindowBase::AddChild(child
);
3313 m_dirtyTabOrder
= true;
3314 wxTheApp
->WakeUpIdle();
3317 void wxWindowGTK::RemoveChild(wxWindowBase
*child
)
3319 wxWindowBase::RemoveChild(child
);
3320 m_dirtyTabOrder
= true;
3321 wxTheApp
->WakeUpIdle();
3325 wxLayoutDirection
wxWindowGTK::GTKGetLayout(GtkWidget
*widget
)
3327 return gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
3328 ? wxLayout_RightToLeft
3329 : wxLayout_LeftToRight
;
3333 void wxWindowGTK::GTKSetLayout(GtkWidget
*widget
, wxLayoutDirection dir
)
3335 wxASSERT_MSG( dir
!= wxLayout_Default
, _T("invalid layout direction") );
3337 gtk_widget_set_direction(widget
,
3338 dir
== wxLayout_RightToLeft
? GTK_TEXT_DIR_RTL
3339 : GTK_TEXT_DIR_LTR
);
3342 wxLayoutDirection
wxWindowGTK::GetLayoutDirection() const
3344 return GTKGetLayout(m_widget
);
3347 void wxWindowGTK::SetLayoutDirection(wxLayoutDirection dir
)
3349 if ( dir
== wxLayout_Default
)
3351 const wxWindow
*const parent
= GetParent();
3354 // inherit layout from parent.
3355 dir
= parent
->GetLayoutDirection();
3357 else // no parent, use global default layout
3359 dir
= wxTheApp
->GetLayoutDirection();
3363 if ( dir
== wxLayout_Default
)
3366 GTKSetLayout(m_widget
, dir
);
3368 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
3369 GTKSetLayout(m_wxwindow
, dir
);
3373 wxWindowGTK::AdjustForLayoutDirection(wxCoord x
,
3374 wxCoord
WXUNUSED(width
),
3375 wxCoord
WXUNUSED(widthTotal
)) const
3377 // We now mirrors the coordinates of RTL windows in GtkPizza
3381 void wxWindowGTK::DoMoveInTabOrder(wxWindow
*win
, MoveKind move
)
3383 wxWindowBase::DoMoveInTabOrder(win
, move
);
3384 m_dirtyTabOrder
= true;
3385 wxTheApp
->WakeUpIdle();
3388 bool wxWindowGTK::DoNavigateIn(int flags
)
3390 if ( flags
& wxNavigationKeyEvent::WinChange
)
3392 wxFAIL_MSG( _T("not implemented") );
3396 else // navigate inside the container
3398 wxWindow
*parent
= wxGetTopLevelParent((wxWindow
*)this);
3399 wxCHECK_MSG( parent
, false, _T("every window must have a TLW parent") );
3401 GtkDirectionType dir
;
3402 dir
= flags
& wxNavigationKeyEvent::IsForward
? GTK_DIR_TAB_FORWARD
3403 : GTK_DIR_TAB_BACKWARD
;
3406 g_signal_emit_by_name(parent
->m_widget
, "focus", dir
, &rc
);
3412 bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3414 // none needed by default
3418 void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget
* WXUNUSED(w
))
3420 // nothing to do by default since none is needed
3423 void wxWindowGTK::RealizeTabOrder()
3427 if ( !m_children
.empty() )
3429 // we don't only construct the correct focus chain but also use
3430 // this opportunity to update the mnemonic widgets for the widgets
3433 GList
*chain
= NULL
;
3434 wxWindowGTK
* mnemonicWindow
= NULL
;
3436 for ( wxWindowList::const_iterator i
= m_children
.begin();
3437 i
!= m_children
.end();
3440 wxWindowGTK
*win
= *i
;
3442 if ( mnemonicWindow
)
3444 if ( win
->AcceptsFocusFromKeyboard() )
3446 // wxComboBox et al. needs to focus on on a different
3447 // widget than m_widget, so if the main widget isn't
3448 // focusable try the connect widget
3449 GtkWidget
* w
= win
->m_widget
;
3450 if ( !GTK_WIDGET_CAN_FOCUS(w
) )
3452 w
= win
->GetConnectWidget();
3453 if ( !GTK_WIDGET_CAN_FOCUS(w
) )
3459 mnemonicWindow
->GTKWidgetDoSetMnemonic(w
);
3460 mnemonicWindow
= NULL
;
3464 else if ( win
->GTKWidgetNeedsMnemonic() )
3466 mnemonicWindow
= win
;
3469 chain
= g_list_prepend(chain
, win
->m_widget
);
3472 chain
= g_list_reverse(chain
);
3474 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow
), chain
);
3479 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow
));
3484 void wxWindowGTK::Raise()
3486 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3488 if (m_wxwindow
&& m_wxwindow
->window
)
3490 gdk_window_raise( m_wxwindow
->window
);
3492 else if (m_widget
->window
)
3494 gdk_window_raise( m_widget
->window
);
3498 void wxWindowGTK::Lower()
3500 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3502 if (m_wxwindow
&& m_wxwindow
->window
)
3504 gdk_window_lower( m_wxwindow
->window
);
3506 else if (m_widget
->window
)
3508 gdk_window_lower( m_widget
->window
);
3512 bool wxWindowGTK::SetCursor( const wxCursor
&cursor
)
3514 if ( !wxWindowBase::SetCursor(cursor
.Ok() ? cursor
: *wxSTANDARD_CURSOR
) )
3522 void wxWindowGTK::GTKUpdateCursor()
3524 wxCursor
cursor(g_globalCursor
.Ok() ? g_globalCursor
: GetCursor());
3527 wxArrayGdkWindows windowsThis
;
3528 GdkWindow
* const winThis
= GTKGetWindow(windowsThis
);
3531 gdk_window_set_cursor(winThis
, cursor
.GetCursor());
3535 const size_t count
= windowsThis
.size();
3536 for ( size_t n
= 0; n
< count
; n
++ )
3538 GdkWindow
*win
= windowsThis
[n
];
3541 wxFAIL_MSG(_T("NULL window returned by GTKGetWindow()?"));
3545 gdk_window_set_cursor(win
, cursor
.GetCursor());
3551 void wxWindowGTK::WarpPointer( int x
, int y
)
3553 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3555 // We provide this function ourselves as it is
3556 // missing in GDK (top of this file).
3558 GdkWindow
*window
= (GdkWindow
*) NULL
;
3560 window
= GTK_PIZZA(m_wxwindow
)->bin_window
;
3562 window
= GetConnectWidget()->window
;
3565 gdk_window_warp_pointer( window
, x
, y
);
3568 wxWindowGTK::ScrollDir
wxWindowGTK::ScrollDirFromRange(GtkRange
*range
) const
3570 // find the scrollbar which generated the event
3571 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
3573 if ( range
== m_scrollBar
[dir
] )
3574 return (ScrollDir
)dir
;
3577 wxFAIL_MSG( _T("event from unknown scrollbar received") );
3579 return ScrollDir_Max
;
3582 bool wxWindowGTK::DoScrollByUnits(ScrollDir dir
, ScrollUnit unit
, int units
)
3584 bool changed
= false;
3585 GtkRange
* range
= m_scrollBar
[dir
];
3586 if ( range
&& units
)
3588 GtkAdjustment
* adj
= range
->adjustment
;
3589 gdouble inc
= unit
== ScrollUnit_Line
? adj
->step_increment
3590 : adj
->page_increment
;
3592 const int posOld
= int(adj
->value
+ 0.5);
3593 gtk_range_set_value(range
, posOld
+ units
*inc
);
3595 changed
= int(adj
->value
+ 0.5) != posOld
;
3601 bool wxWindowGTK::ScrollLines(int lines
)
3603 return DoScrollByUnits(ScrollDir_Vert
, ScrollUnit_Line
, lines
);
3606 bool wxWindowGTK::ScrollPages(int pages
)
3608 return DoScrollByUnits(ScrollDir_Vert
, ScrollUnit_Page
, pages
);
3611 void wxWindowGTK::Refresh( bool eraseBackground
, const wxRect
*rect
)
3615 if (!m_widget
->window
)
3620 if (!GTK_PIZZA(m_wxwindow
)->bin_window
) return;
3622 GdkRectangle gdk_rect
,
3626 gdk_rect
.x
= rect
->x
;
3627 gdk_rect
.y
= rect
->y
;
3628 gdk_rect
.width
= rect
->width
;
3629 gdk_rect
.height
= rect
->height
;
3630 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3631 gdk_rect
.x
= GetClientSize().x
- gdk_rect
.x
- gdk_rect
.width
;
3635 else // invalidate everything
3640 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow
)->bin_window
, p
, TRUE
);
3644 void wxWindowGTK::Update()
3648 // when we call Update() we really want to update the window immediately on
3649 // screen, even if it means flushing the entire queue and hence slowing down
3650 // everything -- but it should still be done, it's just that Update() should
3651 // be called very rarely
3655 void wxWindowGTK::GtkUpdate()
3657 if (m_wxwindow
&& GTK_PIZZA(m_wxwindow
)->bin_window
)
3658 gdk_window_process_updates( GTK_PIZZA(m_wxwindow
)->bin_window
, FALSE
);
3659 if (m_widget
&& m_widget
->window
&& (m_wxwindow
!= m_widget
))
3660 gdk_window_process_updates( m_widget
->window
, FALSE
);
3662 // for consistency with other platforms (and also because it's convenient
3663 // to be able to update an entire TLW by calling Update() only once), we
3664 // should also update all our children here
3665 for ( wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
3667 node
= node
->GetNext() )
3669 node
->GetData()->GtkUpdate();
3673 bool wxWindowGTK::DoIsExposed( int x
, int y
) const
3675 return m_updateRegion
.Contains(x
, y
) != wxOutRegion
;
3679 bool wxWindowGTK::DoIsExposed( int x
, int y
, int w
, int h
) const
3681 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3682 return m_updateRegion
.Contains(x
-w
, y
, w
, h
) != wxOutRegion
;
3684 return m_updateRegion
.Contains(x
, y
, w
, h
) != wxOutRegion
;
3687 void wxWindowGTK::GtkSendPaintEvents()
3691 m_updateRegion
.Clear();
3695 // Clip to paint region in wxClientDC
3696 m_clipPaintRegion
= true;
3698 m_nativeUpdateRegion
= m_updateRegion
;
3700 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3702 // Transform m_updateRegion under RTL
3703 m_updateRegion
.Clear();
3706 gdk_window_get_geometry( GTK_PIZZA(m_wxwindow
)->bin_window
,
3707 NULL
, NULL
, &width
, NULL
, NULL
);
3709 wxRegionIterator
upd( m_nativeUpdateRegion
);
3713 rect
.x
= upd
.GetX();
3714 rect
.y
= upd
.GetY();
3715 rect
.width
= upd
.GetWidth();
3716 rect
.height
= upd
.GetHeight();
3718 rect
.x
= width
- rect
.x
- rect
.width
;
3719 m_updateRegion
.Union( rect
);
3725 // widget to draw on
3726 GtkPizza
*pizza
= GTK_PIZZA (m_wxwindow
);
3728 if (GetThemeEnabled() && (GetBackgroundStyle() == wxBG_STYLE_SYSTEM
))
3730 // find ancestor from which to steal background
3731 wxWindow
*parent
= wxGetTopLevelParent((wxWindow
*)this);
3733 parent
= (wxWindow
*)this;
3735 if (GTK_WIDGET_MAPPED(parent
->m_widget
))
3737 wxRegionIterator
upd( m_nativeUpdateRegion
);
3741 rect
.x
= upd
.GetX();
3742 rect
.y
= upd
.GetY();
3743 rect
.width
= upd
.GetWidth();
3744 rect
.height
= upd
.GetHeight();
3746 gtk_paint_flat_box( parent
->m_widget
->style
,
3748 (GtkStateType
)GTK_WIDGET_STATE(m_wxwindow
),
3761 wxWindowDC
dc( (wxWindow
*)this );
3762 dc
.SetClippingRegion( m_updateRegion
);
3764 wxEraseEvent
erase_event( GetId(), &dc
);
3765 erase_event
.SetEventObject( this );
3767 GetEventHandler()->ProcessEvent(erase_event
);
3770 wxNcPaintEvent
nc_paint_event( GetId() );
3771 nc_paint_event
.SetEventObject( this );
3772 GetEventHandler()->ProcessEvent( nc_paint_event
);
3775 wxPaintEvent
paint_event( GetId() );
3776 paint_event
.SetEventObject( this );
3777 GetEventHandler()->ProcessEvent( paint_event
);
3779 m_clipPaintRegion
= false;
3781 m_updateRegion
.Clear();
3782 m_nativeUpdateRegion
.Clear();
3785 void wxWindowGTK::SetDoubleBuffered( bool on
)
3787 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3790 gtk_widget_set_double_buffered( m_wxwindow
, on
);
3793 bool wxWindowGTK::IsDoubleBuffered() const
3795 return GTK_WIDGET_DOUBLE_BUFFERED( m_wxwindow
);
3798 void wxWindowGTK::ClearBackground()
3800 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
3804 void wxWindowGTK::DoSetToolTip( wxToolTip
*tip
)
3806 wxWindowBase::DoSetToolTip(tip
);
3809 m_tooltip
->Apply( (wxWindow
*)this );
3812 void wxWindowGTK::ApplyToolTip( GtkTooltips
*tips
, const gchar
*tip
)
3814 gtk_tooltips_set_tip(tips
, GetConnectWidget(), tip
, NULL
);
3816 #endif // wxUSE_TOOLTIPS
3818 bool wxWindowGTK::SetBackgroundColour( const wxColour
&colour
)
3820 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
3822 if (!wxWindowBase::SetBackgroundColour(colour
))
3827 // We need the pixel value e.g. for background clearing.
3828 m_backgroundColour
.CalcPixel(gtk_widget_get_colormap(m_widget
));
3831 // apply style change (forceStyle=true so that new style is applied
3832 // even if the bg colour changed from valid to wxNullColour)
3833 if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM
)
3834 ApplyWidgetStyle(true);
3839 bool wxWindowGTK::SetForegroundColour( const wxColour
&colour
)
3841 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
3843 if (!wxWindowBase::SetForegroundColour(colour
))
3850 // We need the pixel value e.g. for background clearing.
3851 m_foregroundColour
.CalcPixel(gtk_widget_get_colormap(m_widget
));
3854 // apply style change (forceStyle=true so that new style is applied
3855 // even if the bg colour changed from valid to wxNullColour):
3856 ApplyWidgetStyle(true);
3861 PangoContext
*wxWindowGTK::GtkGetPangoDefaultContext()
3863 return gtk_widget_get_pango_context( m_widget
);
3866 GtkRcStyle
*wxWindowGTK::CreateWidgetStyle(bool forceStyle
)
3868 // do we need to apply any changes at all?
3871 !m_foregroundColour
.Ok() && !m_backgroundColour
.Ok() )
3876 GtkRcStyle
*style
= gtk_rc_style_new();
3881 pango_font_description_copy( m_font
.GetNativeFontInfo()->description
);
3884 int flagsNormal
= 0,
3887 flagsInsensitive
= 0;
3889 if ( m_foregroundColour
.Ok() )
3891 const GdkColor
*fg
= m_foregroundColour
.GetColor();
3893 style
->fg
[GTK_STATE_NORMAL
] =
3894 style
->text
[GTK_STATE_NORMAL
] = *fg
;
3895 flagsNormal
|= GTK_RC_FG
| GTK_RC_TEXT
;
3897 style
->fg
[GTK_STATE_PRELIGHT
] =
3898 style
->text
[GTK_STATE_PRELIGHT
] = *fg
;
3899 flagsPrelight
|= GTK_RC_FG
| GTK_RC_TEXT
;
3901 style
->fg
[GTK_STATE_ACTIVE
] =
3902 style
->text
[GTK_STATE_ACTIVE
] = *fg
;
3903 flagsActive
|= GTK_RC_FG
| GTK_RC_TEXT
;
3906 if ( m_backgroundColour
.Ok() )
3908 const GdkColor
*bg
= m_backgroundColour
.GetColor();
3910 style
->bg
[GTK_STATE_NORMAL
] =
3911 style
->base
[GTK_STATE_NORMAL
] = *bg
;
3912 flagsNormal
|= GTK_RC_BG
| GTK_RC_BASE
;
3914 style
->bg
[GTK_STATE_PRELIGHT
] =
3915 style
->base
[GTK_STATE_PRELIGHT
] = *bg
;
3916 flagsPrelight
|= GTK_RC_BG
| GTK_RC_BASE
;
3918 style
->bg
[GTK_STATE_ACTIVE
] =
3919 style
->base
[GTK_STATE_ACTIVE
] = *bg
;
3920 flagsActive
|= GTK_RC_BG
| GTK_RC_BASE
;
3922 style
->bg
[GTK_STATE_INSENSITIVE
] =
3923 style
->base
[GTK_STATE_INSENSITIVE
] = *bg
;
3924 flagsInsensitive
|= GTK_RC_BG
| GTK_RC_BASE
;
3927 style
->color_flags
[GTK_STATE_NORMAL
] = (GtkRcFlags
)flagsNormal
;
3928 style
->color_flags
[GTK_STATE_PRELIGHT
] = (GtkRcFlags
)flagsPrelight
;
3929 style
->color_flags
[GTK_STATE_ACTIVE
] = (GtkRcFlags
)flagsActive
;
3930 style
->color_flags
[GTK_STATE_INSENSITIVE
] = (GtkRcFlags
)flagsInsensitive
;
3935 void wxWindowGTK::ApplyWidgetStyle(bool forceStyle
)
3937 GtkRcStyle
*style
= CreateWidgetStyle(forceStyle
);
3940 DoApplyWidgetStyle(style
);
3941 gtk_rc_style_unref(style
);
3944 // Style change may affect GTK+'s size calculation:
3945 InvalidateBestSize();
3948 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle
*style
)
3951 gtk_widget_modify_style(m_wxwindow
, style
);
3953 gtk_widget_modify_style(m_widget
, style
);
3956 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style
)
3958 wxWindowBase::SetBackgroundStyle(style
);
3960 if (style
== wxBG_STYLE_CUSTOM
)
3965 window
= GTK_PIZZA(m_wxwindow
)->bin_window
;
3969 GtkWidget
* const w
= GetConnectWidget();
3970 window
= w
? w
->window
: NULL
;
3975 // Make sure GDK/X11 doesn't refresh the window
3977 gdk_window_set_back_pixmap( window
, None
, False
);
3979 Display
* display
= GDK_WINDOW_DISPLAY(window
);
3982 m_needsStyleChange
= false;
3984 else // window not realized yet
3986 // Do in OnIdle, because the window is not yet available
3987 m_needsStyleChange
= true;
3990 // Don't apply widget style, or we get a grey background
3994 // apply style change (forceStyle=true so that new style is applied
3995 // even if the bg colour changed from valid to wxNullColour):
3996 ApplyWidgetStyle(true);
4001 #if wxUSE_DRAG_AND_DROP
4003 void wxWindowGTK::SetDropTarget( wxDropTarget
*dropTarget
)
4005 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4007 GtkWidget
*dnd_widget
= GetConnectWidget();
4009 if (m_dropTarget
) m_dropTarget
->UnregisterWidget( dnd_widget
);
4011 if (m_dropTarget
) delete m_dropTarget
;
4012 m_dropTarget
= dropTarget
;
4014 if (m_dropTarget
) m_dropTarget
->RegisterWidget( dnd_widget
);
4017 #endif // wxUSE_DRAG_AND_DROP
4019 GtkWidget
* wxWindowGTK::GetConnectWidget()
4021 GtkWidget
*connect_widget
= m_widget
;
4022 if (m_wxwindow
) connect_widget
= m_wxwindow
;
4024 return connect_widget
;
4027 bool wxWindowGTK::GTKIsOwnWindow(GdkWindow
*window
) const
4029 wxArrayGdkWindows windowsThis
;
4030 GdkWindow
* const winThis
= GTKGetWindow(windowsThis
);
4032 return winThis
? window
== winThis
4033 : windowsThis
.Index(window
) != wxNOT_FOUND
;
4036 GdkWindow
*wxWindowGTK::GTKGetWindow(wxArrayGdkWindows
& WXUNUSED(windows
)) const
4038 return m_wxwindow
? GTK_PIZZA(m_wxwindow
)->bin_window
: m_widget
->window
;
4041 bool wxWindowGTK::SetFont( const wxFont
&font
)
4043 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
4045 if (!wxWindowBase::SetFont(font
))
4048 // apply style change (forceStyle=true so that new style is applied
4049 // even if the font changed from valid to wxNullFont):
4050 ApplyWidgetStyle(true);
4055 void wxWindowGTK::DoCaptureMouse()
4057 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4059 GdkWindow
*window
= (GdkWindow
*) NULL
;
4061 window
= GTK_PIZZA(m_wxwindow
)->bin_window
;
4063 window
= GetConnectWidget()->window
;
4065 wxCHECK_RET( window
, _T("CaptureMouse() failed") );
4067 const wxCursor
* cursor
= &m_cursor
;
4069 cursor
= wxSTANDARD_CURSOR
;
4071 gdk_pointer_grab( window
, FALSE
,
4073 (GDK_BUTTON_PRESS_MASK
|
4074 GDK_BUTTON_RELEASE_MASK
|
4075 GDK_POINTER_MOTION_HINT_MASK
|
4076 GDK_POINTER_MOTION_MASK
),
4078 cursor
->GetCursor(),
4079 (guint32
)GDK_CURRENT_TIME
);
4080 g_captureWindow
= this;
4081 g_captureWindowHasMouse
= true;
4084 void wxWindowGTK::DoReleaseMouse()
4086 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4088 wxCHECK_RET( g_captureWindow
, wxT("can't release mouse - not captured") );
4090 g_captureWindow
= (wxWindowGTK
*) NULL
;
4092 GdkWindow
*window
= (GdkWindow
*) NULL
;
4094 window
= GTK_PIZZA(m_wxwindow
)->bin_window
;
4096 window
= GetConnectWidget()->window
;
4101 gdk_pointer_ungrab ( (guint32
)GDK_CURRENT_TIME
);
4104 void wxWindowGTK::GTKReleaseMouseAndNotify()
4107 wxMouseCaptureLostEvent
evt(GetId());
4108 evt
.SetEventObject( this );
4109 GetEventHandler()->ProcessEvent( evt
);
4113 wxWindow
*wxWindowBase::GetCapture()
4115 return (wxWindow
*)g_captureWindow
;
4118 bool wxWindowGTK::IsRetained() const
4123 void wxWindowGTK::SetScrollbar(int orient
,
4127 bool WXUNUSED(update
))
4129 const int dir
= ScrollDirFromOrient(orient
);
4130 GtkRange
* const sb
= m_scrollBar
[dir
];
4131 wxCHECK_RET( sb
, _T("this window is not scrollable") );
4135 m_hasScrolling
= true;
4139 // GtkRange requires upper > lower
4144 GtkAdjustment
* const adj
= sb
->adjustment
;
4145 adj
->step_increment
= 1;
4146 adj
->page_increment
=
4147 adj
->page_size
= thumbVisible
;
4150 g_signal_handlers_block_by_func(
4151 sb
, (void*)gtk_scrollbar_value_changed
, this);
4153 gtk_range_set_range(sb
, 0, range
);
4154 m_scrollPos
[dir
] = sb
->adjustment
->value
;
4156 g_signal_handlers_unblock_by_func(
4157 sb
, (void*)gtk_scrollbar_value_changed
, this);
4160 void wxWindowGTK::SetScrollPos(int orient
, int pos
, bool WXUNUSED(refresh
))
4162 const int dir
= ScrollDirFromOrient(orient
);
4163 GtkRange
* const sb
= m_scrollBar
[dir
];
4164 wxCHECK_RET( sb
, _T("this window is not scrollable") );
4166 // This check is more than an optimization. Without it, the slider
4167 // will not move smoothly while tracking when using wxScrollHelper.
4168 if (GetScrollPos(orient
) != pos
)
4170 g_signal_handlers_block_by_func(
4171 sb
, (void*)gtk_scrollbar_value_changed
, this);
4173 gtk_range_set_value(sb
, pos
);
4174 m_scrollPos
[dir
] = sb
->adjustment
->value
;
4176 g_signal_handlers_unblock_by_func(
4177 sb
, (void*)gtk_scrollbar_value_changed
, this);
4181 int wxWindowGTK::GetScrollThumb(int orient
) const
4183 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4184 wxCHECK_MSG( sb
, 0, _T("this window is not scrollable") );
4186 return int(sb
->adjustment
->page_size
);
4189 int wxWindowGTK::GetScrollPos( int orient
) const
4191 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4192 wxCHECK_MSG( sb
, 0, _T("this window is not scrollable") );
4194 return int(sb
->adjustment
->value
+ 0.5);
4197 int wxWindowGTK::GetScrollRange( int orient
) const
4199 GtkRange
* const sb
= m_scrollBar
[ScrollDirFromOrient(orient
)];
4200 wxCHECK_MSG( sb
, 0, _T("this window is not scrollable") );
4202 return int(sb
->adjustment
->upper
);
4205 // Determine if increment is the same as +/-x, allowing for some small
4206 // difference due to possible inexactness in floating point arithmetic
4207 static inline bool IsScrollIncrement(double increment
, double x
)
4209 wxASSERT(increment
> 0);
4210 const double tolerance
= 1.0 / 1024;
4211 return fabs(increment
- fabs(x
)) < tolerance
;
4214 wxEventType
wxWindowGTK::GetScrollEventType(GtkRange
* range
)
4218 wxASSERT(range
== m_scrollBar
[0] || range
== m_scrollBar
[1]);
4220 const int barIndex
= range
== m_scrollBar
[1];
4221 GtkAdjustment
* adj
= range
->adjustment
;
4223 const int value
= int(adj
->value
+ 0.5);
4225 // save previous position
4226 const double oldPos
= m_scrollPos
[barIndex
];
4227 // update current position
4228 m_scrollPos
[barIndex
] = adj
->value
;
4229 // If event should be ignored, or integral position has not changed
4230 if (!m_hasVMT
|| g_blockEventsOnDrag
|| value
== int(oldPos
+ 0.5))
4235 wxEventType eventType
= wxEVT_SCROLL_THUMBTRACK
;
4238 // Difference from last change event
4239 const double diff
= adj
->value
- oldPos
;
4240 const bool isDown
= diff
> 0;
4242 if (IsScrollIncrement(adj
->step_increment
, diff
))
4244 eventType
= isDown
? wxEVT_SCROLL_LINEDOWN
: wxEVT_SCROLL_LINEUP
;
4246 else if (IsScrollIncrement(adj
->page_increment
, diff
))
4248 eventType
= isDown
? wxEVT_SCROLL_PAGEDOWN
: wxEVT_SCROLL_PAGEUP
;
4250 else if (m_mouseButtonDown
)
4252 // Assume track event
4253 m_isScrolling
= true;
4259 void wxWindowGTK::ScrollWindow( int dx
, int dy
, const wxRect
* WXUNUSED(rect
) )
4261 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4263 wxCHECK_RET( m_wxwindow
!= NULL
, wxT("window needs client area for scrolling") );
4265 // No scrolling requested.
4266 if ((dx
== 0) && (dy
== 0)) return;
4268 m_clipPaintRegion
= true;
4270 if (GetLayoutDirection() == wxLayout_RightToLeft
)
4271 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow
), dx
, -dy
);
4273 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow
), -dx
, -dy
);
4275 m_clipPaintRegion
= false;
4278 bool restoreCaret
= (GetCaret() != NULL
&& GetCaret()->IsVisible());
4281 wxRect
caretRect(GetCaret()->GetPosition(), GetCaret()->GetSize());
4283 caretRect
.width
+= dx
;
4286 caretRect
.x
+= dx
; caretRect
.width
-= dx
;
4289 caretRect
.height
+= dy
;
4292 caretRect
.y
+= dy
; caretRect
.height
-= dy
;
4295 RefreshRect(caretRect
);
4297 #endif // wxUSE_CARET
4300 void wxWindowGTK::GtkScrolledWindowSetBorder(GtkWidget
* w
, int wxstyle
)
4302 //RN: Note that static controls usually have no border on gtk, so maybe
4303 //it makes sense to treat that as simply no border at the wx level
4305 if (!(wxstyle
& wxNO_BORDER
) && !(wxstyle
& wxBORDER_STATIC
))
4307 GtkShadowType gtkstyle
;
4309 if(wxstyle
& wxBORDER_RAISED
)
4310 gtkstyle
= GTK_SHADOW_OUT
;
4311 else if (wxstyle
& wxBORDER_SUNKEN
)
4312 gtkstyle
= GTK_SHADOW_IN
;
4313 else if (wxstyle
& wxBORDER_DOUBLE
)
4314 gtkstyle
= GTK_SHADOW_ETCHED_IN
;
4316 gtkstyle
= GTK_SHADOW_IN
;
4318 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w
),
4323 void wxWindowGTK::SetWindowStyleFlag( long style
)
4325 // Updates the internal variable. NB: Now m_windowStyle bits carry the _new_ style values already
4326 wxWindowBase::SetWindowStyleFlag(style
);
4329 // Find the wxWindow at the current mouse position, also returning the mouse
4331 wxWindow
* wxFindWindowAtPointer(wxPoint
& pt
)
4333 pt
= wxGetMousePosition();
4334 wxWindow
* found
= wxFindWindowAtPoint(pt
);
4338 // Get the current mouse position.
4339 wxPoint
wxGetMousePosition()
4341 /* This crashes when used within wxHelpContext,
4342 so we have to use the X-specific implementation below.
4344 GdkModifierType *mask;
4345 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4347 return wxPoint(x, y);
4351 GdkWindow
* windowAtPtr
= gdk_window_at_pointer(& x
, & y
);
4353 Display
*display
= windowAtPtr
? GDK_WINDOW_XDISPLAY(windowAtPtr
) : GDK_DISPLAY();
4354 Window rootWindow
= RootWindowOfScreen (DefaultScreenOfDisplay(display
));
4355 Window rootReturn
, childReturn
;
4356 int rootX
, rootY
, winX
, winY
;
4357 unsigned int maskReturn
;
4359 XQueryPointer (display
,
4363 &rootX
, &rootY
, &winX
, &winY
, &maskReturn
);
4364 return wxPoint(rootX
, rootY
);
4368 // Needed for implementing e.g. combobox on wxGTK within a modal dialog.
4369 void wxAddGrab(wxWindow
* window
)
4371 gtk_grab_add( (GtkWidget
*) window
->GetHandle() );
4374 void wxRemoveGrab(wxWindow
* window
)
4376 gtk_grab_remove( (GtkWidget
*) window
->GetHandle() );