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
;
429 void wxgtk_combo_size_request_callback(GtkWidget
*widget
,
430 GtkRequisition
*requisition
,
433 // This callback is actually hooked into the text entry
434 // of the combo box, not the GtkHBox.
437 win
->GetSize( &w
, &h
);
443 GtkCombo
*gcombo
= GTK_COMBO(win
->m_widget
);
445 GtkRequisition entry_req
;
447 entry_req
.height
= 2;
448 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(gcombo
->entry
) )->size_request
)
449 (gcombo
->entry
, &entry_req
);
451 GtkRequisition button_req
;
452 button_req
.width
= 2;
453 button_req
.height
= 2;
454 (* GTK_WIDGET_CLASS( GTK_OBJECT_GET_CLASS(gcombo
->button
) )->size_request
)
455 (gcombo
->button
, &button_req
);
457 requisition
->width
= w
- button_req
.width
;
458 requisition
->height
= entry_req
.height
;
462 //-----------------------------------------------------------------------------
463 // "expose_event" of m_wxwindow
464 //-----------------------------------------------------------------------------
468 gtk_window_expose_callback( GtkWidget
*widget
,
469 GdkEventExpose
*gdk_event
,
474 // This callback gets called in drawing-idle time under
475 // GTK 2.0, so we don't need to defer anything to idle
478 GtkPizza
*pizza
= GTK_PIZZA( widget
);
479 if (gdk_event
->window
!= pizza
->bin_window
)
481 // block expose events on GTK_WIDGET(pizza)->window,
482 // all drawing is done on pizza->bin_window
490 wxPrintf( wxT("OnExpose from ") );
491 if (win
->GetClassInfo() && win
->GetClassInfo()->GetClassName())
492 wxPrintf( win
->GetClassInfo()->GetClassName() );
493 wxPrintf( wxT(" %d %d %d %d\n"), (int)gdk_event
->area
.x
,
494 (int)gdk_event
->area
.y
,
495 (int)gdk_event
->area
.width
,
496 (int)gdk_event
->area
.height
);
501 win
->m_wxwindow
->style
,
505 (GdkRectangle
*) NULL
,
507 (char *)"button", // const_cast
512 win
->GetUpdateRegion() = wxRegion( gdk_event
->region
);
514 win
->GtkSendPaintEvents();
516 // Let parent window draw window-less widgets
521 //-----------------------------------------------------------------------------
522 // "key_press_event" from any window
523 //-----------------------------------------------------------------------------
525 // These are used when transforming Ctrl-alpha to ascii values 1-26
526 inline bool wxIsLowerChar(int code
)
528 return (code
>= 'a' && code
<= 'z' );
531 inline bool wxIsUpperChar(int code
)
533 return (code
>= 'A' && code
<= 'Z' );
537 // set WXTRACE to this to see the key event codes on the console
538 #define TRACE_KEYS _T("keyevent")
540 // translates an X key symbol to WXK_XXX value
542 // if isChar is true it means that the value returned will be used for EVT_CHAR
543 // event and then we choose the logical WXK_XXX, i.e. '/' for GDK_KP_Divide,
544 // for example, while if it is false it means that the value is going to be
545 // used for KEY_DOWN/UP events and then we translate GDK_KP_Divide to
547 static long wxTranslateKeySymToWXKey(KeySym keysym
, bool isChar
)
553 // Shift, Control and Alt don't generate the CHAR events at all
556 key_code
= isChar
? 0 : WXK_SHIFT
;
560 key_code
= isChar
? 0 : WXK_CONTROL
;
568 key_code
= isChar
? 0 : WXK_ALT
;
571 // neither do the toggle modifies
572 case GDK_Scroll_Lock
:
573 key_code
= isChar
? 0 : WXK_SCROLL
;
577 key_code
= isChar
? 0 : WXK_CAPITAL
;
581 key_code
= isChar
? 0 : WXK_NUMLOCK
;
585 // various other special keys
598 case GDK_ISO_Left_Tab
:
605 key_code
= WXK_RETURN
;
609 key_code
= WXK_CLEAR
;
613 key_code
= WXK_PAUSE
;
617 key_code
= WXK_SELECT
;
621 key_code
= WXK_PRINT
;
625 key_code
= WXK_EXECUTE
;
629 key_code
= WXK_ESCAPE
;
632 // cursor and other extended keyboard keys
634 key_code
= WXK_DELETE
;
650 key_code
= WXK_RIGHT
;
657 case GDK_Prior
: // == GDK_Page_Up
658 key_code
= WXK_PAGEUP
;
661 case GDK_Next
: // == GDK_Page_Down
662 key_code
= WXK_PAGEDOWN
;
674 key_code
= WXK_INSERT
;
689 key_code
= (isChar
? '0' : WXK_NUMPAD0
) + keysym
- GDK_KP_0
;
693 key_code
= isChar
? ' ' : WXK_NUMPAD_SPACE
;
697 key_code
= isChar
? WXK_TAB
: WXK_NUMPAD_TAB
;
701 key_code
= isChar
? WXK_RETURN
: WXK_NUMPAD_ENTER
;
705 key_code
= isChar
? WXK_F1
: WXK_NUMPAD_F1
;
709 key_code
= isChar
? WXK_F2
: WXK_NUMPAD_F2
;
713 key_code
= isChar
? WXK_F3
: WXK_NUMPAD_F3
;
717 key_code
= isChar
? WXK_F4
: WXK_NUMPAD_F4
;
721 key_code
= isChar
? WXK_HOME
: WXK_NUMPAD_HOME
;
725 key_code
= isChar
? WXK_LEFT
: WXK_NUMPAD_LEFT
;
729 key_code
= isChar
? WXK_UP
: WXK_NUMPAD_UP
;
733 key_code
= isChar
? WXK_RIGHT
: WXK_NUMPAD_RIGHT
;
737 key_code
= isChar
? WXK_DOWN
: WXK_NUMPAD_DOWN
;
740 case GDK_KP_Prior
: // == GDK_KP_Page_Up
741 key_code
= isChar
? WXK_PAGEUP
: WXK_NUMPAD_PAGEUP
;
744 case GDK_KP_Next
: // == GDK_KP_Page_Down
745 key_code
= isChar
? WXK_PAGEDOWN
: WXK_NUMPAD_PAGEDOWN
;
749 key_code
= isChar
? WXK_END
: WXK_NUMPAD_END
;
753 key_code
= isChar
? WXK_HOME
: WXK_NUMPAD_BEGIN
;
757 key_code
= isChar
? WXK_INSERT
: WXK_NUMPAD_INSERT
;
761 key_code
= isChar
? WXK_DELETE
: WXK_NUMPAD_DELETE
;
765 key_code
= isChar
? '=' : WXK_NUMPAD_EQUAL
;
768 case GDK_KP_Multiply
:
769 key_code
= isChar
? '*' : WXK_NUMPAD_MULTIPLY
;
773 key_code
= isChar
? '+' : WXK_NUMPAD_ADD
;
776 case GDK_KP_Separator
:
777 // FIXME: what is this?
778 key_code
= isChar
? '.' : WXK_NUMPAD_SEPARATOR
;
781 case GDK_KP_Subtract
:
782 key_code
= isChar
? '-' : WXK_NUMPAD_SUBTRACT
;
786 key_code
= isChar
? '.' : WXK_NUMPAD_DECIMAL
;
790 key_code
= isChar
? '/' : WXK_NUMPAD_DIVIDE
;
807 key_code
= WXK_F1
+ keysym
- GDK_F1
;
817 static inline bool wxIsAsciiKeysym(KeySym ks
)
822 static void wxFillOtherKeyEventFields(wxKeyEvent
& event
,
824 GdkEventKey
*gdk_event
)
828 GdkModifierType state
;
829 if (gdk_event
->window
)
830 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, &state
);
832 event
.SetTimestamp( gdk_event
->time
);
833 event
.SetId(win
->GetId());
834 event
.m_shiftDown
= (gdk_event
->state
& GDK_SHIFT_MASK
) != 0;
835 event
.m_controlDown
= (gdk_event
->state
& GDK_CONTROL_MASK
) != 0;
836 event
.m_altDown
= (gdk_event
->state
& GDK_MOD1_MASK
) != 0;
837 event
.m_metaDown
= (gdk_event
->state
& GDK_MOD2_MASK
) != 0;
838 event
.m_scanCode
= gdk_event
->keyval
;
839 event
.m_rawCode
= (wxUint32
) gdk_event
->keyval
;
840 event
.m_rawFlags
= 0;
842 event
.m_uniChar
= gdk_keyval_to_unicode(gdk_event
->keyval
);
844 wxGetMousePosition( &x
, &y
);
845 win
->ScreenToClient( &x
, &y
);
848 event
.SetEventObject( win
);
853 wxTranslateGTKKeyEventToWx(wxKeyEvent
& event
,
855 GdkEventKey
*gdk_event
)
857 // VZ: it seems that GDK_KEY_RELEASE event doesn't set event->string
858 // but only event->keyval which is quite useless to us, so remember
859 // the last character from GDK_KEY_PRESS and reuse it as last resort
861 // NB: should be MT-safe as we're always called from the main thread only
866 } s_lastKeyPress
= { 0, 0 };
868 KeySym keysym
= gdk_event
->keyval
;
870 wxLogTrace(TRACE_KEYS
, _T("Key %s event: keysym = %ld"),
871 event
.GetEventType() == wxEVT_KEY_UP
? _T("release")
875 long key_code
= wxTranslateKeySymToWXKey(keysym
, false /* !isChar */);
879 // do we have the translation or is it a plain ASCII character?
880 if ( (gdk_event
->length
== 1) || wxIsAsciiKeysym(keysym
) )
882 // we should use keysym if it is ASCII as X does some translations
883 // like "I pressed while Control is down" => "Ctrl-I" == "TAB"
884 // which we don't want here (but which we do use for OnChar())
885 if ( !wxIsAsciiKeysym(keysym
) )
887 keysym
= (KeySym
)gdk_event
->string
[0];
890 // we want to always get the same key code when the same key is
891 // pressed regardless of the state of the modifiers, i.e. on a
892 // standard US keyboard pressing '5' or '%' ('5' key with
893 // Shift) should result in the same key code in OnKeyDown():
894 // '5' (although OnChar() will get either '5' or '%').
896 // to do it we first translate keysym to keycode (== scan code)
897 // and then back but always using the lower register
898 Display
*dpy
= (Display
*)wxGetDisplay();
899 KeyCode keycode
= XKeysymToKeycode(dpy
, keysym
);
901 wxLogTrace(TRACE_KEYS
, _T("\t-> keycode %d"), keycode
);
903 KeySym keysymNormalized
= XKeycodeToKeysym(dpy
, keycode
, 0);
905 // use the normalized, i.e. lower register, keysym if we've
907 key_code
= keysymNormalized
? keysymNormalized
: keysym
;
909 // as explained above, we want to have lower register key codes
910 // normally but for the letter keys we want to have the upper ones
912 // NB: don't use XConvertCase() here, we want to do it for letters
914 key_code
= toupper(key_code
);
916 else // non ASCII key, what to do?
918 // by default, ignore it
921 // but if we have cached information from the last KEY_PRESS
922 if ( gdk_event
->type
== GDK_KEY_RELEASE
)
925 if ( keysym
== s_lastKeyPress
.keysym
)
927 key_code
= s_lastKeyPress
.keycode
;
932 if ( gdk_event
->type
== GDK_KEY_PRESS
)
934 // remember it to be reused for KEY_UP event later
935 s_lastKeyPress
.keysym
= keysym
;
936 s_lastKeyPress
.keycode
= key_code
;
940 wxLogTrace(TRACE_KEYS
, _T("\t-> wxKeyCode %ld"), key_code
);
942 // sending unknown key events doesn't really make sense
946 // now fill all the other fields
947 wxFillOtherKeyEventFields(event
, win
, gdk_event
);
949 event
.m_keyCode
= key_code
;
951 if ( gdk_event
->type
== GDK_KEY_PRESS
|| gdk_event
->type
== GDK_KEY_RELEASE
)
953 event
.m_uniChar
= key_code
;
963 GtkIMContext
*context
;
964 GdkEventKey
*lastKeyEvent
;
968 context
= gtk_im_multicontext_new();
973 g_object_unref (context
);
979 gtk_window_key_press_callback( GtkWidget
*widget
,
980 GdkEventKey
*gdk_event
,
987 if (g_blockEventsOnDrag
)
991 wxKeyEvent
event( wxEVT_KEY_DOWN
);
993 bool return_after_IM
= false;
995 if( wxTranslateGTKKeyEventToWx(event
, win
, gdk_event
) )
997 // Emit KEY_DOWN event
998 ret
= win
->GetEventHandler()->ProcessEvent( event
);
1002 // Return after IM processing as we cannot do
1003 // anything with it anyhow.
1004 return_after_IM
= true;
1007 // 2005.01.26 modified by Hong Jen Yee (hzysoft@sina.com.tw):
1008 // When we get a key_press event here, it could be originate
1009 // from the current widget or its child widgets. However, only the widget
1010 // with the INPUT FOCUS can generate the INITIAL key_press event. That is,
1011 // if the CURRENT widget doesn't have the FOCUS at all, this event definitely
1012 // originated from its child widgets and shouldn't be passed to IM context.
1013 // In fact, what a GTK+ IM should do is filtering keyEvents and convert them
1014 // into text input ONLY WHEN THE WIDGET HAS INPUT FOCUS. Besides, when current
1015 // widgets has both IM context and input focus, the event should be filtered
1016 // by gtk_im_context_filter_keypress().
1017 // Then, we should, according to GTK+ 2.0 API doc, return whatever it returns.
1018 if ((!ret
) && (win
->m_imData
!= NULL
) && ( wxWindow::FindFocus() == win
))
1020 // We should let GTK+ IM filter key event first. According to GTK+ 2.0 API
1021 // docs, if IM filter returns true, no further processing should be done.
1022 // we should send the key_down event anyway.
1023 bool intercepted_by_IM
= gtk_im_context_filter_keypress(win
->m_imData
->context
, gdk_event
);
1024 win
->m_imData
->lastKeyEvent
= NULL
;
1025 if (intercepted_by_IM
)
1027 wxLogTrace(TRACE_KEYS
, _T("Key event intercepted by IM"));
1032 if (return_after_IM
)
1038 wxWindowGTK
*ancestor
= win
;
1041 int command
= ancestor
->GetAcceleratorTable()->GetCommand( event
);
1044 wxCommandEvent
command_event( wxEVT_COMMAND_MENU_SELECTED
, command
);
1045 ret
= ancestor
->GetEventHandler()->ProcessEvent( command_event
);
1048 if (ancestor
->IsTopLevel())
1050 ancestor
= ancestor
->GetParent();
1053 #endif // wxUSE_ACCEL
1055 // Only send wxEVT_CHAR event if not processed yet. Thus, ALT-x
1056 // will only be sent if it is not in an accelerator table.
1060 KeySym keysym
= gdk_event
->keyval
;
1061 // Find key code for EVT_CHAR and EVT_CHAR_HOOK events
1062 key_code
= wxTranslateKeySymToWXKey(keysym
, true /* isChar */);
1065 if ( wxIsAsciiKeysym(keysym
) )
1068 key_code
= (unsigned char)keysym
;
1070 // gdk_event->string is actually deprecated
1071 else if ( gdk_event
->length
== 1 )
1073 key_code
= (unsigned char)gdk_event
->string
[0];
1079 wxLogTrace(TRACE_KEYS
, _T("Char event: %ld"), key_code
);
1081 event
.m_keyCode
= key_code
;
1083 // To conform to the docs we need to translate Ctrl-alpha
1084 // characters to values in the range 1-26.
1085 if ( event
.ControlDown() &&
1086 ( wxIsLowerChar(key_code
) || wxIsUpperChar(key_code
) ))
1088 if ( wxIsLowerChar(key_code
) )
1089 event
.m_keyCode
= key_code
- 'a' + 1;
1090 if ( wxIsUpperChar(key_code
) )
1091 event
.m_keyCode
= key_code
- 'A' + 1;
1093 event
.m_uniChar
= event
.m_keyCode
;
1097 // Implement OnCharHook by checking ancestor top level windows
1098 wxWindow
*parent
= win
;
1099 while (parent
&& !parent
->IsTopLevel())
1100 parent
= parent
->GetParent();
1103 event
.SetEventType( wxEVT_CHAR_HOOK
);
1104 ret
= parent
->GetEventHandler()->ProcessEvent( event
);
1109 event
.SetEventType(wxEVT_CHAR
);
1110 ret
= win
->GetEventHandler()->ProcessEvent( event
);
1121 gtk_wxwindow_commit_cb (GtkIMContext
*context
,
1125 wxKeyEvent
event( wxEVT_KEY_DOWN
);
1127 // take modifiers, cursor position, timestamp etc. from the last
1128 // key_press_event that was fed into Input Method:
1129 if (window
->m_imData
->lastKeyEvent
)
1131 wxFillOtherKeyEventFields(event
,
1132 window
, window
->m_imData
->lastKeyEvent
);
1135 const wxWxCharBuffer
data(wxGTK_CONV_BACK_SYS(str
));
1141 // Implement OnCharHook by checking ancestor top level windows
1142 wxWindow
*parent
= window
;
1143 while (parent
&& !parent
->IsTopLevel())
1144 parent
= parent
->GetParent();
1146 for( const wxChar
* pstr
= data
; *pstr
; pstr
++ )
1149 event
.m_uniChar
= *pstr
;
1150 // Backward compatible for ISO-8859-1
1151 event
.m_keyCode
= *pstr
< 256 ? event
.m_uniChar
: 0;
1152 wxLogTrace(TRACE_KEYS
, _T("IM sent character '%c'"), event
.m_uniChar
);
1154 event
.m_keyCode
= *pstr
;
1155 #endif // wxUSE_UNICODE
1157 // To conform to the docs we need to translate Ctrl-alpha
1158 // characters to values in the range 1-26.
1159 if ( event
.ControlDown() &&
1160 ( wxIsLowerChar(*pstr
) || wxIsUpperChar(*pstr
) ))
1162 if ( wxIsLowerChar(*pstr
) )
1163 event
.m_keyCode
= *pstr
- 'a' + 1;
1164 if ( wxIsUpperChar(*pstr
) )
1165 event
.m_keyCode
= *pstr
- 'A' + 1;
1167 event
.m_keyCode
= *pstr
- 'a' + 1;
1169 event
.m_uniChar
= event
.m_keyCode
;
1175 event
.SetEventType( wxEVT_CHAR_HOOK
);
1176 ret
= parent
->GetEventHandler()->ProcessEvent( event
);
1181 event
.SetEventType(wxEVT_CHAR
);
1182 ret
= window
->GetEventHandler()->ProcessEvent( event
);
1189 //-----------------------------------------------------------------------------
1190 // "key_release_event" from any window
1191 //-----------------------------------------------------------------------------
1195 gtk_window_key_release_callback( GtkWidget
*widget
,
1196 GdkEventKey
*gdk_event
,
1204 if (g_blockEventsOnDrag
)
1207 wxKeyEvent
event( wxEVT_KEY_UP
);
1208 if ( !wxTranslateGTKKeyEventToWx(event
, win
, gdk_event
) )
1210 // unknown key pressed, ignore (the event would be useless anyhow)
1214 return win
->GTKProcessEvent(event
);
1218 // ============================================================================
1220 // ============================================================================
1222 // ----------------------------------------------------------------------------
1223 // mouse event processing helpers
1224 // ----------------------------------------------------------------------------
1226 // init wxMouseEvent with the info from GdkEventXXX struct
1227 template<typename T
> void InitMouseEvent(wxWindowGTK
*win
,
1228 wxMouseEvent
& event
,
1231 event
.SetTimestamp( gdk_event
->time
);
1232 event
.m_shiftDown
= (gdk_event
->state
& GDK_SHIFT_MASK
);
1233 event
.m_controlDown
= (gdk_event
->state
& GDK_CONTROL_MASK
);
1234 event
.m_altDown
= (gdk_event
->state
& GDK_MOD1_MASK
);
1235 event
.m_metaDown
= (gdk_event
->state
& GDK_MOD2_MASK
);
1236 event
.m_leftDown
= (gdk_event
->state
& GDK_BUTTON1_MASK
);
1237 event
.m_middleDown
= (gdk_event
->state
& GDK_BUTTON2_MASK
);
1238 event
.m_rightDown
= (gdk_event
->state
& GDK_BUTTON3_MASK
);
1239 if (event
.GetEventType() == wxEVT_MOUSEWHEEL
)
1241 event
.m_linesPerAction
= 3;
1242 event
.m_wheelDelta
= 120;
1243 if (((GdkEventButton
*)gdk_event
)->button
== 4)
1244 event
.m_wheelRotation
= 120;
1245 else if (((GdkEventButton
*)gdk_event
)->button
== 5)
1246 event
.m_wheelRotation
= -120;
1249 wxPoint pt
= win
->GetClientAreaOrigin();
1250 event
.m_x
= (wxCoord
)gdk_event
->x
- pt
.x
;
1251 event
.m_y
= (wxCoord
)gdk_event
->y
- pt
.y
;
1253 if ((win
->m_wxwindow
) && (win
->GetLayoutDirection() == wxLayout_RightToLeft
))
1255 // origin in the upper right corner
1256 int window_width
= gtk_pizza_get_rtl_offset( GTK_PIZZA(win
->m_wxwindow
) );
1257 event
.m_x
= window_width
- event
.m_x
;
1260 event
.SetEventObject( win
);
1261 event
.SetId( win
->GetId() );
1262 event
.SetTimestamp( gdk_event
->time
);
1265 static void AdjustEventButtonState(wxMouseEvent
& event
)
1267 // GDK reports the old state of the button for a button press event, but
1268 // for compatibility with MSW and common sense we want m_leftDown be TRUE
1269 // for a LEFT_DOWN event, not FALSE, so we will invert
1270 // left/right/middleDown for the corresponding click events
1272 if ((event
.GetEventType() == wxEVT_LEFT_DOWN
) ||
1273 (event
.GetEventType() == wxEVT_LEFT_DCLICK
) ||
1274 (event
.GetEventType() == wxEVT_LEFT_UP
))
1276 event
.m_leftDown
= !event
.m_leftDown
;
1280 if ((event
.GetEventType() == wxEVT_MIDDLE_DOWN
) ||
1281 (event
.GetEventType() == wxEVT_MIDDLE_DCLICK
) ||
1282 (event
.GetEventType() == wxEVT_MIDDLE_UP
))
1284 event
.m_middleDown
= !event
.m_middleDown
;
1288 if ((event
.GetEventType() == wxEVT_RIGHT_DOWN
) ||
1289 (event
.GetEventType() == wxEVT_RIGHT_DCLICK
) ||
1290 (event
.GetEventType() == wxEVT_RIGHT_UP
))
1292 event
.m_rightDown
= !event
.m_rightDown
;
1297 // find the window to send the mouse event too
1299 wxWindowGTK
*FindWindowForMouseEvent(wxWindowGTK
*win
, wxCoord
& x
, wxCoord
& y
)
1304 if (win
->m_wxwindow
)
1306 GtkPizza
*pizza
= GTK_PIZZA(win
->m_wxwindow
);
1307 xx
+= gtk_pizza_get_xoffset( pizza
);
1308 yy
+= gtk_pizza_get_yoffset( pizza
);
1311 wxWindowList::compatibility_iterator node
= win
->GetChildren().GetFirst();
1314 wxWindowGTK
*child
= node
->GetData();
1316 node
= node
->GetNext();
1317 if (!child
->IsShown())
1320 if (child
->IsTransparentForMouse())
1322 // wxStaticBox is transparent in the box itself
1323 int xx1
= child
->m_x
;
1324 int yy1
= child
->m_y
;
1325 int xx2
= child
->m_x
+ child
->m_width
;
1326 int yy2
= child
->m_y
+ child
->m_height
;
1329 if (((xx
>= xx1
) && (xx
<= xx1
+10) && (yy
>= yy1
) && (yy
<= yy2
)) ||
1331 ((xx
>= xx2
-10) && (xx
<= xx2
) && (yy
>= yy1
) && (yy
<= yy2
)) ||
1333 ((xx
>= xx1
) && (xx
<= xx2
) && (yy
>= yy1
) && (yy
<= yy1
+10)) ||
1335 ((xx
>= xx1
) && (xx
<= xx2
) && (yy
>= yy2
-1) && (yy
<= yy2
)))
1346 if ((child
->m_wxwindow
== (GtkWidget
*) NULL
) &&
1347 (child
->m_x
<= xx
) &&
1348 (child
->m_y
<= yy
) &&
1349 (child
->m_x
+child
->m_width
>= xx
) &&
1350 (child
->m_y
+child
->m_height
>= yy
))
1363 // ----------------------------------------------------------------------------
1364 // common event handlers helpers
1365 // ----------------------------------------------------------------------------
1367 bool wxWindowGTK::GTKProcessEvent(wxEvent
& event
) const
1369 // nothing special at this level
1370 return GetEventHandler()->ProcessEvent(event
);
1373 int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny
*event
) const
1379 if (g_blockEventsOnDrag
)
1381 if (g_blockEventsOnScroll
)
1384 if (!GTKIsOwnWindow(event
->window
))
1390 // overloads for all GDK event types we use here: we need to have this as
1391 // GdkEventXXX can't be implicitly cast to GdkEventAny even if it, in fact,
1392 // derives from it in the sense that the structs have the same layout
1393 #define wxDEFINE_COMMON_PROLOGUE_OVERLOAD(T) \
1394 static int wxGtkCallbackCommonPrologue(T *event, wxWindowGTK *win) \
1396 return win->GTKCallbackCommonPrologue((GdkEventAny *)event); \
1399 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventButton
)
1400 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventMotion
)
1401 wxDEFINE_COMMON_PROLOGUE_OVERLOAD(GdkEventCrossing
)
1403 #undef wxDEFINE_COMMON_PROLOGUE_OVERLOAD
1405 #define wxCOMMON_CALLBACK_PROLOGUE(event, win) \
1406 const int rc = wxGtkCallbackCommonPrologue(event, win); \
1410 // send the wxChildFocusEvent and wxFocusEvent, common code of
1411 // gtk_window_focus_in_callback() and SetFocus()
1412 static bool DoSendFocusEvents(wxWindow
*win
)
1414 // Notify the parent keeping track of focus for the kbd navigation
1415 // purposes that we got it.
1416 wxChildFocusEvent
eventChildFocus(win
);
1417 (void)win
->GetEventHandler()->ProcessEvent(eventChildFocus
);
1419 wxFocusEvent
eventFocus(wxEVT_SET_FOCUS
, win
->GetId());
1420 eventFocus
.SetEventObject(win
);
1422 return win
->GetEventHandler()->ProcessEvent(eventFocus
);
1425 // all event handlers must have C linkage as they're called from GTK+ C code
1429 //-----------------------------------------------------------------------------
1430 // "button_press_event"
1431 //-----------------------------------------------------------------------------
1434 gtk_window_button_press_callback( GtkWidget
*widget
,
1435 GdkEventButton
*gdk_event
,
1438 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1440 g_lastButtonNumber
= gdk_event
->button
;
1442 if (win
->m_wxwindow
&& (g_focusWindow
!= win
) && win
->CanAcceptFocus())
1444 gtk_widget_grab_focus( win
->m_wxwindow
);
1447 // GDK sends surplus button down events
1448 // before a double click event. We
1449 // need to filter these out.
1450 if ((gdk_event
->type
== GDK_BUTTON_PRESS
) && (win
->m_wxwindow
))
1452 GdkEvent
*peek_event
= gdk_event_peek();
1455 if ((peek_event
->type
== GDK_2BUTTON_PRESS
) ||
1456 (peek_event
->type
== GDK_3BUTTON_PRESS
))
1458 gdk_event_free( peek_event
);
1463 gdk_event_free( peek_event
);
1468 wxEventType event_type
= wxEVT_NULL
;
1470 // GdkDisplay is a GTK+ 2.2.0 thing
1471 #if defined(__WXGTK20__) && GTK_CHECK_VERSION(2, 2, 0)
1472 if ( gdk_event
->type
== GDK_2BUTTON_PRESS
&&
1473 !gtk_check_version(2,2,0) &&
1474 gdk_event
->button
>= 1 && gdk_event
->button
<= 3 )
1476 // Reset GDK internal timestamp variables in order to disable GDK
1477 // triple click events. GDK will then next time believe no button has
1478 // been clicked just before, and send a normal button click event.
1479 GdkDisplay
* display
= gtk_widget_get_display (widget
);
1480 display
->button_click_time
[1] = 0;
1481 display
->button_click_time
[0] = 0;
1485 if (gdk_event
->button
== 1)
1487 // note that GDK generates triple click events which are not supported
1488 // by wxWidgets but still have to be passed to the app as otherwise
1489 // clicks would simply go missing
1490 switch (gdk_event
->type
)
1492 // we shouldn't get triple clicks at all for GTK2 because we
1493 // suppress them artificially using the code above but we still
1494 // should map them to something for GTK1 and not just ignore them
1495 // as this would lose clicks
1496 case GDK_3BUTTON_PRESS
: // we could also map this to DCLICK...
1497 case GDK_BUTTON_PRESS
:
1498 event_type
= wxEVT_LEFT_DOWN
;
1501 case GDK_2BUTTON_PRESS
:
1502 event_type
= wxEVT_LEFT_DCLICK
;
1506 // just to silence gcc warnings
1510 else if (gdk_event
->button
== 2)
1512 switch (gdk_event
->type
)
1514 case GDK_3BUTTON_PRESS
:
1515 case GDK_BUTTON_PRESS
:
1516 event_type
= wxEVT_MIDDLE_DOWN
;
1519 case GDK_2BUTTON_PRESS
:
1520 event_type
= wxEVT_MIDDLE_DCLICK
;
1527 else if (gdk_event
->button
== 3)
1529 switch (gdk_event
->type
)
1531 case GDK_3BUTTON_PRESS
:
1532 case GDK_BUTTON_PRESS
:
1533 event_type
= wxEVT_RIGHT_DOWN
;
1536 case GDK_2BUTTON_PRESS
:
1537 event_type
= wxEVT_RIGHT_DCLICK
;
1544 else if (gdk_event
->button
== 4 || gdk_event
->button
== 5)
1546 if (gdk_event
->type
== GDK_BUTTON_PRESS
)
1548 event_type
= wxEVT_MOUSEWHEEL
;
1552 if ( event_type
== wxEVT_NULL
)
1554 // unknown mouse button or click type
1558 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1560 wxMouseEvent
event( event_type
);
1561 InitMouseEvent( win
, event
, gdk_event
);
1563 AdjustEventButtonState(event
);
1565 // wxListBox actually gets mouse events from the item, so we need to give it
1566 // a chance to correct this
1567 win
->FixUpMouseEvent(widget
, event
.m_x
, event
.m_y
);
1569 // find the correct window to send the event to: it may be a different one
1570 // from the one which got it at GTK+ level because some controls don't have
1571 // their own X window and thus cannot get any events.
1572 if ( !g_captureWindow
)
1573 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1575 // reset the event object and id in case win changed.
1576 event
.SetEventObject( win
);
1577 event
.SetId( win
->GetId() );
1579 bool ret
= win
->GTKProcessEvent( event
);
1580 g_lastMouseEvent
= NULL
;
1584 if ((event_type
== wxEVT_LEFT_DOWN
) && !win
->IsOfStandardClass() &&
1585 (g_focusWindow
!= win
) && win
->CanAcceptFocus())
1587 gtk_widget_grab_focus( win
->m_wxwindow
);
1590 if (event_type
== wxEVT_RIGHT_DOWN
)
1592 // generate a "context menu" event: this is similar to right mouse
1593 // click under many GUIs except that it is generated differently
1594 // (right up under MSW, ctrl-click under Mac, right down here) and
1596 // (a) it's a command event and so is propagated to the parent
1597 // (b) under some ports it can be generated from kbd too
1598 // (c) it uses screen coords (because of (a))
1599 wxContextMenuEvent
evtCtx(
1602 win
->ClientToScreen(event
.GetPosition()));
1603 evtCtx
.SetEventObject(win
);
1604 return win
->GTKProcessEvent(evtCtx
);
1610 //-----------------------------------------------------------------------------
1611 // "button_release_event"
1612 //-----------------------------------------------------------------------------
1615 gtk_window_button_release_callback( GtkWidget
*widget
,
1616 GdkEventButton
*gdk_event
,
1619 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1621 g_lastButtonNumber
= 0;
1623 wxEventType event_type
= wxEVT_NULL
;
1625 switch (gdk_event
->button
)
1628 event_type
= wxEVT_LEFT_UP
;
1632 event_type
= wxEVT_MIDDLE_UP
;
1636 event_type
= wxEVT_RIGHT_UP
;
1640 // unknown button, don't process
1644 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1646 wxMouseEvent
event( event_type
);
1647 InitMouseEvent( win
, event
, gdk_event
);
1649 AdjustEventButtonState(event
);
1651 // same wxListBox hack as above
1652 win
->FixUpMouseEvent(widget
, event
.m_x
, event
.m_y
);
1654 if ( !g_captureWindow
)
1655 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1657 // reset the event object and id in case win changed.
1658 event
.SetEventObject( win
);
1659 event
.SetId( win
->GetId() );
1661 bool ret
= win
->GTKProcessEvent(event
);
1663 g_lastMouseEvent
= NULL
;
1668 //-----------------------------------------------------------------------------
1669 // "motion_notify_event"
1670 //-----------------------------------------------------------------------------
1673 gtk_window_motion_notify_callback( GtkWidget
*widget
,
1674 GdkEventMotion
*gdk_event
,
1677 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1679 if (gdk_event
->is_hint
)
1683 GdkModifierType state
;
1684 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, &state
);
1689 g_lastMouseEvent
= (GdkEvent
*) gdk_event
;
1691 wxMouseEvent
event( wxEVT_MOTION
);
1692 InitMouseEvent(win
, event
, gdk_event
);
1694 if ( g_captureWindow
)
1696 // synthesise a mouse enter or leave event if needed
1697 GdkWindow
*winUnderMouse
= gdk_window_at_pointer(NULL
, NULL
);
1698 // This seems to be necessary and actually been added to
1699 // GDK itself in version 2.0.X
1702 bool hasMouse
= winUnderMouse
== gdk_event
->window
;
1703 if ( hasMouse
!= g_captureWindowHasMouse
)
1705 // the mouse changed window
1706 g_captureWindowHasMouse
= hasMouse
;
1708 wxMouseEvent
eventM(g_captureWindowHasMouse
? wxEVT_ENTER_WINDOW
1709 : wxEVT_LEAVE_WINDOW
);
1710 InitMouseEvent(win
, eventM
, gdk_event
);
1711 eventM
.SetEventObject(win
);
1712 win
->GTKProcessEvent(eventM
);
1717 win
= FindWindowForMouseEvent(win
, event
.m_x
, event
.m_y
);
1719 // reset the event object and id in case win changed.
1720 event
.SetEventObject( win
);
1721 event
.SetId( win
->GetId() );
1724 if ( !g_captureWindow
)
1726 wxSetCursorEvent
cevent( event
.m_x
, event
.m_y
);
1727 if (win
->GTKProcessEvent( cevent
))
1729 win
->SetCursor( cevent
.GetCursor() );
1733 bool ret
= win
->GTKProcessEvent(event
);
1735 g_lastMouseEvent
= NULL
;
1740 //-----------------------------------------------------------------------------
1741 // "scroll_event", (mouse wheel event)
1742 //-----------------------------------------------------------------------------
1745 window_scroll_event(GtkWidget
*, GdkEventScroll
* gdk_event
, wxWindow
* win
)
1749 if (gdk_event
->direction
!= GDK_SCROLL_UP
&&
1750 gdk_event
->direction
!= GDK_SCROLL_DOWN
)
1755 wxMouseEvent
event(wxEVT_MOUSEWHEEL
);
1756 // Can't use InitMouse macro because scroll events don't have button
1757 event
.SetTimestamp( gdk_event
->time
);
1758 event
.m_shiftDown
= (gdk_event
->state
& GDK_SHIFT_MASK
);
1759 event
.m_controlDown
= (gdk_event
->state
& GDK_CONTROL_MASK
);
1760 event
.m_altDown
= (gdk_event
->state
& GDK_MOD1_MASK
);
1761 event
.m_metaDown
= (gdk_event
->state
& GDK_MOD2_MASK
);
1762 event
.m_leftDown
= (gdk_event
->state
& GDK_BUTTON1_MASK
);
1763 event
.m_middleDown
= (gdk_event
->state
& GDK_BUTTON2_MASK
);
1764 event
.m_rightDown
= (gdk_event
->state
& GDK_BUTTON3_MASK
);
1765 event
.m_linesPerAction
= 3;
1766 event
.m_wheelDelta
= 120;
1767 if (gdk_event
->direction
== GDK_SCROLL_UP
)
1768 event
.m_wheelRotation
= 120;
1770 event
.m_wheelRotation
= -120;
1772 wxPoint pt
= win
->GetClientAreaOrigin();
1773 event
.m_x
= (wxCoord
)gdk_event
->x
- pt
.x
;
1774 event
.m_y
= (wxCoord
)gdk_event
->y
- pt
.y
;
1776 event
.SetEventObject( win
);
1777 event
.SetId( win
->GetId() );
1778 event
.SetTimestamp( gdk_event
->time
);
1780 return win
->GTKProcessEvent(event
);
1783 //-----------------------------------------------------------------------------
1785 //-----------------------------------------------------------------------------
1787 static gboolean
wxgtk_window_popup_menu_callback(GtkWidget
*, wxWindowGTK
* win
)
1789 wxContextMenuEvent
event(wxEVT_CONTEXT_MENU
, win
->GetId(), wxPoint(-1, -1));
1790 event
.SetEventObject(win
);
1791 return win
->GTKProcessEvent(event
);
1794 //-----------------------------------------------------------------------------
1796 //-----------------------------------------------------------------------------
1799 gtk_window_focus_in_callback( GtkWidget
*widget
,
1800 GdkEventFocus
*WXUNUSED(event
),
1806 gtk_im_context_focus_in(win
->m_imData
->context
);
1809 g_focusWindow
= win
;
1811 wxLogTrace(TRACE_FOCUS
,
1812 _T("%s: focus in"), win
->GetName().c_str());
1815 // caret needs to be informed about focus change
1816 wxCaret
*caret
= win
->GetCaret();
1819 caret
->OnSetFocus();
1821 #endif // wxUSE_CARET
1823 gboolean ret
= FALSE
;
1825 // does the window itself think that it has the focus?
1826 if ( !win
->m_hasFocus
)
1828 // not yet, notify it
1829 win
->m_hasFocus
= true;
1831 (void)DoSendFocusEvents(win
);
1836 // Disable default focus handling for custom windows
1837 // since the default GTK+ handler issues a repaint
1838 if (win
->m_wxwindow
)
1844 //-----------------------------------------------------------------------------
1845 // "focus_out_event"
1846 //-----------------------------------------------------------------------------
1849 gtk_window_focus_out_callback( GtkWidget
*widget
,
1850 GdkEventFocus
*gdk_event
,
1856 gtk_im_context_focus_out(win
->m_imData
->context
);
1858 wxLogTrace( TRACE_FOCUS
,
1859 _T("%s: focus out"), win
->GetName().c_str() );
1862 wxWindowGTK
*winFocus
= wxFindFocusedChild(win
);
1866 g_focusWindow
= (wxWindowGTK
*)NULL
;
1869 // caret needs to be informed about focus change
1870 wxCaret
*caret
= win
->GetCaret();
1873 caret
->OnKillFocus();
1875 #endif // wxUSE_CARET
1877 // don't send the window a kill focus event if it thinks that it doesn't
1878 // have focus already
1879 if ( win
->m_hasFocus
)
1881 // the event handler might delete the window when it loses focus, so
1882 // check whether this is a custom window before calling it
1883 const bool has_wxwindow
= win
->m_wxwindow
!= NULL
;
1885 win
->m_hasFocus
= false;
1887 wxFocusEvent
event( wxEVT_KILL_FOCUS
, win
->GetId() );
1888 event
.SetEventObject( win
);
1890 (void)win
->GTKProcessEvent( event
);
1892 // Disable default focus handling for custom windows
1893 // since the default GTK+ handler issues a repaint
1898 // continue with normal processing
1903 wx_window_focus_callback(GtkWidget
*widget
,
1904 GtkDirectionType direction
,
1907 // the default handler for focus signal in GtkPizza (or, rather, in
1908 // GtkScrolledWindow from which GtkPizza inherits this behaviour) sets
1909 // focus to the window itself even if it doesn't accept focus, i.e. has no
1910 // GTK_CAN_FOCUS in its style -- work around this by forcibly preventing
1911 // the signal from reaching gtk_scrolled_window_focus() if we don't have
1912 // any children which might accept focus (we know we don't accept the focus
1913 // ourselves as this signal is only connected in this case)
1914 if ( win
->GetChildren().empty() )
1915 g_signal_stop_emission_by_name(widget
, "focus");
1917 // we didn't change the focus
1921 //-----------------------------------------------------------------------------
1922 // "enter_notify_event"
1923 //-----------------------------------------------------------------------------
1926 gtk_window_enter_callback( GtkWidget
*widget
,
1927 GdkEventCrossing
*gdk_event
,
1930 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1932 // Event was emitted after a grab
1933 if (gdk_event
->mode
!= GDK_CROSSING_NORMAL
) return FALSE
;
1937 GdkModifierType state
= (GdkModifierType
)0;
1939 gdk_window_get_pointer( widget
->window
, &x
, &y
, &state
);
1941 wxMouseEvent
event( wxEVT_ENTER_WINDOW
);
1942 InitMouseEvent(win
, event
, gdk_event
);
1943 wxPoint pt
= win
->GetClientAreaOrigin();
1944 event
.m_x
= x
+ pt
.x
;
1945 event
.m_y
= y
+ pt
.y
;
1947 if ( !g_captureWindow
)
1949 wxSetCursorEvent
cevent( event
.m_x
, event
.m_y
);
1950 if (win
->GTKProcessEvent( cevent
))
1952 win
->SetCursor( cevent
.GetCursor() );
1956 return win
->GTKProcessEvent(event
);
1959 //-----------------------------------------------------------------------------
1960 // "leave_notify_event"
1961 //-----------------------------------------------------------------------------
1964 gtk_window_leave_callback( GtkWidget
*widget
,
1965 GdkEventCrossing
*gdk_event
,
1968 wxCOMMON_CALLBACK_PROLOGUE(gdk_event
, win
);
1970 // Event was emitted after an ungrab
1971 if (gdk_event
->mode
!= GDK_CROSSING_NORMAL
) return FALSE
;
1973 wxMouseEvent
event( wxEVT_LEAVE_WINDOW
);
1974 event
.SetTimestamp( gdk_event
->time
);
1975 event
.SetEventObject( win
);
1979 GdkModifierType state
= (GdkModifierType
)0;
1981 gdk_window_get_pointer( widget
->window
, &x
, &y
, &state
);
1983 event
.m_shiftDown
= (state
& GDK_SHIFT_MASK
) != 0;
1984 event
.m_controlDown
= (state
& GDK_CONTROL_MASK
) != 0;
1985 event
.m_altDown
= (state
& GDK_MOD1_MASK
) != 0;
1986 event
.m_metaDown
= (state
& GDK_MOD2_MASK
) != 0;
1987 event
.m_leftDown
= (state
& GDK_BUTTON1_MASK
) != 0;
1988 event
.m_middleDown
= (state
& GDK_BUTTON2_MASK
) != 0;
1989 event
.m_rightDown
= (state
& GDK_BUTTON3_MASK
) != 0;
1991 wxPoint pt
= win
->GetClientAreaOrigin();
1992 event
.m_x
= x
+ pt
.x
;
1993 event
.m_y
= y
+ pt
.y
;
1995 return win
->GTKProcessEvent(event
);
1998 //-----------------------------------------------------------------------------
1999 // "value_changed" from scrollbar
2000 //-----------------------------------------------------------------------------
2003 gtk_scrollbar_value_changed(GtkRange
* range
, wxWindow
* win
)
2005 wxEventType eventType
= win
->GetScrollEventType(range
);
2006 if (eventType
!= wxEVT_NULL
)
2008 // Convert scroll event type to scrollwin event type
2009 eventType
+= wxEVT_SCROLLWIN_TOP
- wxEVT_SCROLL_TOP
;
2011 // find the scrollbar which generated the event
2012 wxWindowGTK::ScrollDir dir
= win
->ScrollDirFromRange(range
);
2014 // generate the corresponding wx event
2015 const int orient
= wxWindow::OrientFromScrollDir(dir
);
2016 wxScrollWinEvent
event(eventType
, win
->GetScrollPos(orient
), orient
);
2017 event
.SetEventObject(win
);
2019 win
->m_blockValueChanged
[dir
] = true;
2020 win
->GTKProcessEvent(event
);
2021 win
->m_blockValueChanged
[dir
] = false;
2025 //-----------------------------------------------------------------------------
2026 // "button_press_event" from scrollbar
2027 //-----------------------------------------------------------------------------
2030 gtk_scrollbar_button_press_event(GtkRange
*, GdkEventButton
*, wxWindow
* win
)
2034 g_blockEventsOnScroll
= true;
2035 win
->m_mouseButtonDown
= true;
2040 //-----------------------------------------------------------------------------
2041 // "event_after" from scrollbar
2042 //-----------------------------------------------------------------------------
2045 gtk_scrollbar_event_after(GtkRange
* range
, GdkEvent
* event
, wxWindow
* win
)
2047 if (event
->type
== GDK_BUTTON_RELEASE
)
2049 g_signal_handlers_block_by_func(range
, (void*)gtk_scrollbar_event_after
, win
);
2051 const int orient
= wxWindow::OrientFromScrollDir(
2052 win
->ScrollDirFromRange(range
));
2053 wxScrollWinEvent
event(wxEVT_SCROLLWIN_THUMBRELEASE
, win
->GetScrollPos(orient
), orient
);
2054 event
.SetEventObject(win
);
2055 win
->GTKProcessEvent(event
);
2059 //-----------------------------------------------------------------------------
2060 // "button_release_event" from scrollbar
2061 //-----------------------------------------------------------------------------
2064 gtk_scrollbar_button_release_event(GtkRange
* range
, GdkEventButton
*, wxWindow
* win
)
2068 g_blockEventsOnScroll
= false;
2069 win
->m_mouseButtonDown
= false;
2070 // If thumb tracking
2071 if (win
->m_isScrolling
)
2073 win
->m_isScrolling
= false;
2074 // Hook up handler to send thumb release event after this emission is finished.
2075 // To allow setting scroll position from event handler, sending event must
2076 // be deferred until after the GtkRange handler for this signal has run
2077 g_signal_handlers_unblock_by_func(range
, (void*)gtk_scrollbar_event_after
, win
);
2083 //-----------------------------------------------------------------------------
2084 // "realize" from m_widget
2085 //-----------------------------------------------------------------------------
2088 gtk_window_realized_callback( GtkWidget
*m_widget
, wxWindow
*win
)
2094 GtkPizza
*pizza
= GTK_PIZZA( m_widget
);
2095 gtk_im_context_set_client_window( win
->m_imData
->context
,
2096 pizza
->bin_window
);
2099 // We cannot set colours and fonts before the widget
2100 // been realized, so we do this directly after realization
2101 // or otherwise in idle time
2103 if (win
->m_needsStyleChange
)
2105 win
->SetBackgroundStyle(win
->GetBackgroundStyle());
2106 win
->m_needsStyleChange
= false;
2109 wxWindowCreateEvent
event( win
);
2110 event
.SetEventObject( win
);
2111 win
->GTKProcessEvent( event
);
2114 //-----------------------------------------------------------------------------
2116 //-----------------------------------------------------------------------------
2119 void gtk_window_size_callback( GtkWidget
*WXUNUSED(widget
),
2120 GtkAllocation
*alloc
,
2123 int client_width
= 0;
2124 int client_height
= 0;
2125 win
->GetClientSize( &client_width
, &client_height
);
2126 if ((client_width
== win
->m_oldClientWidth
) && (client_height
== win
->m_oldClientHeight
))
2129 if ( !client_width
&& !client_height
)
2131 // the window is currently unmapped, don't generate size events
2135 win
->m_oldClientWidth
= client_width
;
2136 win
->m_oldClientHeight
= client_height
;
2138 if (!win
->m_nativeSizeEvent
)
2140 wxSizeEvent
event( win
->GetSize(), win
->GetId() );
2141 event
.SetEventObject( win
);
2142 win
->GTKProcessEvent( event
);
2148 // ----------------------------------------------------------------------------
2149 // this wxWindowBase function is implemented here (in platform-specific file)
2150 // because it is static and so couldn't be made virtual
2151 // ----------------------------------------------------------------------------
2153 wxWindow
*wxWindowBase::DoFindFocus()
2155 // the cast is necessary when we compile in wxUniversal mode
2156 return (wxWindow
*)g_focusWindow
;
2159 //-----------------------------------------------------------------------------
2160 // InsertChild for wxWindowGTK.
2161 //-----------------------------------------------------------------------------
2163 /* Callback for wxWindowGTK. This very strange beast has to be used because
2164 * C++ has no virtual methods in a constructor. We have to emulate a
2165 * virtual function here as wxNotebook requires a different way to insert
2166 * a child in it. I had opted for creating a wxNotebookPage window class
2167 * which would have made this superfluous (such in the MDI window system),
2168 * but no-one was listening to me... */
2170 static void wxInsertChildInWindow( wxWindowGTK
* parent
, wxWindowGTK
* child
)
2172 /* the window might have been scrolled already, do we
2173 have to adapt the position */
2174 GtkPizza
*pizza
= GTK_PIZZA(parent
->m_wxwindow
);
2175 child
->m_x
+= gtk_pizza_get_xoffset( pizza
);
2176 child
->m_y
+= gtk_pizza_get_yoffset( pizza
);
2178 gtk_pizza_put( GTK_PIZZA(parent
->m_wxwindow
),
2186 //-----------------------------------------------------------------------------
2188 //-----------------------------------------------------------------------------
2190 wxWindow
*wxGetActiveWindow()
2192 return wxWindow::FindFocus();
2196 wxMouseState
wxGetMouseState()
2202 GdkModifierType mask
;
2204 gdk_window_get_pointer(NULL
, &x
, &y
, &mask
);
2208 ms
.SetLeftDown(mask
& GDK_BUTTON1_MASK
);
2209 ms
.SetMiddleDown(mask
& GDK_BUTTON2_MASK
);
2210 ms
.SetRightDown(mask
& GDK_BUTTON3_MASK
);
2212 ms
.SetControlDown(mask
& GDK_CONTROL_MASK
);
2213 ms
.SetShiftDown(mask
& GDK_SHIFT_MASK
);
2214 ms
.SetAltDown(mask
& GDK_MOD1_MASK
);
2215 ms
.SetMetaDown(mask
& GDK_MOD2_MASK
);
2220 //-----------------------------------------------------------------------------
2222 //-----------------------------------------------------------------------------
2224 // in wxUniv/MSW this class is abstract because it doesn't have DoPopupMenu()
2226 #ifdef __WXUNIVERSAL__
2227 IMPLEMENT_ABSTRACT_CLASS(wxWindowGTK
, wxWindowBase
)
2229 IMPLEMENT_DYNAMIC_CLASS(wxWindow
, wxWindowBase
)
2230 #endif // __WXUNIVERSAL__/__WXGTK__
2232 void wxWindowGTK::Init()
2235 m_widget
= (GtkWidget
*) NULL
;
2236 m_wxwindow
= (GtkWidget
*) NULL
;
2237 m_focusWidget
= (GtkWidget
*) NULL
;
2247 m_isBeingDeleted
= false;
2249 m_showOnIdle
= false;
2252 m_nativeSizeEvent
= false;
2254 m_hasScrolling
= false;
2255 m_isScrolling
= false;
2256 m_mouseButtonDown
= false;
2257 m_blockScrollEvent
= false;
2259 // initialize scrolling stuff
2260 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
2262 m_scrollBar
[dir
] = NULL
;
2263 m_scrollPos
[dir
] = 0;
2264 m_blockValueChanged
[dir
] = false;
2268 m_oldClientHeight
= 0;
2272 m_insertCallback
= (wxInsertChildFunction
) NULL
;
2276 m_clipPaintRegion
= false;
2278 m_needsStyleChange
= false;
2280 m_cursor
= *wxSTANDARD_CURSOR
;
2283 m_dirtyTabOrder
= false;
2286 wxWindowGTK::wxWindowGTK()
2291 wxWindowGTK::wxWindowGTK( wxWindow
*parent
,
2296 const wxString
&name
)
2300 Create( parent
, id
, pos
, size
, style
, name
);
2303 bool wxWindowGTK::Create( wxWindow
*parent
,
2308 const wxString
&name
)
2310 if (!PreCreation( parent
, pos
, size
) ||
2311 !CreateBase( parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
))
2313 wxFAIL_MSG( wxT("wxWindowGTK creation failed") );
2317 m_insertCallback
= wxInsertChildInWindow
;
2320 if (!HasFlag(wxHSCROLL
) && !HasFlag(wxVSCROLL
))
2322 m_wxwindow
= gtk_pizza_new_no_scroll();
2324 #ifndef __WXUNIVERSAL__
2325 if (HasFlag(wxSIMPLE_BORDER
))
2326 gtk_container_set_border_width((GtkContainer
*)m_wxwindow
, 1);
2327 else if (HasFlag(wxRAISED_BORDER
) || HasFlag(wxSUNKEN_BORDER
))
2328 gtk_container_set_border_width((GtkContainer
*)m_wxwindow
, 2);
2329 #endif // __WXUNIVERSAL__
2331 m_widget
= m_wxwindow
;
2335 m_wxwindow
= gtk_pizza_new();
2337 #ifndef __WXUNIVERSAL__
2338 if (HasFlag(wxSIMPLE_BORDER
))
2339 gtk_container_set_border_width((GtkContainer
*)m_wxwindow
, 1);
2340 else if (HasFlag(wxRAISED_BORDER
) || HasFlag(wxSUNKEN_BORDER
))
2341 gtk_container_set_border_width((GtkContainer
*)m_wxwindow
, 2);
2342 #endif // __WXUNIVERSAL__
2344 m_widget
= gtk_scrolled_window_new( (GtkAdjustment
*) NULL
, (GtkAdjustment
*) NULL
);
2346 GtkScrolledWindow
*scrolledWindow
= GTK_SCROLLED_WINDOW(m_widget
);
2348 GtkScrolledWindowClass
*scroll_class
= GTK_SCROLLED_WINDOW_CLASS( GTK_OBJECT_GET_CLASS(m_widget
) );
2349 scroll_class
->scrollbar_spacing
= 0;
2351 // There is a conflict with default bindings at GTK+
2352 // level between scrolled windows and notebooks both of which want to use
2353 // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
2354 // direction and notebooks for changing pages -- we decide that if we don't
2355 // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
2356 // means we can get working keyboard navigation in notebooks
2357 if ( !HasFlag(wxHSCROLL
) )
2360 bindings
= gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget
));
2363 gtk_binding_entry_remove(bindings
, GDK_Page_Up
, GDK_CONTROL_MASK
);
2364 gtk_binding_entry_remove(bindings
, GDK_Page_Down
, GDK_CONTROL_MASK
);
2368 if (HasFlag(wxALWAYS_SHOW_SB
))
2370 gtk_scrolled_window_set_policy( scrolledWindow
, GTK_POLICY_ALWAYS
, GTK_POLICY_ALWAYS
);
2372 scrolledWindow
->hscrollbar_visible
= TRUE
;
2373 scrolledWindow
->vscrollbar_visible
= TRUE
;
2377 gtk_scrolled_window_set_policy( scrolledWindow
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
2380 m_scrollBar
[ScrollDir_Horz
] = GTK_RANGE(scrolledWindow
->hscrollbar
);
2381 m_scrollBar
[ScrollDir_Vert
] = GTK_RANGE(scrolledWindow
->vscrollbar
);
2382 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2383 gtk_range_set_inverted( m_scrollBar
[ScrollDir_Horz
], TRUE
);
2385 gtk_container_add( GTK_CONTAINER(m_widget
), m_wxwindow
);
2387 // connect various scroll-related events
2388 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
2390 // these handlers block mouse events to any window during scrolling
2391 // such as motion events and prevent GTK and wxWidgets from fighting
2392 // over where the slider should be
2393 g_signal_connect(m_scrollBar
[dir
], "button_press_event",
2394 G_CALLBACK(gtk_scrollbar_button_press_event
), this);
2395 g_signal_connect(m_scrollBar
[dir
], "button_release_event",
2396 G_CALLBACK(gtk_scrollbar_button_release_event
), this);
2398 gulong handler_id
= g_signal_connect(m_scrollBar
[dir
], "event_after",
2399 G_CALLBACK(gtk_scrollbar_event_after
), this);
2400 g_signal_handler_block(m_scrollBar
[dir
], handler_id
);
2402 // these handlers get notified when scrollbar slider moves
2403 g_signal_connect(m_scrollBar
[dir
], "value_changed",
2404 G_CALLBACK(gtk_scrollbar_value_changed
), this);
2407 gtk_widget_show( m_wxwindow
);
2411 m_parent
->DoAddChild( this );
2413 m_focusWidget
= m_wxwindow
;
2420 wxWindowGTK::~wxWindowGTK()
2424 if (g_focusWindow
== this)
2425 g_focusWindow
= NULL
;
2427 if ( g_delayedFocus
== this )
2428 g_delayedFocus
= NULL
;
2430 m_isBeingDeleted
= true;
2433 // destroy children before destroying this window itself
2436 // unhook focus handlers to prevent stray events being
2437 // propagated to this (soon to be) dead object
2438 if (m_focusWidget
!= NULL
)
2440 g_signal_handlers_disconnect_by_func (m_focusWidget
,
2441 (gpointer
) gtk_window_focus_in_callback
,
2443 g_signal_handlers_disconnect_by_func (m_focusWidget
,
2444 (gpointer
) gtk_window_focus_out_callback
,
2451 // delete before the widgets to avoid a crash on solaris
2454 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
2456 gtk_widget_destroy( m_wxwindow
);
2457 m_wxwindow
= (GtkWidget
*) NULL
;
2462 gtk_widget_destroy( m_widget
);
2463 m_widget
= (GtkWidget
*) NULL
;
2467 bool wxWindowGTK::PreCreation( wxWindowGTK
*parent
, const wxPoint
&pos
, const wxSize
&size
)
2469 if ( GTKNeedsParent() )
2471 wxCHECK_MSG( parent
, false, wxT("Must have non-NULL parent") );
2474 // Use either the given size, or the default if -1 is given.
2475 // See wxWindowBase for these functions.
2476 m_width
= WidthDefault(size
.x
) ;
2477 m_height
= HeightDefault(size
.y
);
2485 void wxWindowGTK::PostCreation()
2487 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
2493 // these get reported to wxWidgets -> wxPaintEvent
2495 g_signal_connect (m_wxwindow
, "expose_event",
2496 G_CALLBACK (gtk_window_expose_callback
), this);
2498 if (GetLayoutDirection() == wxLayout_LeftToRight
)
2499 gtk_widget_set_redraw_on_allocate( GTK_WIDGET(m_wxwindow
), HasFlag( wxFULL_REPAINT_ON_RESIZE
) );
2502 // Create input method handler
2503 m_imData
= new wxGtkIMData
;
2505 // Cannot handle drawing preedited text yet
2506 gtk_im_context_set_use_preedit( m_imData
->context
, FALSE
);
2508 g_signal_connect (m_imData
->context
, "commit",
2509 G_CALLBACK (gtk_wxwindow_commit_cb
), this);
2511 // these are called when the "sunken" or "raised" borders are drawn
2512 g_signal_connect (m_widget
, "expose_event",
2513 G_CALLBACK (gtk_window_own_expose_callback
), this);
2518 if (!GTK_IS_WINDOW(m_widget
))
2520 if (m_focusWidget
== NULL
)
2521 m_focusWidget
= m_widget
;
2525 g_signal_connect (m_focusWidget
, "focus_in_event",
2526 G_CALLBACK (gtk_window_focus_in_callback
), this);
2527 g_signal_connect (m_focusWidget
, "focus_out_event",
2528 G_CALLBACK (gtk_window_focus_out_callback
), this);
2532 g_signal_connect_after (m_focusWidget
, "focus_in_event",
2533 G_CALLBACK (gtk_window_focus_in_callback
), this);
2534 g_signal_connect_after (m_focusWidget
, "focus_out_event",
2535 G_CALLBACK (gtk_window_focus_out_callback
), this);
2539 if ( !AcceptsFocusFromKeyboard() )
2543 g_signal_connect(m_widget
, "focus",
2544 G_CALLBACK(wx_window_focus_callback
), this);
2547 // connect to the various key and mouse handlers
2549 GtkWidget
*connect_widget
= GetConnectWidget();
2551 ConnectWidget( connect_widget
);
2553 /* We cannot set colours, fonts and cursors before the widget has
2554 been realized, so we do this directly after realization */
2555 g_signal_connect (connect_widget
, "realize",
2556 G_CALLBACK (gtk_window_realized_callback
), this);
2560 // Catch native resize events
2561 g_signal_connect (m_wxwindow
, "size_allocate",
2562 G_CALLBACK (gtk_window_size_callback
), this);
2565 if (GTK_IS_COMBO(m_widget
))
2567 GtkCombo
*gcombo
= GTK_COMBO(m_widget
);
2569 g_signal_connect (gcombo
->entry
, "size_request",
2570 G_CALLBACK (wxgtk_combo_size_request_callback
),
2573 #ifdef GTK_IS_FILE_CHOOSER_BUTTON
2574 else if (!gtk_check_version(2,6,0) && GTK_IS_FILE_CHOOSER_BUTTON(m_widget
))
2576 // If we connect to the "size_request" signal of a GtkFileChooserButton
2577 // then that control won't be sized properly when placed inside sizers
2578 // (this can be tested removing this elseif and running XRC or WIDGETS samples)
2579 // FIXME: what should be done here ?
2582 else if ( !IsTopLevel() ) // top level windows use their own callback
2584 // This is needed if we want to add our windows into native
2585 // GTK controls, such as the toolbar. With this callback, the
2586 // toolbar gets to know the correct size (the one set by the
2587 // programmer). Sadly, it misbehaves for wxComboBox.
2588 g_signal_connect (m_widget
, "size_request",
2589 G_CALLBACK (wxgtk_window_size_request_callback
),
2593 InheritAttributes();
2597 SetLayoutDirection(wxLayout_Default
);
2599 // unless the window was created initially hidden (i.e. Hide() had been
2600 // called before Create()), we should show it at GTK+ level as well
2602 gtk_widget_show( m_widget
);
2605 void wxWindowGTK::ConnectWidget( GtkWidget
*widget
)
2607 g_signal_connect (widget
, "key_press_event",
2608 G_CALLBACK (gtk_window_key_press_callback
), this);
2609 g_signal_connect (widget
, "key_release_event",
2610 G_CALLBACK (gtk_window_key_release_callback
), this);
2611 g_signal_connect (widget
, "button_press_event",
2612 G_CALLBACK (gtk_window_button_press_callback
), this);
2613 g_signal_connect (widget
, "button_release_event",
2614 G_CALLBACK (gtk_window_button_release_callback
), this);
2615 g_signal_connect (widget
, "motion_notify_event",
2616 G_CALLBACK (gtk_window_motion_notify_callback
), this);
2617 g_signal_connect (widget
, "scroll_event",
2618 G_CALLBACK (window_scroll_event
), this);
2619 g_signal_connect (widget
, "popup_menu",
2620 G_CALLBACK (wxgtk_window_popup_menu_callback
), this);
2621 g_signal_connect (widget
, "enter_notify_event",
2622 G_CALLBACK (gtk_window_enter_callback
), this);
2623 g_signal_connect (widget
, "leave_notify_event",
2624 G_CALLBACK (gtk_window_leave_callback
), this);
2627 bool wxWindowGTK::Destroy()
2629 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
2633 return wxWindowBase::Destroy();
2636 void wxWindowGTK::DoMoveWindow(int x
, int y
, int width
, int height
)
2638 // inform the parent to perform the move
2639 gtk_pizza_set_size( GTK_PIZZA(m_parent
->m_wxwindow
), m_widget
, x
, y
, width
, height
);
2643 void wxWindowGTK::ConstrainSize()
2646 // GPE's window manager doesn't like size hints at all, esp. when the user
2647 // has to use the virtual keyboard, so don't constrain size there
2651 const wxSize minSize
= GetMinSize();
2652 const wxSize maxSize
= GetMaxSize();
2653 if (minSize
.x
> 0 && m_width
< minSize
.x
) m_width
= minSize
.x
;
2654 if (minSize
.y
> 0 && m_height
< minSize
.y
) m_height
= minSize
.y
;
2655 if (maxSize
.x
> 0 && m_width
> maxSize
.x
) m_width
= maxSize
.x
;
2656 if (maxSize
.y
> 0 && m_height
> maxSize
.y
) m_height
= maxSize
.y
;
2660 void wxWindowGTK::DoSetSize( int x
, int y
, int width
, int height
, int sizeFlags
)
2662 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
2663 wxASSERT_MSG( (m_parent
!= NULL
), wxT("wxWindowGTK::SetSize requires parent.\n") );
2665 if (m_resizing
) return; /* I don't like recursions */
2668 int currentX
, currentY
;
2669 GetPosition(¤tX
, ¤tY
);
2670 if (x
== -1 && !(sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
2672 if (y
== -1 && !(sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
))
2674 AdjustForParentClientOrigin(x
, y
, sizeFlags
);
2676 // calculate the best size if we should auto size the window
2677 if ( ((sizeFlags
& wxSIZE_AUTO_WIDTH
) && width
== -1) ||
2678 ((sizeFlags
& wxSIZE_AUTO_HEIGHT
) && height
== -1) )
2680 const wxSize sizeBest
= GetBestSize();
2681 if ( (sizeFlags
& wxSIZE_AUTO_WIDTH
) && width
== -1 )
2683 if ( (sizeFlags
& wxSIZE_AUTO_HEIGHT
) && height
== -1 )
2684 height
= sizeBest
.y
;
2694 #if wxUSE_TOOLBAR_NATIVE
2695 if (wxDynamicCast(GetParent(), wxToolBar
))
2697 // don't take the x,y values, they're wrong because toolbar sets them
2698 GtkWidget
*widget
= m_widget
;
2699 gtk_widget_set_size_request (widget
, m_width
, m_height
);
2703 if (m_parent
->m_wxwindow
== NULL
) // i.e. wxNotebook
2705 // don't set the size for children of wxNotebook, just take the values.
2713 GtkPizza
*pizza
= GTK_PIZZA(m_parent
->m_wxwindow
);
2714 if ((sizeFlags
& wxSIZE_ALLOW_MINUS_ONE
) == 0)
2716 if (x
!= -1) m_x
= x
+ gtk_pizza_get_xoffset( pizza
);
2717 if (y
!= -1) m_y
= y
+ gtk_pizza_get_yoffset( pizza
);
2721 m_x
= x
+ gtk_pizza_get_xoffset( pizza
);
2722 m_y
= y
+ gtk_pizza_get_yoffset( pizza
);
2725 int left_border
= 0;
2726 int right_border
= 0;
2728 int bottom_border
= 0;
2730 /* the default button has a border around it */
2731 if (GTK_WIDGET_CAN_DEFAULT(m_widget
))
2733 GtkBorder
*default_border
= NULL
;
2734 gtk_widget_style_get( m_widget
, "default_border", &default_border
, NULL
);
2737 left_border
+= default_border
->left
;
2738 right_border
+= default_border
->right
;
2739 top_border
+= default_border
->top
;
2740 bottom_border
+= default_border
->bottom
;
2741 g_free( default_border
);
2745 DoMoveWindow( m_x
-top_border
,
2747 m_width
+left_border
+right_border
,
2748 m_height
+top_border
+bottom_border
);
2753 /* Sometimes the client area changes size without the
2754 whole windows's size changing, but if the whole
2755 windows's size doesn't change, no wxSizeEvent will
2756 normally be sent. Here we add an extra test if
2757 the client test has been changed and this will
2759 GetClientSize( &m_oldClientWidth
, &m_oldClientHeight
);
2763 wxPrintf( "OnSize sent from " );
2764 if (GetClassInfo() && GetClassInfo()->GetClassName())
2765 wxPrintf( GetClassInfo()->GetClassName() );
2766 wxPrintf( " %d %d %d %d\n", (int)m_x, (int)m_y, (int)m_width, (int)m_height );
2769 if (!m_nativeSizeEvent
)
2771 wxSizeEvent
event( wxSize(m_width
,m_height
), GetId() );
2772 event
.SetEventObject( this );
2773 GetEventHandler()->ProcessEvent( event
);
2779 bool wxWindowGTK::GtkShowFromOnIdle()
2781 if (IsShown() && m_showOnIdle
&& !GTK_WIDGET_VISIBLE (m_widget
))
2783 GtkAllocation alloc
;
2786 alloc
.width
= m_width
;
2787 alloc
.height
= m_height
;
2788 gtk_widget_size_allocate( m_widget
, &alloc
);
2789 gtk_widget_show( m_widget
);
2790 wxShowEvent
eventShow(GetId(), true);
2791 eventShow
.SetEventObject(this);
2792 GetEventHandler()->ProcessEvent(eventShow
);
2793 m_showOnIdle
= false;
2800 void wxWindowGTK::OnInternalIdle()
2802 // Check if we have to show window now
2803 if (GtkShowFromOnIdle()) return;
2805 if ( m_dirtyTabOrder
)
2807 m_dirtyTabOrder
= false;
2811 // Update style if the window was not yet realized
2812 // and SetBackgroundStyle(wxBG_STYLE_CUSTOM) was called
2813 if (m_needsStyleChange
)
2815 SetBackgroundStyle(GetBackgroundStyle());
2816 m_needsStyleChange
= false;
2819 wxCursor cursor
= m_cursor
;
2820 if (g_globalCursor
.Ok()) cursor
= g_globalCursor
;
2824 /* I now set the cursor anew in every OnInternalIdle call
2825 as setting the cursor in a parent window also effects the
2826 windows above so that checking for the current cursor is
2829 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
2831 GdkWindow
*window
= GTK_PIZZA(m_wxwindow
)->bin_window
;
2833 gdk_window_set_cursor( window
, cursor
.GetCursor() );
2835 if (!g_globalCursor
.Ok())
2836 cursor
= *wxSTANDARD_CURSOR
;
2838 window
= m_widget
->window
;
2839 if ((window
) && !(GTK_WIDGET_NO_WINDOW(m_widget
)))
2840 gdk_window_set_cursor( window
, cursor
.GetCursor() );
2843 else if ( m_widget
)
2845 GdkWindow
*window
= m_widget
->window
;
2846 if ( window
&& !GTK_WIDGET_NO_WINDOW(m_widget
) )
2847 gdk_window_set_cursor( window
, cursor
.GetCursor() );
2851 if (wxUpdateUIEvent::CanUpdate(this))
2852 UpdateWindowUI(wxUPDATE_UI_FROMIDLE
);
2855 void wxWindowGTK::DoGetSize( int *width
, int *height
) const
2857 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2859 if (width
) (*width
) = m_width
;
2860 if (height
) (*height
) = m_height
;
2863 void wxWindowGTK::DoSetClientSize( int width
, int height
)
2865 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2874 GetScrollbarWidth(m_widget
, dw
, dh
);
2877 const int border
= GTK_CONTAINER(m_wxwindow
)->border_width
;
2885 SetSize(width
, height
);
2888 void wxWindowGTK::DoGetClientSize( int *width
, int *height
) const
2890 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2901 GetScrollbarWidth(m_widget
, dw
, dh
);
2903 const int border
= GTK_CONTAINER(m_wxwindow
)->border_width
;
2915 if (width
) *width
= w
;
2916 if (height
) *height
= h
;
2919 void wxWindowGTK::DoGetPosition( int *x
, int *y
) const
2921 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2925 if (m_parent
&& m_parent
->m_wxwindow
)
2927 GtkPizza
*pizza
= GTK_PIZZA(m_parent
->m_wxwindow
);
2928 dx
= gtk_pizza_get_xoffset( pizza
);
2929 dy
= gtk_pizza_get_yoffset( pizza
);
2932 if (m_x
== -1 && m_y
== -1)
2934 GdkWindow
*source
= (GdkWindow
*) NULL
;
2936 source
= GTK_PIZZA(m_wxwindow
)->bin_window
;
2938 source
= m_widget
->window
;
2944 gdk_window_get_origin( source
, &org_x
, &org_y
);
2947 m_parent
->ScreenToClient(&org_x
, &org_y
);
2949 wx_const_cast(wxWindowGTK
*, this)->m_x
= org_x
;
2950 wx_const_cast(wxWindowGTK
*, this)->m_y
= org_y
;
2954 if (x
) (*x
) = m_x
- dx
;
2955 if (y
) (*y
) = m_y
- dy
;
2958 void wxWindowGTK::DoClientToScreen( int *x
, int *y
) const
2960 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2962 if (!m_widget
->window
) return;
2964 GdkWindow
*source
= (GdkWindow
*) NULL
;
2966 source
= GTK_PIZZA(m_wxwindow
)->bin_window
;
2968 source
= m_widget
->window
;
2972 gdk_window_get_origin( source
, &org_x
, &org_y
);
2976 if (GTK_WIDGET_NO_WINDOW (m_widget
))
2978 org_x
+= m_widget
->allocation
.x
;
2979 org_y
+= m_widget
->allocation
.y
;
2986 if (GetLayoutDirection() == wxLayout_RightToLeft
)
2987 *x
= (GetClientSize().x
- *x
) + org_x
;
2995 void wxWindowGTK::DoScreenToClient( int *x
, int *y
) const
2997 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
2999 if (!m_widget
->window
) return;
3001 GdkWindow
*source
= (GdkWindow
*) NULL
;
3003 source
= GTK_PIZZA(m_wxwindow
)->bin_window
;
3005 source
= m_widget
->window
;
3009 gdk_window_get_origin( source
, &org_x
, &org_y
);
3013 if (GTK_WIDGET_NO_WINDOW (m_widget
))
3015 org_x
+= m_widget
->allocation
.x
;
3016 org_y
+= m_widget
->allocation
.y
;
3022 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3023 *x
= (GetClientSize().x
- *x
) - org_x
;
3030 bool wxWindowGTK::Show( bool show
)
3032 wxCHECK_MSG( (m_widget
!= NULL
), false, wxT("invalid window") );
3034 if (!wxWindowBase::Show(show
))
3044 gtk_widget_show( m_widget
);
3045 wxShowEvent
eventShow(GetId(), show
);
3046 eventShow
.SetEventObject(this);
3047 GetEventHandler()->ProcessEvent(eventShow
);
3052 gtk_widget_hide( m_widget
);
3053 wxShowEvent
eventShow(GetId(), show
);
3054 eventShow
.SetEventObject(this);
3055 GetEventHandler()->ProcessEvent(eventShow
);
3061 void wxWindowGTK::DoEnable( bool enable
)
3063 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3065 gtk_widget_set_sensitive( m_widget
, enable
);
3066 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
3067 gtk_widget_set_sensitive( m_wxwindow
, enable
);
3070 int wxWindowGTK::GetCharHeight() const
3072 wxCHECK_MSG( (m_widget
!= NULL
), 12, wxT("invalid window") );
3074 wxFont font
= GetFont();
3075 wxCHECK_MSG( font
.Ok(), 12, wxT("invalid font") );
3077 PangoContext
*context
= NULL
;
3079 context
= gtk_widget_get_pango_context( m_widget
);
3084 PangoFontDescription
*desc
= font
.GetNativeFontInfo()->description
;
3085 PangoLayout
*layout
= pango_layout_new(context
);
3086 pango_layout_set_font_description(layout
, desc
);
3087 pango_layout_set_text(layout
, "H", 1);
3088 PangoLayoutLine
*line
= (PangoLayoutLine
*)pango_layout_get_lines(layout
)->data
;
3090 PangoRectangle rect
;
3091 pango_layout_line_get_extents(line
, NULL
, &rect
);
3093 g_object_unref (layout
);
3095 return (int) PANGO_PIXELS(rect
.height
);
3098 int wxWindowGTK::GetCharWidth() const
3100 wxCHECK_MSG( (m_widget
!= NULL
), 8, wxT("invalid window") );
3102 wxFont font
= GetFont();
3103 wxCHECK_MSG( font
.Ok(), 8, wxT("invalid font") );
3105 PangoContext
*context
= NULL
;
3107 context
= gtk_widget_get_pango_context( m_widget
);
3112 PangoFontDescription
*desc
= font
.GetNativeFontInfo()->description
;
3113 PangoLayout
*layout
= pango_layout_new(context
);
3114 pango_layout_set_font_description(layout
, desc
);
3115 pango_layout_set_text(layout
, "g", 1);
3116 PangoLayoutLine
*line
= (PangoLayoutLine
*)pango_layout_get_lines(layout
)->data
;
3118 PangoRectangle rect
;
3119 pango_layout_line_get_extents(line
, NULL
, &rect
);
3121 g_object_unref (layout
);
3123 return (int) PANGO_PIXELS(rect
.width
);
3126 void wxWindowGTK::GetTextExtent( const wxString
& string
,
3130 int *externalLeading
,
3131 const wxFont
*theFont
) const
3133 wxFont fontToUse
= theFont
? *theFont
: GetFont();
3135 wxCHECK_RET( fontToUse
.Ok(), wxT("invalid font") );
3144 PangoContext
*context
= NULL
;
3146 context
= gtk_widget_get_pango_context( m_widget
);
3155 PangoFontDescription
*desc
= fontToUse
.GetNativeFontInfo()->description
;
3156 PangoLayout
*layout
= pango_layout_new(context
);
3157 pango_layout_set_font_description(layout
, desc
);
3159 const wxCharBuffer data
= wxGTK_CONV( string
);
3161 pango_layout_set_text(layout
, data
, strlen(data
));
3164 PangoRectangle rect
;
3165 pango_layout_get_extents(layout
, NULL
, &rect
);
3167 if (x
) (*x
) = (wxCoord
) PANGO_PIXELS(rect
.width
);
3168 if (y
) (*y
) = (wxCoord
) PANGO_PIXELS(rect
.height
);
3171 PangoLayoutIter
*iter
= pango_layout_get_iter(layout
);
3172 int baseline
= pango_layout_iter_get_baseline(iter
);
3173 pango_layout_iter_free(iter
);
3174 *descent
= *y
- PANGO_PIXELS(baseline
);
3176 if (externalLeading
) (*externalLeading
) = 0; // ??
3178 g_object_unref (layout
);
3181 bool wxWindowGTK::GTKSetDelayedFocusIfNeeded()
3183 if ( g_delayedFocus
== this )
3185 if ( GTK_WIDGET_REALIZED(m_widget
) )
3187 gtk_widget_grab_focus(m_widget
);
3188 g_delayedFocus
= NULL
;
3197 void wxWindowGTK::SetFocus()
3199 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
3202 // don't do anything if we already have focus
3208 if (!GTK_WIDGET_HAS_FOCUS (m_wxwindow
))
3210 gtk_widget_grab_focus (m_wxwindow
);
3215 if (GTK_IS_CONTAINER(m_widget
))
3217 if (IsKindOf(CLASSINFO(wxRadioButton
)))
3219 gtk_widget_grab_focus (m_widget
);
3223 gtk_widget_child_focus( m_widget
, GTK_DIR_TAB_FORWARD
);
3226 if (GTK_WIDGET_CAN_FOCUS(m_widget
) && !GTK_WIDGET_HAS_FOCUS (m_widget
) )
3229 if (!GTK_WIDGET_REALIZED(m_widget
))
3231 // we can't set the focus to the widget now so we remember that
3232 // it should be focused and will do it later, during the idle
3233 // time, as soon as we can
3234 wxLogTrace(TRACE_FOCUS
,
3235 _T("Delaying setting focus to %s(%s)"),
3236 GetClassInfo()->GetClassName(), GetLabel().c_str());
3238 g_delayedFocus
= this;
3242 wxLogTrace(TRACE_FOCUS
,
3243 _T("Setting focus to %s(%s)"),
3244 GetClassInfo()->GetClassName(), GetLabel().c_str());
3246 gtk_widget_grab_focus (m_widget
);
3251 wxLogTrace(TRACE_FOCUS
,
3252 _T("Can't set focus to %s(%s)"),
3253 GetClassInfo()->GetClassName(), GetLabel().c_str());
3258 void wxWindowGTK::SetCanFocus(bool canFocus
)
3261 GTK_WIDGET_SET_FLAGS(m_widget
, GTK_CAN_FOCUS
);
3263 GTK_WIDGET_UNSET_FLAGS(m_widget
, GTK_CAN_FOCUS
);
3265 if ( m_wxwindow
&& (m_widget
!= m_wxwindow
) )
3268 GTK_WIDGET_SET_FLAGS(m_wxwindow
, GTK_CAN_FOCUS
);
3270 GTK_WIDGET_UNSET_FLAGS(m_wxwindow
, GTK_CAN_FOCUS
);
3274 bool wxWindowGTK::Reparent( wxWindowBase
*newParentBase
)
3276 wxCHECK_MSG( (m_widget
!= NULL
), false, wxT("invalid window") );
3278 wxWindowGTK
*oldParent
= m_parent
,
3279 *newParent
= (wxWindowGTK
*)newParentBase
;
3281 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3283 if ( !wxWindowBase::Reparent(newParent
) )
3286 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3288 /* prevent GTK from deleting the widget arbitrarily */
3289 gtk_widget_ref( m_widget
);
3293 gtk_container_remove( GTK_CONTAINER(m_widget
->parent
), m_widget
);
3296 wxASSERT( GTK_IS_WIDGET(m_widget
) );
3300 if (GTK_WIDGET_VISIBLE (newParent
->m_widget
))
3302 m_showOnIdle
= true;
3303 gtk_widget_hide( m_widget
);
3306 /* insert GTK representation */
3307 (*(newParent
->m_insertCallback
))(newParent
, this);
3310 /* reverse: prevent GTK from deleting the widget arbitrarily */
3311 gtk_widget_unref( m_widget
);
3313 SetLayoutDirection(wxLayout_Default
);
3318 void wxWindowGTK::DoAddChild(wxWindowGTK
*child
)
3320 wxASSERT_MSG( (m_widget
!= NULL
), wxT("invalid window") );
3322 wxASSERT_MSG( (child
!= NULL
), wxT("invalid child window") );
3324 wxASSERT_MSG( (m_insertCallback
!= NULL
), wxT("invalid child insertion function") );
3329 /* insert GTK representation */
3330 (*m_insertCallback
)(this, child
);
3333 void wxWindowGTK::AddChild(wxWindowBase
*child
)
3335 wxWindowBase::AddChild(child
);
3336 m_dirtyTabOrder
= true;
3337 wxTheApp
->WakeUpIdle();
3340 void wxWindowGTK::RemoveChild(wxWindowBase
*child
)
3342 wxWindowBase::RemoveChild(child
);
3343 m_dirtyTabOrder
= true;
3344 wxTheApp
->WakeUpIdle();
3348 wxLayoutDirection
wxWindowGTK::GTKGetLayout(GtkWidget
*widget
)
3350 return gtk_widget_get_direction(widget
) == GTK_TEXT_DIR_RTL
3351 ? wxLayout_RightToLeft
3352 : wxLayout_LeftToRight
;
3356 void wxWindowGTK::GTKSetLayout(GtkWidget
*widget
, wxLayoutDirection dir
)
3358 wxASSERT_MSG( dir
!= wxLayout_Default
, _T("invalid layout direction") );
3360 gtk_widget_set_direction(widget
,
3361 dir
== wxLayout_RightToLeft
? GTK_TEXT_DIR_RTL
3362 : GTK_TEXT_DIR_LTR
);
3365 wxLayoutDirection
wxWindowGTK::GetLayoutDirection() const
3367 return GTKGetLayout(m_widget
);
3370 void wxWindowGTK::SetLayoutDirection(wxLayoutDirection dir
)
3372 if ( dir
== wxLayout_Default
)
3374 const wxWindow
*const parent
= GetParent();
3377 // inherit layout from parent.
3378 dir
= parent
->GetLayoutDirection();
3380 else // no parent, use global default layout
3382 dir
= wxTheApp
->GetLayoutDirection();
3386 if ( dir
== wxLayout_Default
)
3389 GTKSetLayout(m_widget
, dir
);
3391 if (m_wxwindow
&& (m_wxwindow
!= m_widget
))
3392 GTKSetLayout(m_wxwindow
, dir
);
3396 wxWindowGTK::AdjustForLayoutDirection(wxCoord x
,
3397 wxCoord
WXUNUSED(width
),
3398 wxCoord
WXUNUSED(widthTotal
)) const
3400 // We now mirrors the coordinates of RTL windows in GtkPizza
3404 void wxWindowGTK::DoMoveInTabOrder(wxWindow
*win
, MoveKind move
)
3406 wxWindowBase::DoMoveInTabOrder(win
, move
);
3407 m_dirtyTabOrder
= true;
3408 wxTheApp
->WakeUpIdle();
3411 bool wxWindowGTK::DoNavigateIn(int flags
)
3413 if ( flags
& wxNavigationKeyEvent::WinChange
)
3415 wxFAIL_MSG( _T("not implemented") );
3419 else // navigate inside the container
3421 wxWindow
*parent
= wxGetTopLevelParent(this);
3422 wxCHECK_MSG( parent
, false, _T("every window must have a TLW parent") );
3424 GtkDirectionType dir
;
3425 dir
= flags
& wxNavigationKeyEvent::IsForward
? GTK_DIR_TAB_FORWARD
3426 : GTK_DIR_TAB_BACKWARD
;
3429 g_signal_emit_by_name(parent
->m_widget
, "focus", dir
, &rc
);
3435 bool wxWindowGTK::GTKWidgetNeedsMnemonic() const
3437 // none needed by default
3441 void wxWindowGTK::GTKWidgetDoSetMnemonic(GtkWidget
* WXUNUSED(w
))
3443 // nothing to do by default since none is needed
3446 void wxWindowGTK::RealizeTabOrder()
3450 if ( !m_children
.empty() )
3452 // we don't only construct the correct focus chain but also use
3453 // this opportunity to update the mnemonic widgets for the widgets
3456 GList
*chain
= NULL
;
3457 wxWindowGTK
* mnemonicWindow
= NULL
;
3459 for ( wxWindowList::const_iterator i
= m_children
.begin();
3460 i
!= m_children
.end();
3463 wxWindowGTK
*win
= *i
;
3465 if ( mnemonicWindow
)
3467 if ( win
->AcceptsFocusFromKeyboard() )
3469 // wxComboBox et al. needs to focus on on a different
3470 // widget than m_widget, so if the main widget isn't
3471 // focusable try the connect widget
3472 GtkWidget
* w
= win
->m_widget
;
3473 if ( !GTK_WIDGET_CAN_FOCUS(w
) )
3475 w
= win
->GetConnectWidget();
3476 if ( !GTK_WIDGET_CAN_FOCUS(w
) )
3482 mnemonicWindow
->GTKWidgetDoSetMnemonic(w
);
3483 mnemonicWindow
= NULL
;
3487 else if ( win
->GTKWidgetNeedsMnemonic() )
3489 mnemonicWindow
= win
;
3492 chain
= g_list_prepend(chain
, win
->m_widget
);
3495 chain
= g_list_reverse(chain
);
3497 gtk_container_set_focus_chain(GTK_CONTAINER(m_wxwindow
), chain
);
3502 gtk_container_unset_focus_chain(GTK_CONTAINER(m_wxwindow
));
3507 void wxWindowGTK::Raise()
3509 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3511 if (m_wxwindow
&& m_wxwindow
->window
)
3513 gdk_window_raise( m_wxwindow
->window
);
3515 else if (m_widget
->window
)
3517 gdk_window_raise( m_widget
->window
);
3521 void wxWindowGTK::Lower()
3523 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3525 if (m_wxwindow
&& m_wxwindow
->window
)
3527 gdk_window_lower( m_wxwindow
->window
);
3529 else if (m_widget
->window
)
3531 gdk_window_lower( m_widget
->window
);
3535 bool wxWindowGTK::SetCursor( const wxCursor
&cursor
)
3537 if ( !wxWindowBase::SetCursor(cursor
.Ok() ? cursor
: *wxSTANDARD_CURSOR
) )
3545 void wxWindowGTK::GTKUpdateCursor()
3547 wxCursor
cursor(g_globalCursor
.Ok() ? g_globalCursor
: GetCursor());
3550 wxArrayGdkWindows windowsThis
;
3551 GdkWindow
* const winThis
= GTKGetWindow(windowsThis
);
3554 gdk_window_set_cursor(winThis
, cursor
.GetCursor());
3558 const size_t count
= windowsThis
.size();
3559 for ( size_t n
= 0; n
< count
; n
++ )
3561 GdkWindow
*win
= windowsThis
[n
];
3564 wxFAIL_MSG(_T("NULL window returned by GTKGetWindow()?"));
3568 gdk_window_set_cursor(win
, cursor
.GetCursor());
3574 void wxWindowGTK::WarpPointer( int x
, int y
)
3576 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3578 // We provide this function ourselves as it is
3579 // missing in GDK (top of this file).
3581 GdkWindow
*window
= (GdkWindow
*) NULL
;
3583 window
= GTK_PIZZA(m_wxwindow
)->bin_window
;
3585 window
= GetConnectWidget()->window
;
3588 gdk_window_warp_pointer( window
, x
, y
);
3591 wxWindowGTK::ScrollDir
wxWindowGTK::ScrollDirFromRange(GtkRange
*range
) const
3593 // find the scrollbar which generated the event
3594 for ( int dir
= 0; dir
< ScrollDir_Max
; dir
++ )
3596 if ( range
== m_scrollBar
[dir
] )
3597 return (ScrollDir
)dir
;
3600 wxFAIL_MSG( _T("event from unknown scrollbar received") );
3602 return ScrollDir_Max
;
3605 bool wxWindowGTK::DoScrollByUnits(ScrollDir dir
, ScrollUnit unit
, int units
)
3607 bool changed
= false;
3608 GtkRange
* range
= m_scrollBar
[dir
];
3609 if ( range
&& units
)
3611 GtkAdjustment
* adj
= range
->adjustment
;
3612 gdouble inc
= unit
== ScrollUnit_Line
? adj
->step_increment
3613 : adj
->page_increment
;
3615 const int posOld
= int(adj
->value
+ 0.5);
3616 gtk_range_set_value(range
, posOld
+ units
*inc
);
3618 changed
= int(adj
->value
+ 0.5) != posOld
;
3624 bool wxWindowGTK::ScrollLines(int lines
)
3626 return DoScrollByUnits(ScrollDir_Vert
, ScrollUnit_Line
, lines
);
3629 bool wxWindowGTK::ScrollPages(int pages
)
3631 return DoScrollByUnits(ScrollDir_Vert
, ScrollUnit_Page
, pages
);
3634 void wxWindowGTK::Refresh( bool eraseBackground
, const wxRect
*rect
)
3638 if (!m_widget
->window
)
3643 if (!GTK_PIZZA(m_wxwindow
)->bin_window
) return;
3645 GdkRectangle gdk_rect
,
3649 gdk_rect
.x
= rect
->x
;
3650 gdk_rect
.y
= rect
->y
;
3651 gdk_rect
.width
= rect
->width
;
3652 gdk_rect
.height
= rect
->height
;
3653 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3654 gdk_rect
.x
= GetClientSize().x
- gdk_rect
.x
- gdk_rect
.width
;
3658 else // invalidate everything
3663 gdk_window_invalidate_rect( GTK_PIZZA(m_wxwindow
)->bin_window
, p
, TRUE
);
3667 void wxWindowGTK::Update()
3671 // when we call Update() we really want to update the window immediately on
3672 // screen, even if it means flushing the entire queue and hence slowing down
3673 // everything -- but it should still be done, it's just that Update() should
3674 // be called very rarely
3678 void wxWindowGTK::GtkUpdate()
3680 if (m_wxwindow
&& GTK_PIZZA(m_wxwindow
)->bin_window
)
3681 gdk_window_process_updates( GTK_PIZZA(m_wxwindow
)->bin_window
, FALSE
);
3682 if (m_widget
&& m_widget
->window
&& (m_wxwindow
!= m_widget
))
3683 gdk_window_process_updates( m_widget
->window
, FALSE
);
3685 // for consistency with other platforms (and also because it's convenient
3686 // to be able to update an entire TLW by calling Update() only once), we
3687 // should also update all our children here
3688 for ( wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
3690 node
= node
->GetNext() )
3692 node
->GetData()->GtkUpdate();
3696 bool wxWindowGTK::DoIsExposed( int x
, int y
) const
3698 return m_updateRegion
.Contains(x
, y
) != wxOutRegion
;
3702 bool wxWindowGTK::DoIsExposed( int x
, int y
, int w
, int h
) const
3704 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3705 return m_updateRegion
.Contains(x
-w
, y
, w
, h
) != wxOutRegion
;
3707 return m_updateRegion
.Contains(x
, y
, w
, h
) != wxOutRegion
;
3710 void wxWindowGTK::GtkSendPaintEvents()
3714 m_updateRegion
.Clear();
3718 // Clip to paint region in wxClientDC
3719 m_clipPaintRegion
= true;
3721 m_nativeUpdateRegion
= m_updateRegion
;
3723 if (GetLayoutDirection() == wxLayout_RightToLeft
)
3725 // Transform m_updateRegion under RTL
3726 m_updateRegion
.Clear();
3729 gdk_window_get_geometry( GTK_PIZZA(m_wxwindow
)->bin_window
,
3730 NULL
, NULL
, &width
, NULL
, NULL
);
3732 wxRegionIterator
upd( m_nativeUpdateRegion
);
3736 rect
.x
= upd
.GetX();
3737 rect
.y
= upd
.GetY();
3738 rect
.width
= upd
.GetWidth();
3739 rect
.height
= upd
.GetHeight();
3741 rect
.x
= width
- rect
.x
- rect
.width
;
3742 m_updateRegion
.Union( rect
);
3748 // widget to draw on
3749 GtkPizza
*pizza
= GTK_PIZZA (m_wxwindow
);
3751 if (GetThemeEnabled() && (GetBackgroundStyle() == wxBG_STYLE_SYSTEM
))
3753 // find ancestor from which to steal background
3754 wxWindow
*parent
= wxGetTopLevelParent((wxWindow
*)this);
3756 parent
= (wxWindow
*)this;
3758 if (GTK_WIDGET_MAPPED(parent
->m_widget
))
3760 wxRegionIterator
upd( m_nativeUpdateRegion
);
3764 rect
.x
= upd
.GetX();
3765 rect
.y
= upd
.GetY();
3766 rect
.width
= upd
.GetWidth();
3767 rect
.height
= upd
.GetHeight();
3769 gtk_paint_flat_box( parent
->m_widget
->style
,
3771 (GtkStateType
)GTK_WIDGET_STATE(m_wxwindow
),
3784 wxWindowDC
dc( (wxWindow
*)this );
3785 dc
.SetClippingRegion( m_updateRegion
);
3787 wxEraseEvent
erase_event( GetId(), &dc
);
3788 erase_event
.SetEventObject( this );
3790 GetEventHandler()->ProcessEvent(erase_event
);
3793 wxNcPaintEvent
nc_paint_event( GetId() );
3794 nc_paint_event
.SetEventObject( this );
3795 GetEventHandler()->ProcessEvent( nc_paint_event
);
3797 wxPaintEvent
paint_event( GetId() );
3798 paint_event
.SetEventObject( this );
3799 GetEventHandler()->ProcessEvent( paint_event
);
3801 m_clipPaintRegion
= false;
3803 m_updateRegion
.Clear();
3804 m_nativeUpdateRegion
.Clear();
3807 void wxWindowGTK::SetDoubleBuffered( bool on
)
3809 wxCHECK_RET( (m_widget
!= NULL
), wxT("invalid window") );
3812 gtk_widget_set_double_buffered( m_wxwindow
, on
);
3815 bool wxWindowGTK::IsDoubleBuffered() const
3817 return GTK_WIDGET_DOUBLE_BUFFERED( m_wxwindow
);
3820 void wxWindowGTK::ClearBackground()
3822 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
3826 void wxWindowGTK::DoSetToolTip( wxToolTip
*tip
)
3828 wxWindowBase::DoSetToolTip(tip
);
3831 m_tooltip
->Apply( (wxWindow
*)this );
3834 void wxWindowGTK::ApplyToolTip( GtkTooltips
*tips
, const wxChar
*tip
)
3838 wxString
tmp( tip
);
3839 gtk_tooltips_set_tip( tips
, GetConnectWidget(), wxGTK_CONV(tmp
), (gchar
*) NULL
);
3843 gtk_tooltips_set_tip( tips
, GetConnectWidget(), NULL
, NULL
);
3846 #endif // wxUSE_TOOLTIPS
3848 bool wxWindowGTK::SetBackgroundColour( const wxColour
&colour
)
3850 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
3852 if (!wxWindowBase::SetBackgroundColour(colour
))
3857 // We need the pixel value e.g. for background clearing.
3858 m_backgroundColour
.CalcPixel(gtk_widget_get_colormap(m_widget
));
3861 // apply style change (forceStyle=true so that new style is applied
3862 // even if the bg colour changed from valid to wxNullColour)
3863 if (GetBackgroundStyle() != wxBG_STYLE_CUSTOM
)
3864 ApplyWidgetStyle(true);
3869 bool wxWindowGTK::SetForegroundColour( const wxColour
&colour
)
3871 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
3873 if (!wxWindowBase::SetForegroundColour(colour
))
3880 // We need the pixel value e.g. for background clearing.
3881 m_foregroundColour
.CalcPixel(gtk_widget_get_colormap(m_widget
));
3884 // apply style change (forceStyle=true so that new style is applied
3885 // even if the bg colour changed from valid to wxNullColour):
3886 ApplyWidgetStyle(true);
3891 PangoContext
*wxWindowGTK::GtkGetPangoDefaultContext()
3893 return gtk_widget_get_pango_context( m_widget
);
3896 GtkRcStyle
*wxWindowGTK::CreateWidgetStyle(bool forceStyle
)
3898 // do we need to apply any changes at all?
3901 !m_foregroundColour
.Ok() && !m_backgroundColour
.Ok() )
3906 GtkRcStyle
*style
= gtk_rc_style_new();
3911 pango_font_description_copy( m_font
.GetNativeFontInfo()->description
);
3914 int flagsNormal
= 0,
3917 flagsInsensitive
= 0;
3919 if ( m_foregroundColour
.Ok() )
3921 const GdkColor
*fg
= m_foregroundColour
.GetColor();
3923 style
->fg
[GTK_STATE_NORMAL
] =
3924 style
->text
[GTK_STATE_NORMAL
] = *fg
;
3925 flagsNormal
|= GTK_RC_FG
| GTK_RC_TEXT
;
3927 style
->fg
[GTK_STATE_PRELIGHT
] =
3928 style
->text
[GTK_STATE_PRELIGHT
] = *fg
;
3929 flagsPrelight
|= GTK_RC_FG
| GTK_RC_TEXT
;
3931 style
->fg
[GTK_STATE_ACTIVE
] =
3932 style
->text
[GTK_STATE_ACTIVE
] = *fg
;
3933 flagsActive
|= GTK_RC_FG
| GTK_RC_TEXT
;
3936 if ( m_backgroundColour
.Ok() )
3938 const GdkColor
*bg
= m_backgroundColour
.GetColor();
3940 style
->bg
[GTK_STATE_NORMAL
] =
3941 style
->base
[GTK_STATE_NORMAL
] = *bg
;
3942 flagsNormal
|= GTK_RC_BG
| GTK_RC_BASE
;
3944 style
->bg
[GTK_STATE_PRELIGHT
] =
3945 style
->base
[GTK_STATE_PRELIGHT
] = *bg
;
3946 flagsPrelight
|= GTK_RC_BG
| GTK_RC_BASE
;
3948 style
->bg
[GTK_STATE_ACTIVE
] =
3949 style
->base
[GTK_STATE_ACTIVE
] = *bg
;
3950 flagsActive
|= GTK_RC_BG
| GTK_RC_BASE
;
3952 style
->bg
[GTK_STATE_INSENSITIVE
] =
3953 style
->base
[GTK_STATE_INSENSITIVE
] = *bg
;
3954 flagsInsensitive
|= GTK_RC_BG
| GTK_RC_BASE
;
3957 style
->color_flags
[GTK_STATE_NORMAL
] = (GtkRcFlags
)flagsNormal
;
3958 style
->color_flags
[GTK_STATE_PRELIGHT
] = (GtkRcFlags
)flagsPrelight
;
3959 style
->color_flags
[GTK_STATE_ACTIVE
] = (GtkRcFlags
)flagsActive
;
3960 style
->color_flags
[GTK_STATE_INSENSITIVE
] = (GtkRcFlags
)flagsInsensitive
;
3965 void wxWindowGTK::ApplyWidgetStyle(bool forceStyle
)
3967 GtkRcStyle
*style
= CreateWidgetStyle(forceStyle
);
3970 DoApplyWidgetStyle(style
);
3971 gtk_rc_style_unref(style
);
3974 // Style change may affect GTK+'s size calculation:
3975 InvalidateBestSize();
3978 void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle
*style
)
3981 gtk_widget_modify_style(m_wxwindow
, style
);
3983 gtk_widget_modify_style(m_widget
, style
);
3986 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style
)
3988 wxWindowBase::SetBackgroundStyle(style
);
3990 if (style
== wxBG_STYLE_CUSTOM
)
3992 GdkWindow
*window
= (GdkWindow
*) NULL
;
3994 window
= GTK_PIZZA(m_wxwindow
)->bin_window
;
3996 window
= GetConnectWidget()->window
;
4000 // Make sure GDK/X11 doesn't refresh the window
4002 gdk_window_set_back_pixmap( window
, None
, False
);
4004 Display
* display
= GDK_WINDOW_DISPLAY(window
);
4007 m_needsStyleChange
= false;
4010 // Do in OnIdle, because the window is not yet available
4011 m_needsStyleChange
= true;
4013 // Don't apply widget style, or we get a grey background
4017 // apply style change (forceStyle=true so that new style is applied
4018 // even if the bg colour changed from valid to wxNullColour):
4019 ApplyWidgetStyle(true);
4024 #if wxUSE_DRAG_AND_DROP
4026 void wxWindowGTK::SetDropTarget( wxDropTarget
*dropTarget
)
4028 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4030 GtkWidget
*dnd_widget
= GetConnectWidget();
4032 if (m_dropTarget
) m_dropTarget
->UnregisterWidget( dnd_widget
);
4034 if (m_dropTarget
) delete m_dropTarget
;
4035 m_dropTarget
= dropTarget
;
4037 if (m_dropTarget
) m_dropTarget
->RegisterWidget( dnd_widget
);
4040 #endif // wxUSE_DRAG_AND_DROP
4042 GtkWidget
* wxWindowGTK::GetConnectWidget()
4044 GtkWidget
*connect_widget
= m_widget
;
4045 if (m_wxwindow
) connect_widget
= m_wxwindow
;
4047 return connect_widget
;
4050 bool wxWindowGTK::GTKIsOwnWindow(GdkWindow
*window
) const
4052 wxArrayGdkWindows windowsThis
;
4053 GdkWindow
* const winThis
= GTKGetWindow(windowsThis
);
4055 return winThis
? window
== winThis
4056 : windowsThis
.Index(window
) != wxNOT_FOUND
;
4059 GdkWindow
*wxWindowGTK::GTKGetWindow(wxArrayGdkWindows
& WXUNUSED(windows
)) const
4061 return m_wxwindow
? GTK_PIZZA(m_wxwindow
)->bin_window
: m_widget
->window
;
4064 bool wxWindowGTK::SetFont( const wxFont
&font
)
4066 wxCHECK_MSG( m_widget
!= NULL
, false, wxT("invalid window") );
4068 if (!wxWindowBase::SetFont(font
))
4071 // apply style change (forceStyle=true so that new style is applied
4072 // even if the font changed from valid to wxNullFont):
4073 ApplyWidgetStyle(true);
4078 void wxWindowGTK::DoCaptureMouse()
4080 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4082 GdkWindow
*window
= (GdkWindow
*) NULL
;
4084 window
= GTK_PIZZA(m_wxwindow
)->bin_window
;
4086 window
= GetConnectWidget()->window
;
4088 wxCHECK_RET( window
, _T("CaptureMouse() failed") );
4090 const wxCursor
* cursor
= &m_cursor
;
4092 cursor
= wxSTANDARD_CURSOR
;
4094 gdk_pointer_grab( window
, FALSE
,
4096 (GDK_BUTTON_PRESS_MASK
|
4097 GDK_BUTTON_RELEASE_MASK
|
4098 GDK_POINTER_MOTION_HINT_MASK
|
4099 GDK_POINTER_MOTION_MASK
),
4101 cursor
->GetCursor(),
4102 (guint32
)GDK_CURRENT_TIME
);
4103 g_captureWindow
= this;
4104 g_captureWindowHasMouse
= true;
4107 void wxWindowGTK::DoReleaseMouse()
4109 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4111 wxCHECK_RET( g_captureWindow
, wxT("can't release mouse - not captured") );
4113 g_captureWindow
= (wxWindowGTK
*) NULL
;
4115 GdkWindow
*window
= (GdkWindow
*) NULL
;
4117 window
= GTK_PIZZA(m_wxwindow
)->bin_window
;
4119 window
= GetConnectWidget()->window
;
4124 gdk_pointer_ungrab ( (guint32
)GDK_CURRENT_TIME
);
4128 wxWindow
*wxWindowBase::GetCapture()
4130 return (wxWindow
*)g_captureWindow
;
4133 bool wxWindowGTK::IsRetained() const
4138 void wxWindowGTK::BlockScrollEvent()
4140 wxASSERT(!m_blockScrollEvent
);
4141 m_blockScrollEvent
= true;
4144 void wxWindowGTK::UnblockScrollEvent()
4146 wxASSERT(m_blockScrollEvent
);
4147 m_blockScrollEvent
= false;
4150 void wxWindowGTK::SetScrollbar(int orient
,
4154 bool WXUNUSED(update
))
4156 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4157 wxCHECK_RET( m_wxwindow
!= NULL
, wxT("window needs client area for scrolling") );
4158 wxCHECK_RET( m_wxwindow
!= m_widget
, wxT("no scrolling for this wxWindow, use wxHSCROLL or wxVSCROLL") );
4162 m_hasScrolling
= true;
4166 // GtkRange requires upper > lower
4171 if (pos
> range
- thumbVisible
)
4172 pos
= range
- thumbVisible
;
4175 GtkAdjustment
* adj
= m_scrollBar
[ScrollDirFromOrient(orient
)]->adjustment
;
4176 adj
->step_increment
= 1;
4177 adj
->page_increment
=
4178 adj
->page_size
= thumbVisible
;
4180 SetScrollPos(orient
, pos
);
4181 gtk_adjustment_changed(adj
);
4184 void wxWindowGTK::SetScrollPos(int orient
, int pos
, bool WXUNUSED(refresh
))
4186 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4187 wxCHECK_RET( m_wxwindow
!= NULL
, wxT("window needs client area for scrolling") );
4188 wxCHECK_RET( m_wxwindow
!= m_widget
, wxT("no scrolling for this wxWindow, use wxHSCROLL or wxVSCROLL") );
4190 // This check is more than an optimization. Without it, the slider
4191 // will not move smoothly while tracking when using wxScrollHelper.
4192 if (GetScrollPos(orient
) != pos
)
4194 const int dir
= ScrollDirFromOrient(orient
);
4195 GtkAdjustment
* adj
= m_scrollBar
[dir
]->adjustment
;
4196 const int max
= int(adj
->upper
- adj
->page_size
);
4201 m_scrollPos
[dir
] = adj
->value
= pos
;
4203 // If a "value_changed" signal emission is not already in progress
4204 if (!m_blockValueChanged
[dir
])
4206 gtk_adjustment_value_changed(adj
);
4211 int wxWindowGTK::GetScrollThumb(int orient
) const
4213 wxCHECK_MSG( m_widget
!= NULL
, 0, wxT("invalid window") );
4214 wxCHECK_MSG( m_wxwindow
!= NULL
, 0, wxT("window needs client area for scrolling") );
4215 wxCHECK_MSG( m_wxwindow
!= m_widget
, 0, wxT("no scrolling for this wxWindow, use wxHSCROLL or wxVSCROLL") );
4217 return int(m_scrollBar
[ScrollDirFromOrient(orient
)]->adjustment
->page_size
);
4220 int wxWindowGTK::GetScrollPos( int orient
) const
4222 wxCHECK_MSG( m_widget
!= NULL
, 0, wxT("invalid window") );
4223 wxCHECK_MSG( m_wxwindow
!= NULL
, 0, wxT("window needs client area for scrolling") );
4224 wxCHECK_MSG( m_wxwindow
!= m_widget
, 0, wxT("no scrolling for this wxWindow, use wxHSCROLL or wxVSCROLL") );
4226 return int(m_scrollBar
[ScrollDirFromOrient(orient
)]->adjustment
->value
+ 0.5);
4229 int wxWindowGTK::GetScrollRange( int orient
) const
4231 wxCHECK_MSG( m_widget
!= NULL
, 0, wxT("invalid window") );
4232 wxCHECK_MSG( m_wxwindow
!= NULL
, 0, wxT("window needs client area for scrolling") );
4233 wxCHECK_MSG( m_wxwindow
!= m_widget
, 0, wxT("no scrolling for this wxWindow, use wxHSCROLL or wxVSCROLL") );
4235 return int(m_scrollBar
[ScrollDirFromOrient(orient
)]->adjustment
->upper
);
4238 // Determine if increment is the same as +/-x, allowing for some small
4239 // difference due to possible inexactness in floating point arithmetic
4240 static inline bool IsScrollIncrement(double increment
, double x
)
4242 wxASSERT(increment
> 0);
4243 const double tolerance
= 1.0 / 1024;
4244 return fabs(increment
- fabs(x
)) < tolerance
;
4247 wxEventType
wxWindowGTK::GetScrollEventType(GtkRange
* range
)
4251 wxASSERT(range
== m_scrollBar
[0] || range
== m_scrollBar
[1]);
4253 const int barIndex
= range
== m_scrollBar
[1];
4254 GtkAdjustment
* adj
= range
->adjustment
;
4256 const int value
= int(adj
->value
+ 0.5);
4258 // save previous position
4259 const double oldPos
= m_scrollPos
[barIndex
];
4260 // update current position
4261 m_scrollPos
[barIndex
] = adj
->value
;
4262 // If event should be ignored, or integral position has not changed
4263 if (!m_hasVMT
|| g_blockEventsOnDrag
|| value
== int(oldPos
+ 0.5))
4268 wxEventType eventType
= wxEVT_SCROLL_THUMBTRACK
;
4271 // Difference from last change event
4272 const double diff
= adj
->value
- oldPos
;
4273 const bool isDown
= diff
> 0;
4275 if (IsScrollIncrement(adj
->step_increment
, diff
))
4277 eventType
= isDown
? wxEVT_SCROLL_LINEDOWN
: wxEVT_SCROLL_LINEUP
;
4279 else if (IsScrollIncrement(adj
->page_increment
, diff
))
4281 eventType
= isDown
? wxEVT_SCROLL_PAGEDOWN
: wxEVT_SCROLL_PAGEUP
;
4283 else if (m_mouseButtonDown
)
4285 // Assume track event
4286 m_isScrolling
= true;
4292 void wxWindowGTK::ScrollWindow( int dx
, int dy
, const wxRect
* WXUNUSED(rect
) )
4294 wxCHECK_RET( m_widget
!= NULL
, wxT("invalid window") );
4296 wxCHECK_RET( m_wxwindow
!= NULL
, wxT("window needs client area for scrolling") );
4298 // No scrolling requested.
4299 if ((dx
== 0) && (dy
== 0)) return;
4301 m_clipPaintRegion
= true;
4303 if (GetLayoutDirection() == wxLayout_RightToLeft
)
4304 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow
), dx
, -dy
);
4306 gtk_pizza_scroll( GTK_PIZZA(m_wxwindow
), -dx
, -dy
);
4308 m_clipPaintRegion
= false;
4311 bool restoreCaret
= (GetCaret() != NULL
&& GetCaret()->IsVisible());
4314 wxRect
caretRect(GetCaret()->GetPosition(), GetCaret()->GetSize());
4316 caretRect
.width
+= dx
;
4319 caretRect
.x
+= dx
; caretRect
.width
-= dx
;
4322 caretRect
.height
+= dy
;
4325 caretRect
.y
+= dy
; caretRect
.height
-= dy
;
4328 RefreshRect(caretRect
);
4330 #endif // wxUSE_CARET
4333 void wxWindowGTK::GtkScrolledWindowSetBorder(GtkWidget
* w
, int wxstyle
)
4335 //RN: Note that static controls usually have no border on gtk, so maybe
4336 //it makes sense to treat that as simply no border at the wx level
4338 if (!(wxstyle
& wxNO_BORDER
) && !(wxstyle
& wxBORDER_STATIC
))
4340 GtkShadowType gtkstyle
;
4342 if(wxstyle
& wxBORDER_RAISED
)
4343 gtkstyle
= GTK_SHADOW_OUT
;
4344 else if (wxstyle
& wxBORDER_SUNKEN
)
4345 gtkstyle
= GTK_SHADOW_IN
;
4346 else if (wxstyle
& wxBORDER_DOUBLE
)
4347 gtkstyle
= GTK_SHADOW_ETCHED_IN
;
4349 gtkstyle
= GTK_SHADOW_IN
;
4351 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(w
),
4356 void wxWindowGTK::SetWindowStyleFlag( long style
)
4358 // Updates the internal variable. NB: Now m_windowStyle bits carry the _new_ style values already
4359 wxWindowBase::SetWindowStyleFlag(style
);
4362 // Find the wxWindow at the current mouse position, also returning the mouse
4364 wxWindow
* wxFindWindowAtPointer(wxPoint
& pt
)
4366 pt
= wxGetMousePosition();
4367 wxWindow
* found
= wxFindWindowAtPoint(pt
);
4371 // Get the current mouse position.
4372 wxPoint
wxGetMousePosition()
4374 /* This crashes when used within wxHelpContext,
4375 so we have to use the X-specific implementation below.
4377 GdkModifierType *mask;
4378 (void) gdk_window_get_pointer(NULL, &x, &y, mask);
4380 return wxPoint(x, y);
4384 GdkWindow
* windowAtPtr
= gdk_window_at_pointer(& x
, & y
);
4386 Display
*display
= windowAtPtr
? GDK_WINDOW_XDISPLAY(windowAtPtr
) : GDK_DISPLAY();
4387 Window rootWindow
= RootWindowOfScreen (DefaultScreenOfDisplay(display
));
4388 Window rootReturn
, childReturn
;
4389 int rootX
, rootY
, winX
, winY
;
4390 unsigned int maskReturn
;
4392 XQueryPointer (display
,
4396 &rootX
, &rootY
, &winX
, &winY
, &maskReturn
);
4397 return wxPoint(rootX
, rootY
);
4401 // Needed for implementing e.g. combobox on wxGTK within a modal dialog.
4402 void wxAddGrab(wxWindow
* window
)
4404 gtk_grab_add( (GtkWidget
*) window
->GetHandle() );
4407 void wxRemoveGrab(wxWindow
* window
)
4409 gtk_grab_remove( (GtkWidget
*) window
->GetHandle() );