]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk/window.cpp
Implement wxComboBox::Popup() and Dismiss() for wxOSX/Cocoa.
[wxWidgets.git] / src / gtk / window.cpp
index 26004164af57ff10dea2c20481eeb9213b050631..2b2ead040554a16ff8fc2b0cf7cf1b35dbc8d945 100644 (file)
@@ -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 <ctype.h>
@@ -47,29 +48,6 @@ using namespace wxGTKImpl;
 #include <gdk/gdkkeysyms-compat.h>
 #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<wxWindow*>(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(&currentX, &currentY);
-    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(&currentX, &currentY);
+        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;