X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/4a99d5975088e9c3cf05b3d4d9889bd9b70ea75d..ff8cb900da92489dd87712e409bfd4c1f5eacfb8:/src/gtk/window.cpp diff --git a/src/gtk/window.cpp b/src/gtk/window.cpp index 26004164af..2b2ead0405 100644 --- a/src/gtk/window.cpp +++ b/src/gtk/window.cpp @@ -31,6 +31,7 @@ #include "wx/tooltip.h" #include "wx/caret.h" #include "wx/fontutil.h" +#include "wx/scopeguard.h" #include "wx/sysopt.h" #include @@ -47,29 +48,6 @@ using namespace wxGTKImpl; #include #endif -#if !GTK_CHECK_VERSION(2,10,0) - // GTK+ can reliably detect Meta key state only since 2.10 when - // GDK_META_MASK was introduced -- there wasn't any way to detect it - // in older versions. wxGTK used GDK_MOD2_MASK for this purpose, but - // GDK_MOD2_MASK is documented as: - // - // the fifth modifier key (it depends on the modifier mapping of the X - // server which key is interpreted as this modifier) - // - // In other words, it isn't guaranteed to map to Meta. This is a real - // problem: it is common to map NumLock to it (in fact, it's an exception - // if the X server _doesn't_ use it for NumLock). So the old code caused - // wxKeyEvent::MetaDown() to always return true as long as NumLock was on - // on many systems, which broke all applications using - // wxKeyEvent::GetModifiers() to check modifiers state (see e.g. here: - // http://tinyurl.com/56lsk2). - // - // Because of this, it's better to not detect Meta key state at all than - // to detect it incorrectly. Hence the following #define, which causes - // m_metaDown to be always set to false. - #define GDK_META_MASK 0 -#endif - //----------------------------------------------------------------------------- // documentation on internals //----------------------------------------------------------------------------- @@ -339,7 +317,7 @@ expose_event_border(GtkWidget* widget, GdkEventExpose* gdk_event, wxWindow* win) extern "C" { static void -parent_set(GtkWidget* widget, GtkObject* old_parent, wxWindow* win) +parent_set(GtkWidget* widget, GtkWidget* old_parent, wxWindow* win) { if (old_parent) { @@ -853,34 +831,22 @@ struct wxGtkIMData namespace { -// Send wxEVT_CHAR_HOOK event to the parent of the window and if it wasn't -// processed, send wxEVT_CHAR to the window itself. Return true if either of -// them was handled. -bool -SendCharHookAndCharEvents(const wxKeyEvent& event, wxWindow *win) +// Send wxEVT_CHAR_HOOK event to the parent of the window and return true only +// if it was processed (and not skipped). +bool SendCharHookEvent(const wxKeyEvent& event, wxWindow *win) { - // wxEVT_CHAR_HOOK must be sent to the top level parent window to allow it + // wxEVT_CHAR_HOOK must be sent to allow the parent windows (e.g. a dialog + // which typically closes when Esc key is pressed in any of its controls) // to handle key events in all of its children unless the mouse is captured // in which case we consider that the keyboard should be "captured" too. if ( !g_captureWindow ) { - wxWindow * const parent = wxGetTopLevelParent(win); - if ( parent ) - { - // We need to make a copy of the event object because it is - // modified while it's handled, notably its WasProcessed() flag - // is set after it had been processed once. - wxKeyEvent eventCharHook(event); - eventCharHook.SetEventType(wxEVT_CHAR_HOOK); - if ( parent->HandleWindowEvent(eventCharHook) ) - return true; - } + wxKeyEvent eventCharHook(wxEVT_CHAR_HOOK, event); + if ( win->HandleWindowEvent(eventCharHook) ) + return true; } - // As above, make a copy of the event first. - wxKeyEvent eventChar(event); - eventChar.SetEventType(wxEVT_CHAR); - return win->HandleWindowEvent(eventChar); + return false; } } // anonymous namespace @@ -902,6 +868,13 @@ gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget), if( wxTranslateGTKKeyEventToWx(event, win, gdk_event) ) { + // Send the CHAR_HOOK event first + if ( SendCharHookEvent(event, win) ) + { + // Don't do anything at all with this event any more. + return TRUE; + } + // Emit KEY_DOWN event ret = win->HandleWindowEvent( event ); } @@ -985,25 +958,27 @@ gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget), if ( key_code ) { + wxKeyEvent eventChar(wxEVT_CHAR, event); + wxLogTrace(TRACE_KEYS, wxT("Char event: %ld"), key_code); - event.m_keyCode = key_code; + eventChar.m_keyCode = key_code; // To conform to the docs we need to translate Ctrl-alpha // characters to values in the range 1-26. - if ( event.ControlDown() && + if ( eventChar.ControlDown() && ( wxIsLowerChar(key_code) || wxIsUpperChar(key_code) )) { if ( wxIsLowerChar(key_code) ) - event.m_keyCode = key_code - 'a' + 1; + eventChar.m_keyCode = key_code - 'a' + 1; if ( wxIsUpperChar(key_code) ) - event.m_keyCode = key_code - 'A' + 1; + eventChar.m_keyCode = key_code - 'A' + 1; #if wxUSE_UNICODE - event.m_uniChar = event.m_keyCode; + eventChar.m_uniChar = event.m_keyCode; #endif } - ret = SendCharHookAndCharEvents(event, win); + ret = win->HandleWindowEvent(eventChar); } } @@ -1017,7 +992,7 @@ gtk_wxwindow_commit_cb (GtkIMContext * WXUNUSED(context), const gchar *str, wxWindow *window) { - wxKeyEvent event( wxEVT_KEY_DOWN ); + wxKeyEvent event( wxEVT_CHAR ); // take modifiers, cursor position, timestamp etc. from the last // key_press_event that was fed into Input Method: @@ -1062,7 +1037,7 @@ gtk_wxwindow_commit_cb (GtkIMContext * WXUNUSED(context), #endif } - SendCharHookAndCharEvents(event, window); + window->HandleWindowEvent(event); } } } @@ -1878,38 +1853,19 @@ gtk_scrollbar_button_release_event(GtkRange* range, GdkEventButton*, wxWindow* w //----------------------------------------------------------------------------- static void -gtk_window_realized_callback(GtkWidget* WXUNUSED(widget), wxWindow* win) +gtk_window_realized_callback(GtkWidget* WXUNUSED(widget), wxWindowGTK* win) { win->GTKHandleRealized(); } -void wxWindowGTK::GTKHandleRealized() -{ - if (m_imData) - { - gtk_im_context_set_client_window - ( - m_imData->context, - m_wxwindow ? GTKGetDrawingWindow() - : gtk_widget_get_window(m_widget) - ); - } - - // We cannot set colours and fonts before the widget - // been realized, so we do this directly after realization - // or otherwise in idle time - - if (m_needsStyleChange) - { - SetBackgroundStyle(GetBackgroundStyle()); - m_needsStyleChange = false; - } - - wxWindowCreateEvent event( this ); - event.SetEventObject( this ); - GTKProcessEvent( event ); +//----------------------------------------------------------------------------- +// "unrealize" from m_wxwindow +//----------------------------------------------------------------------------- - GTKUpdateCursor(true, false); +static void unrealize(GtkWidget*, wxWindowGTK* win) +{ + if (win->m_imData) + gtk_im_context_set_client_window(win->m_imData->context, NULL); } //----------------------------------------------------------------------------- @@ -1990,6 +1946,35 @@ void gtk_window_style_set_callback( GtkWidget *WXUNUSED(widget), } // extern "C" +void wxWindowGTK::GTKHandleRealized() +{ + if (m_imData) + { + gtk_im_context_set_client_window + ( + m_imData->context, + m_wxwindow ? GTKGetDrawingWindow() + : gtk_widget_get_window(m_widget) + ); + } + + // We cannot set colours and fonts before the widget + // been realized, so we do this directly after realization + // or otherwise in idle time + + if (m_needsStyleChange) + { + SetBackgroundStyle(GetBackgroundStyle()); + m_needsStyleChange = false; + } + + wxWindowCreateEvent event(static_cast(this)); + event.SetEventObject( this ); + GTKProcessEvent( event ); + + GTKUpdateCursor(true, false); +} + // ---------------------------------------------------------------------------- // this wxWindowBase function is implemented here (in platform-specific file) // because it is static and so couldn't be made virtual @@ -2270,6 +2255,7 @@ wxWindowGTK::~wxWindowGTK() // delete before the widgets to avoid a crash on solaris delete m_imData; + m_imData = NULL; // avoid problem with GTK+ 2.18 where a frozen window causes the whole // TLW to be frozen, and if the window is then destroyed, nothing ever @@ -2333,6 +2319,7 @@ void wxWindowGTK::PostCreation() g_signal_connect (m_imData->context, "commit", G_CALLBACK (gtk_wxwindow_commit_cb), this); + g_signal_connect(m_wxwindow, "unrealize", G_CALLBACK(unrealize), this); } // focus handling @@ -2432,7 +2419,8 @@ void wxWindowGTK::PostCreation() gtk_widget_show( m_widget ); } -unsigned long wxWindowGTK::GTKConnectWidget(const char *signal, void (*callback)()) +unsigned long +wxWindowGTK::GTKConnectWidget(const char *signal, wxGTKCallback callback) { return g_signal_connect(m_widget, signal, callback, this); } @@ -2510,12 +2498,15 @@ void wxWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") ); wxASSERT_MSG( (m_parent != NULL), wxT("wxWindowGTK::SetSize requires parent.\n") ); - int currentX, currentY; - GetPosition(¤tX, ¤tY); - if (x == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE)) - x = currentX; - if (y == -1 && !(sizeFlags & wxSIZE_ALLOW_MINUS_ONE)) - y = currentY; + if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0 && (x == -1 || y == -1)) + { + int currentX, currentY; + GetPosition(¤tX, ¤tY); + if (x == -1) + x = currentX; + if (y == -1) + y = currentY; + } AdjustForParentClientOrigin(x, y, sizeFlags); // calculate the best size if we should auto size the window @@ -3577,43 +3568,31 @@ bool wxWindowGTK::ScrollPages(int pages) void wxWindowGTK::Refresh(bool WXUNUSED(eraseBackground), const wxRect *rect) { - if ( !m_widget ) - { - // it is valid to call Refresh() for a window which hasn't been created - // yet, it simply doesn't do anything in this case - return; - } - - if (!m_wxwindow) + if (m_wxwindow) { - if (rect) - gtk_widget_queue_draw_area( m_widget, rect->x, rect->y, rect->width, rect->height ); - else - gtk_widget_queue_draw( m_widget ); + if (gtk_widget_get_mapped(m_wxwindow)) + { + GdkWindow* window = gtk_widget_get_window(m_wxwindow); + if (rect) + { + GdkRectangle r = { rect->x, rect->y, rect->width, rect->height }; + if (GetLayoutDirection() == wxLayout_RightToLeft) + r.x = gdk_window_get_width(window) - r.x - rect->width; + gdk_window_invalidate_rect(window, &r, true); + } + else + gdk_window_invalidate_rect(window, NULL, true); + } } - else + else if (m_widget) { - // Just return if the widget or one of its ancestors isn't mapped - GtkWidget *w; - for (w = m_wxwindow; w != NULL; w = gtk_widget_get_parent(w)) - if (!gtk_widget_get_mapped (w)) - return; - - GdkWindow* window = GTKGetDrawingWindow(); - if (rect) + if (gtk_widget_get_mapped(m_widget)) { - int x = rect->x; - if (GetLayoutDirection() == wxLayout_RightToLeft) - x = GetClientSize().x - x - rect->width; - GdkRectangle r; - r.x = rect->x; - r.y = rect->y; - r.width = rect->width; - r.height = rect->height; - gdk_window_invalidate_rect(window, &r, true); + if (rect) + gtk_widget_queue_draw_area(m_widget, rect->x, rect->y, rect->width, rect->height); + else + gtk_widget_queue_draw(m_widget); } - else - gdk_window_invalidate_rect(window, NULL, true); } } @@ -4043,6 +4022,14 @@ bool wxWindowGTK::DoPopupMenu( wxMenu *menu, int x, int y ) { wxCHECK_MSG( m_widget != NULL, false, wxT("invalid window") ); + // For compatibility with other ports, pretend that the window showing the + // menu has focus while the menu is shown. This is needed because the popup + // menu actually steals the focus from the window it's associated it in + // wxGTK unlike, say, wxMSW. + wxWindowGTK* const oldPendingFocus = gs_pendingFocus; + gs_pendingFocus = this; + wxON_BLOCK_EXIT_SET( gs_pendingFocus, oldPendingFocus ); + menu->UpdateUI(); wxPoint pos;