]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk/window.cpp
Fix crash in wxDC::GetMultiLineTextExtent() after last commit.
[wxWidgets.git] / src / gtk / window.cpp
index 2a60441507f5ebf7b763ee03fe49e3930ea8b28a..978f556ca75953261c85405458f5ccdaa05ae061 100644 (file)
@@ -43,6 +43,7 @@
 #include "wx/gtk/private/gtk2-compat.h"
 #include "wx/gtk/private/event.h"
 #include "wx/gtk/private/win_gtk.h"
+#include "wx/private/textmeasure.h"
 using namespace wxGTKImpl;
 
 #ifdef GDK_WINDOWING_X11
@@ -226,6 +227,17 @@ int          g_lastButtonNumber = 0;
 // the trace mask used for the focus debugging messages
 #define TRACE_FOCUS wxT("focus")
 
+// A handy function to run from under gdb to show information about the given
+// GtkWidget. Right now it only shows its type, we could enhance it to show
+// more information later but this is already pretty useful.
+const char* wxDumpGtkWidget(GtkWidget* w)
+{
+    static wxString s;
+    s.Printf("GtkWidget %p, type \"%s\"", w, G_OBJECT_TYPE_NAME(w));
+
+    return s.c_str();
+}
+
 //-----------------------------------------------------------------------------
 // "expose_event"/"draw" from m_wxwindow
 //-----------------------------------------------------------------------------
@@ -666,6 +678,16 @@ static void wxFillOtherKeyEventFields(wxKeyEvent& event,
     event.m_altDown = (gdk_event->state & GDK_MOD1_MASK) != 0;
     event.m_metaDown = (gdk_event->state & GDK_META_MASK) != 0;
 
+    // At least with current Linux systems, MOD5 corresponds to AltGr key and
+    // we represent it, for consistency with Windows, which really allows to
+    // use Ctrl+Alt as a replacement for AltGr if this key is not present, as a
+    // combination of these two modifiers.
+    if ( gdk_event->state & GDK_MOD5_MASK )
+    {
+        event.m_controlDown =
+        event.m_altDown = true;
+    }
+
     // Normally we take the state of modifiers directly from the low level GDK
     // event but unfortunately GDK uses a different convention from MSW for the
     // key events corresponding to the modifier keys themselves: in it, when
@@ -711,8 +733,6 @@ static void wxFillOtherKeyEventFields(wxKeyEvent& event,
     event.m_rawCode = (wxUint32) gdk_event->keyval;
     event.m_rawFlags = gdk_event->hardware_keycode;
 
-    wxGetMousePosition(&event.m_x, &event.m_y);
-    win->ScreenToClient(&event.m_x, &event.m_y);
     event.SetEventObject( win );
 }
 
@@ -931,8 +951,6 @@ gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget),
                                GdkEventKey *gdk_event,
                                wxWindow *win )
 {
-    if (!win->m_hasVMT)
-        return FALSE;
     if (g_blockEventsOnDrag)
         return FALSE;
 
@@ -1014,10 +1032,9 @@ gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget),
     // will only be sent if it is not in an accelerator table.
     if (!ret)
     {
-        long key_code;
         KeySym keysym = gdk_event->keyval;
         // Find key code for EVT_CHAR and EVT_CHAR_HOOK events
-        key_code = wxTranslateKeySymToWXKey(keysym, true /* isChar */);
+        long key_code = wxTranslateKeySymToWXKey(keysym, true /* isChar */);
         if ( !key_code )
         {
             if ( wxIsAsciiKeysym(keysym) )
@@ -1039,6 +1056,9 @@ gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget),
             wxLogTrace(TRACE_KEYS, wxT("Char event: %ld"), key_code);
 
             eventChar.m_keyCode = key_code;
+#if wxUSE_UNICODE
+            eventChar.m_uniChar = gdk_keyval_to_unicode(key_code);
+#endif // wxUSE_UNICODE
 
             AdjustCharEventKeyCodes(eventChar);
 
@@ -1103,9 +1123,6 @@ gtk_window_key_release_callback( GtkWidget * WXUNUSED(widget),
                                  GdkEventKey *gdk_event,
                                  wxWindowGTK *win )
 {
-    if (!win->m_hasVMT)
-        return FALSE;
-
     if (g_blockEventsOnDrag)
         return FALSE;
 
@@ -1255,13 +1272,11 @@ bool wxWindowGTK::GTKProcessEvent(wxEvent& event) const
 
 bool wxWindowGTK::GTKShouldIgnoreEvent() const
 {
-    return !m_hasVMT || g_blockEventsOnDrag;
+    return g_blockEventsOnDrag;
 }
 
 int wxWindowGTK::GTKCallbackCommonPrologue(GdkEventAny *event) const
 {
-    if (!m_hasVMT)
-        return FALSE;
     if (g_blockEventsOnDrag)
         return TRUE;
     if (g_blockEventsOnScroll)
@@ -1960,22 +1975,21 @@ static void style_updated(GtkWidget*, GtkStyle*, wxWindow* win)
 }
 
 //-----------------------------------------------------------------------------
-// "unrealize" from m_wxwindow
+// "unrealize"
 //-----------------------------------------------------------------------------
 
 static void unrealize(GtkWidget*, wxWindow* win)
 {
-    if (win->m_imData)
-        gtk_im_context_set_client_window(win->m_imData->context, NULL);
-
-    g_signal_handlers_disconnect_by_func(
-        win->m_wxwindow, (void*)style_updated, win);
+    win->GTKHandleUnrealize();
 }
 
 } // extern "C"
 
 void wxWindowGTK::GTKHandleRealized()
 {
+    if (IsFrozen())
+        DoFreeze();
+
     if (m_imData)
     {
         gtk_im_context_set_client_window
@@ -2038,6 +2052,23 @@ void wxWindowGTK::GTKHandleRealized()
     }
 }
 
+void wxWindowGTK::GTKHandleUnrealize()
+{
+    // unrealizing a frozen window seems to have some lingering effect
+    // preventing updates to the affected area
+    if (IsFrozen())
+        DoThaw();
+
+    if (m_wxwindow)
+    {
+        if (m_imData)
+            gtk_im_context_set_client_window(m_imData->context, NULL);
+
+        g_signal_handlers_disconnect_by_func(
+            m_wxwindow, (void*)style_updated, this);
+    }
+}
+
 // ----------------------------------------------------------------------------
 // this wxWindowBase function is implemented here (in platform-specific file)
 // because it is static and so couldn't be made virtual
@@ -2154,8 +2185,6 @@ void wxWindowGTK::Init()
     m_width = 0;
     m_height = 0;
 
-    m_hasVMT = false;
-
     m_showOnIdle = false;
 
     m_noExpose = false;
@@ -2204,6 +2233,71 @@ wxWindowGTK::wxWindowGTK( wxWindow *parent,
     Create( parent, id, pos, size, style, name );
 }
 
+void wxWindowGTK::GTKCreateScrolledWindowWith(GtkWidget* view)
+{
+    wxASSERT_MSG( HasFlag(wxHSCROLL) || HasFlag(wxVSCROLL),
+                  wxS("Must not be called if scrolling is not needed.") );
+
+    m_widget = gtk_scrolled_window_new( NULL, NULL );
+
+    GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
+
+    // There is a conflict with default bindings at GTK+
+    // level between scrolled windows and notebooks both of which want to use
+    // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
+    // direction and notebooks for changing pages -- we decide that if we don't
+    // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
+    // means we can get working keyboard navigation in notebooks
+    if ( !HasFlag(wxHSCROLL) )
+    {
+        GtkBindingSet *
+            bindings = gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget));
+        if ( bindings )
+        {
+            gtk_binding_entry_remove(bindings, GDK_Page_Up, GDK_CONTROL_MASK);
+            gtk_binding_entry_remove(bindings, GDK_Page_Down, GDK_CONTROL_MASK);
+        }
+    }
+
+    if (HasFlag(wxALWAYS_SHOW_SB))
+    {
+        gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS );
+    }
+    else
+    {
+        gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+    }
+
+    m_scrollBar[ScrollDir_Horz] = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(scrolledWindow));
+    m_scrollBar[ScrollDir_Vert] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(scrolledWindow));
+    if (GetLayoutDirection() == wxLayout_RightToLeft)
+        gtk_range_set_inverted( m_scrollBar[ScrollDir_Horz], TRUE );
+
+    gtk_container_add( GTK_CONTAINER(m_widget), view );
+
+    // connect various scroll-related events
+    for ( int dir = 0; dir < ScrollDir_Max; dir++ )
+    {
+        // these handlers block mouse events to any window during scrolling
+        // such as motion events and prevent GTK and wxWidgets from fighting
+        // over where the slider should be
+        g_signal_connect(m_scrollBar[dir], "button_press_event",
+                     G_CALLBACK(gtk_scrollbar_button_press_event), this);
+        g_signal_connect(m_scrollBar[dir], "button_release_event",
+                     G_CALLBACK(gtk_scrollbar_button_release_event), this);
+
+        gulong handler_id = g_signal_connect(m_scrollBar[dir], "event_after",
+                            G_CALLBACK(gtk_scrollbar_event_after), this);
+        g_signal_handler_block(m_scrollBar[dir], handler_id);
+
+        // these handlers get notified when scrollbar slider moves
+        g_signal_connect_after(m_scrollBar[dir], "value_changed",
+                     G_CALLBACK(gtk_scrollbar_value_changed), this);
+    }
+
+    gtk_widget_show( view );
+}
+
 bool wxWindowGTK::Create( wxWindow *parent,
                           wxWindowID id,
                           const wxPoint &pos,
@@ -2242,66 +2336,7 @@ bool wxWindowGTK::Create( wxWindow *parent,
     if (!HasFlag(wxHSCROLL) && !HasFlag(wxVSCROLL))
         m_widget = m_wxwindow;
     else
-    {
-        m_widget = gtk_scrolled_window_new( NULL, NULL );
-
-        GtkScrolledWindow *scrolledWindow = GTK_SCROLLED_WINDOW(m_widget);
-
-        // There is a conflict with default bindings at GTK+
-        // level between scrolled windows and notebooks both of which want to use
-        // Ctrl-PageUp/Down: scrolled windows for scrolling in the horizontal
-        // direction and notebooks for changing pages -- we decide that if we don't
-        // have wxHSCROLL style we can safely sacrifice horizontal scrolling if it
-        // means we can get working keyboard navigation in notebooks
-        if ( !HasFlag(wxHSCROLL) )
-        {
-            GtkBindingSet *
-                bindings = gtk_binding_set_by_class(G_OBJECT_GET_CLASS(m_widget));
-            if ( bindings )
-            {
-                gtk_binding_entry_remove(bindings, GDK_Page_Up, GDK_CONTROL_MASK);
-                gtk_binding_entry_remove(bindings, GDK_Page_Down, GDK_CONTROL_MASK);
-            }
-        }
-
-        if (HasFlag(wxALWAYS_SHOW_SB))
-        {
-            gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS );
-        }
-        else
-        {
-            gtk_scrolled_window_set_policy( scrolledWindow, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
-        }
-
-        m_scrollBar[ScrollDir_Horz] = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(scrolledWindow));
-        m_scrollBar[ScrollDir_Vert] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(scrolledWindow));
-        if (GetLayoutDirection() == wxLayout_RightToLeft)
-            gtk_range_set_inverted( m_scrollBar[ScrollDir_Horz], TRUE );
-
-        gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
-
-        // connect various scroll-related events
-        for ( int dir = 0; dir < ScrollDir_Max; dir++ )
-        {
-            // these handlers block mouse events to any window during scrolling
-            // such as motion events and prevent GTK and wxWidgets from fighting
-            // over where the slider should be
-            g_signal_connect(m_scrollBar[dir], "button_press_event",
-                         G_CALLBACK(gtk_scrollbar_button_press_event), this);
-            g_signal_connect(m_scrollBar[dir], "button_release_event",
-                         G_CALLBACK(gtk_scrollbar_button_release_event), this);
-
-            gulong handler_id = g_signal_connect(m_scrollBar[dir], "event_after",
-                                G_CALLBACK(gtk_scrollbar_event_after), this);
-            g_signal_handler_block(m_scrollBar[dir], handler_id);
-
-            // these handlers get notified when scrollbar slider moves
-            g_signal_connect_after(m_scrollBar[dir], "value_changed",
-                         G_CALLBACK(gtk_scrollbar_value_changed), this);
-        }
-
-        gtk_widget_show( m_wxwindow );
-    }
+        GTKCreateScrolledWindowWith(m_wxwindow);
     g_object_ref(m_widget);
 
     if (m_parent)
@@ -2316,6 +2351,12 @@ bool wxWindowGTK::Create( wxWindow *parent,
     return true;
 }
 
+void wxWindowGTK::GTKDisconnect(void* instance)
+{
+    g_signal_handlers_disconnect_matched(instance,
+        GSignalMatchType(G_SIGNAL_MATCH_DATA), 0, 0, NULL, NULL, this);
+}
+
 wxWindowGTK::~wxWindowGTK()
 {
     SendDestroyEvent();
@@ -2328,23 +2369,14 @@ wxWindowGTK::~wxWindowGTK()
     if ( gs_deferredFocusOut == this )
         gs_deferredFocusOut = NULL;
 
-    m_hasVMT = false;
+    if (m_widget)
+        GTKDisconnect(m_widget);
+    if (m_wxwindow && m_wxwindow != m_widget)
+        GTKDisconnect(m_wxwindow);
 
     // destroy children before destroying this window itself
     DestroyChildren();
 
-    // unhook focus handlers to prevent stray events being
-    // propagated to this (soon to be) dead object
-    if (m_focusWidget != NULL)
-    {
-        g_signal_handlers_disconnect_by_func (m_focusWidget,
-                                              (gpointer) gtk_window_focus_in_callback,
-                                              this);
-        g_signal_handlers_disconnect_by_func (m_focusWidget,
-                                              (gpointer) gtk_window_focus_out_callback,
-                                              this);
-    }
-
     if (m_widget)
         Show( false );
 
@@ -2355,8 +2387,8 @@ wxWindowGTK::~wxWindowGTK()
     // 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
     // gets painted again
-    if (IsFrozen())
-        DoThaw();
+    while (IsFrozen())
+        Thaw();
 
     if (m_widget)
     {
@@ -2437,7 +2469,6 @@ 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
@@ -2482,13 +2513,14 @@ void wxWindowGTK::PostCreation()
     // was in fact realized already.
     if ( gtk_widget_get_realized(connect_widget) )
     {
-        gtk_window_realized_callback(connect_widget, this);
+        GTKHandleRealized();
     }
     else
     {
         g_signal_connect (connect_widget, "realize",
                           G_CALLBACK (gtk_window_realized_callback), this);
     }
+    g_signal_connect(connect_widget, "unrealize", G_CALLBACK(unrealize), this);
 
     if (!IsTopLevel())
     {
@@ -2521,8 +2553,6 @@ void wxWindowGTK::PostCreation()
 
     InheritAttributes();
 
-    m_hasVMT = true;
-
     SetLayoutDirection(wxLayout_Default);
 
     // unless the window was created initially hidden (i.e. Hide() had been
@@ -2567,13 +2597,6 @@ void wxWindowGTK::ConnectWidget( GtkWidget *widget )
                       G_CALLBACK (gtk_window_leave_callback), this);
 }
 
-bool wxWindowGTK::Destroy()
-{
-    m_hasVMT = false;
-
-    return wxWindowBase::Destroy();
-}
-
 static GSList* gs_queueResizeList;
 
 extern "C" {
@@ -3058,52 +3081,18 @@ void wxWindowGTK::DoGetTextExtent( const wxString& string,
                                    int *externalLeading,
                                    const wxFont *theFont ) const
 {
-    wxFont fontToUse = theFont ? *theFont : GetFont();
+    // ensure we work with a valid font
+    wxFont fontToUse;
+    if ( !theFont || !theFont->IsOk() )
+        fontToUse = GetFont();
+    else
+        fontToUse = *theFont;
 
     wxCHECK_RET( fontToUse.IsOk(), wxT("invalid font") );
 
-    if (string.empty())
-    {
-        if (x) (*x) = 0;
-        if (y) (*y) = 0;
-        return;
-    }
-
-    PangoContext *context = NULL;
-    if (m_widget)
-        context = gtk_widget_get_pango_context( m_widget );
-
-    if (!context)
-    {
-        if (x) (*x) = 0;
-        if (y) (*y) = 0;
-        return;
-    }
-
-    PangoFontDescription *desc = fontToUse.GetNativeFontInfo()->description;
-    PangoLayout *layout = pango_layout_new(context);
-    pango_layout_set_font_description(layout, desc);
-    {
-        const wxCharBuffer data = wxGTK_CONV( string );
-        if ( data )
-            pango_layout_set_text(layout, data, strlen(data));
-    }
-
-    PangoRectangle rect;
-    pango_layout_get_extents(layout, NULL, &rect);
-
-    if (x) (*x) = (wxCoord) PANGO_PIXELS(rect.width);
-    if (y) (*y) = (wxCoord) PANGO_PIXELS(rect.height);
-    if (descent)
-    {
-        PangoLayoutIter *iter = pango_layout_get_iter(layout);
-        int baseline = pango_layout_iter_get_baseline(iter);
-        pango_layout_iter_free(iter);
-        *descent = *y - PANGO_PIXELS(baseline);
-    }
-    if (externalLeading) (*externalLeading) = 0;  // ??
-
-    g_object_unref (layout);
+    const wxWindow* win = static_cast<const wxWindow*>(this);
+    wxTextMeasure txm(win, &fontToUse);
+    txm.GetTextExtent(string, x, y, descent, externalLeading);
 }
 
 void wxWindowGTK::GTKDisableFocusOutEvent()
@@ -4528,7 +4517,7 @@ wxEventType wxWindowGTK::GTKGetScrollEventType(GtkRange* range)
     // update current position
     m_scrollPos[barIndex] = value;
     // If event should be ignored, or integral position has not changed
-    if (!m_hasVMT || g_blockEventsOnDrag || wxRound(value) == wxRound(oldPos))
+    if (g_blockEventsOnDrag || wxRound(value) == wxRound(oldPos))
     {
         return wxEVT_NULL;
     }
@@ -4652,91 +4641,38 @@ GdkWindow* wxWindowGTK::GTKGetDrawingWindow() const
 // freeze/thaw
 // ----------------------------------------------------------------------------
 
-extern "C"
-{
-
-// this is called if we attempted to freeze unrealized widget when it finally
-// is realized (and so can be frozen):
-static void wx_frozen_widget_realize(GtkWidget* w, wxWindowGTK* win)
-{
-    wxASSERT( w && gtk_widget_get_has_window(w) );
-    wxASSERT( gtk_widget_get_realized(w) );
-
-    g_signal_handlers_disconnect_by_func
-    (
-        w,
-        (void*)wx_frozen_widget_realize,
-        win
-    );
-
-    GdkWindow* window;
-    if (w == win->m_wxwindow)
-        window = win->GTKGetDrawingWindow();
-    else
-        window = gtk_widget_get_window(w);
-    gdk_window_freeze_updates(window);
-}
-
-} // extern "C"
-
-void wxWindowGTK::GTKFreezeWidget(GtkWidget *w)
+void wxWindowGTK::GTKFreezeWidget(GtkWidget* widget)
 {
-    if ( !w || !gtk_widget_get_has_window(w) )
-        return; // window-less widget, cannot be frozen
-
-    GdkWindow* window = gtk_widget_get_window(w);
-    if (window == NULL)
+    if (widget && gtk_widget_get_has_window(widget))
     {
-        // we can't thaw unrealized widgets because they don't have GdkWindow,
-        // so set it up to be done immediately after realization:
-        g_signal_connect_after
-        (
-            w,
-            "realize",
-            G_CALLBACK(wx_frozen_widget_realize),
-            this
-        );
-        return;
+        GdkWindow* window = gtk_widget_get_window(widget);
+        if (window)
+            gdk_window_freeze_updates(window);
     }
-
-    if (w == m_wxwindow)
-        window = GTKGetDrawingWindow();
-    gdk_window_freeze_updates(window);
 }
 
-void wxWindowGTK::GTKThawWidget(GtkWidget *w)
+void wxWindowGTK::GTKThawWidget(GtkWidget* widget)
 {
-    if ( !w || !gtk_widget_get_has_window(w) )
-        return; // window-less widget, cannot be frozen
-
-    GdkWindow* window = gtk_widget_get_window(w);
-    if (window == NULL)
+    if (widget && gtk_widget_get_has_window(widget))
     {
-        // the widget wasn't realized yet, no need to thaw
-        g_signal_handlers_disconnect_by_func
-        (
-            w,
-            (void*)wx_frozen_widget_realize,
-            this
-        );
-        return;
+        GdkWindow* window = gtk_widget_get_window(widget);
+        if (window)
+            gdk_window_thaw_updates(window);
     }
-
-    if (w == m_wxwindow)
-        window = GTKGetDrawingWindow();
-    gdk_window_thaw_updates(window);
 }
 
 void wxWindowGTK::DoFreeze()
 {
-    GTKFreezeWidget(m_widget);
-    if ( m_wxwindow && m_widget != m_wxwindow )
-        GTKFreezeWidget(m_wxwindow);
+    GtkWidget* widget = m_wxwindow;
+    if (widget == NULL)
+        widget = m_widget;
+    GTKFreezeWidget(widget);
 }
 
 void wxWindowGTK::DoThaw()
 {
-    GTKThawWidget(m_widget);
-    if ( m_wxwindow && m_widget != m_wxwindow )
-        GTKThawWidget(m_wxwindow);
+    GtkWidget* widget = m_wxwindow;
+    if (widget == NULL)
+        widget = m_widget;
+    GTKThawWidget(widget);
 }