]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk/textctrl.cpp
Force wide char environment version initialization in wxGetEnvMap().
[wxWidgets.git] / src / gtk / textctrl.cpp
index ccdba0721ec09a11513f568f1262aa8c7c2a532a..12d35c479b482cd8252891f4271387d602881faf 100644 (file)
@@ -22,6 +22,7 @@
     #include "wx/math.h"
 #endif
 
+#include "wx/scopeguard.h"
 #include "wx/strconv.h"
 #include "wx/fontutil.h"        // for wxNativeFontInfo (GetNativeFontInfo())
 
@@ -109,6 +110,18 @@ static void wxGtkTextApplyTagsFromAttr(GtkWidget *text,
                                                   NULL );
             gtk_text_buffer_apply_tag (text_buffer, tag, start, end);
         }
+        if ( font.GetStrikethrough() )
+        {
+            g_snprintf(buf, sizeof(buf), "WXFONTSTRIKETHROUGH");
+            tag = gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer ),
+                                             buf );
+            if (!tag)
+                tag = gtk_text_buffer_create_tag( text_buffer, buf,
+                                                  "strikethrough-set", TRUE,
+                                                  "strikethrough", TRUE,
+                                                  NULL );
+            gtk_text_buffer_apply_tag (text_buffer, tag, start, end);
+        }
     }
 
     if (attr.HasTextColour())
@@ -149,7 +162,7 @@ static void wxGtkTextApplyTagsFromAttr(GtkWidget *text,
                                           gtk_text_iter_get_line(start) );
         gtk_text_iter_forward_line(&para_end);
 
-        wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXALIGNMENT", start, end);
+        wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXALIGNMENT", &para_start, &para_end);
 
         GtkJustification align;
         switch (attr.GetAlignment())
@@ -195,7 +208,7 @@ static void wxGtkTextApplyTagsFromAttr(GtkWidget *text,
                                           gtk_text_iter_get_line(start) );
         gtk_text_iter_forward_line(&para_end);
 
-        wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXINDENT", start, end);
+        wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXINDENT", &para_start, &para_end);
 
         // Convert indent from 1/10th of a mm into pixels
         float factor =
@@ -239,14 +252,14 @@ static void wxGtkTextApplyTagsFromAttr(GtkWidget *text,
                                           gtk_text_iter_get_line(start) );
         gtk_text_iter_forward_line(&para_end);
 
-        wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXTABS", start, end);
+        wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXTABS", &para_start, &para_end);
 
         const wxArrayInt& tabs = attr.GetTabs();
 
-        wxString tagname = _T("WXTABS");
+        wxString tagname = wxT("WXTABS");
         g_snprintf(buf, sizeof(buf), "WXTABS");
         for (size_t i = 0; i < tabs.GetCount(); i++)
-            tagname += wxString::Format(_T(" %d"), tabs[i]);
+            tagname += wxString::Format(wxT(" %d"), tabs[i]);
 
         const wxWX2MBbuf buftag = tagname.utf8_str();
 
@@ -350,7 +363,7 @@ extern "C" {
 static void
 au_check_word( GtkTextIter *s, GtkTextIter *e )
 {
-    static const char *URIPrefixes[] =
+    static const char *const URIPrefixes[] =
     {
         "http://",
         "ftp://",
@@ -390,7 +403,7 @@ au_check_word( GtkTextIter *s, GtkTextIter *e )
     for( n = 0; n < WXSIZEOF(URIPrefixes); ++n )
     {
         prefix_len = strlen(URIPrefixes[n]);
-        if((len > prefix_len) && !strncasecmp(text, URIPrefixes[n], prefix_len))
+        if((len > prefix_len) && !wxStrnicmp(text, URIPrefixes[n], prefix_len))
             break;
     }
 
@@ -494,6 +507,27 @@ au_delete_range_callback(GtkTextBuffer * WXUNUSED(buffer),
 }
 }
 
+//-----------------------------------------------------------------------------
+//  "populate_popup" from text control and "unmap" from its poup menu
+//-----------------------------------------------------------------------------
+
+extern "C" {
+static void
+gtk_textctrl_popup_unmap( GtkMenu *WXUNUSED(menu), wxTextCtrl* win )
+{
+    win->GTKEnableFocusOutEvent();
+}
+}
+
+extern "C" {
+static void
+gtk_textctrl_populate_popup( GtkEntry *WXUNUSED(entry), GtkMenu *menu, wxTextCtrl *win )
+{
+    win->GTKDisableFocusOutEvent();
+
+    g_signal_connect (menu, "unmap", G_CALLBACK (gtk_textctrl_popup_unmap), win );
+}
+}
 
 //-----------------------------------------------------------------------------
 //  "changed"
@@ -501,7 +535,7 @@ au_delete_range_callback(GtkTextBuffer * WXUNUSED(buffer),
 
 extern "C" {
 static void
-gtk_text_changed_callback( GtkWidget * WXUNUSED(widget), wxTextCtrl *win )
+gtk_text_changed_callback( GtkWidget *WXUNUSED(widget), wxTextCtrl *win )
 {
     if ( win->IgnoreTextUpdate() )
         return;
@@ -526,7 +560,7 @@ handle_text_clipboard_callback( GtkWidget *widget, wxTextCtrl *win,
 {
     wxClipboardTextEvent event( eventType, win->GetId() );
     event.SetEventObject( win );
-    if ( win->GetEventHandler()->ProcessEvent( event ) )
+    if ( win->HandleWindowEvent( event ) )
     {
         // don't let the default processing to take place if we did something
         // ourselves in the event handler
@@ -558,26 +592,21 @@ gtk_paste_clipboard_callback( GtkWidget *widget, wxTextCtrl *win )
 }
 
 //-----------------------------------------------------------------------------
-// "expose_event" from scrolled window and textview
+//  "mark_set"
 //-----------------------------------------------------------------------------
 
 extern "C" {
-static gboolean
-gtk_text_exposed_callback( GtkWidget * WXUNUSED(widget),
-                           GdkEventExpose * WXUNUSED(event),
-                           wxTextCtrl * WXUNUSED(win) )
+static void mark_set(GtkTextBuffer*, GtkTextIter*, GtkTextMark* mark, GSList** markList)
 {
-    return TRUE;
+    if (gtk_text_mark_get_name(mark) == NULL)
+        *markList = g_slist_prepend(*markList, mark);
 }
 }
 
-
 //-----------------------------------------------------------------------------
 //  wxTextCtrl
 //-----------------------------------------------------------------------------
 
-IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxTextCtrlBase)
-
 BEGIN_EVENT_TABLE(wxTextCtrl, wxTextCtrlBase)
     EVT_CHAR(wxTextCtrl::OnChar)
 
@@ -614,18 +643,14 @@ void wxTextCtrl::Init()
     SetUpdateFont(false);
 
     m_text = NULL;
-    m_freezeCount = 0;
     m_showPositionOnThaw = NULL;
-    m_gdkHandCursor = NULL;
-    m_gdkXTermCursor = NULL;
+    m_anonymousMarkList = NULL;
 }
 
 wxTextCtrl::~wxTextCtrl()
 {
-    if(m_gdkHandCursor)
-        gdk_cursor_unref(m_gdkHandCursor);
-    if(m_gdkXTermCursor)
-        gdk_cursor_unref(m_gdkXTermCursor);
+    if (m_anonymousMarkList)
+        g_slist_free(m_anonymousMarkList);
 }
 
 wxTextCtrl::wxTextCtrl( wxWindow *parent,
@@ -662,10 +687,13 @@ bool wxTextCtrl::Create( wxWindow *parent,
 
     if (multi_line)
     {
+        m_buffer = gtk_text_buffer_new(NULL);
+        gulong sig_id = g_signal_connect(m_buffer, "mark_set", G_CALLBACK(mark_set), &m_anonymousMarkList);
         // Create view
-        m_text = gtk_text_view_new();
-
-        m_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(m_text) );
+        m_text = gtk_text_view_new_with_buffer(m_buffer);
+        // gtk_text_view_set_buffer adds its own reference
+        g_object_unref(m_buffer);
+        g_signal_handler_disconnect(m_buffer, sig_id);
 
         // create "ShowPosition" marker
         GtkTextIter iter;
@@ -680,28 +708,33 @@ bool wxTextCtrl::Create( wxWindow *parent,
                                             ? GTK_POLICY_NEVER
                                             : GTK_POLICY_AUTOMATIC );
         // for ScrollLines/Pages
-        m_scrollBar[1] = (GtkRange*)((GtkScrolledWindow*)m_widget)->vscrollbar;
+        m_scrollBar[1] = GTK_RANGE(gtk_scrolled_window_get_vscrollbar(GTK_SCROLLED_WINDOW(m_widget)));
 
         // Insert view into scrolled window
         gtk_container_add( GTK_CONTAINER(m_widget), m_text );
 
         GTKSetWrapMode();
 
-        GtkScrolledWindowSetBorder(m_widget, style);
+        GTKScrolledWindowSetBorder(m_widget, style);
 
         gtk_widget_add_events( GTK_WIDGET(m_text), GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK );
 
-        GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
+        gtk_widget_set_can_focus(m_widget, FALSE);
     }
     else
     {
         // a single-line text control: no need for scrollbars
         m_widget =
         m_text = gtk_entry_new();
+        // work around probable bug in GTK+ 2.18 when calling WriteText on a
+        // new, empty control, see http://trac.wxwidgets.org/ticket/11409
+        gtk_entry_get_text((GtkEntry*)m_text);
 
         if (style & wxNO_BORDER)
             g_object_set (m_text, "has-frame", FALSE, NULL);
+
     }
+    g_object_ref(m_widget);
 
     m_parent->DoAddChild( this );
 
@@ -726,6 +759,11 @@ bool wxTextCtrl::Create( wxWindow *parent,
                           G_CALLBACK (gtk_text_changed_callback), this);
     }
 
+    // Catch to disable focus out handling
+    g_signal_connect (m_text, "populate_popup",
+                      G_CALLBACK (gtk_textctrl_populate_popup),
+                      this);
+
     if (!value.empty())
     {
         SetValue( value );
@@ -737,9 +775,6 @@ bool wxTextCtrl::Create( wxWindow *parent,
     if (style & wxTE_READONLY)
         GTKSetEditable();
 
-    if (style & wxTE_PROCESS_ENTER)
-        GTKSetActivatesDefault();
-
     // left justification (alignment) is the default anyhow
     if ( style & (wxTE_RIGHT | wxTE_CENTRE) )
         GTKSetJustification();
@@ -750,11 +785,9 @@ bool wxTextCtrl::Create( wxWindow *parent,
         if (style & wxTE_AUTO_URL)
         {
             GtkTextIter start, end;
-            m_gdkHandCursor = gdk_cursor_new(GDK_HAND2);
-            m_gdkXTermCursor = gdk_cursor_new(GDK_XTERM);
 
             // We create our wxUrl tag here for slight efficiency gain - we
-            // don't have to check for the tag existance in callbacks,
+            // don't have to check for the tag existence in callbacks,
             // hereby it's guaranteed to exist.
             gtk_text_buffer_create_tag(m_buffer, "wxUrl",
                                        "foreground", "blue",
@@ -782,6 +815,13 @@ bool wxTextCtrl::Create( wxWindow *parent,
             au_check_range(&start, &end);
         }
     }
+    else // single line
+    {
+        // do the right thing with Enter presses depending on whether we have
+        // wxTE_PROCESS_ENTER or not
+        GTKSetActivatesDefault();
+    }
+
 
     g_signal_connect (m_text, "copy-clipboard",
                       G_CALLBACK (gtk_copy_clipboard_callback), this);
@@ -802,6 +842,11 @@ GtkEditable *wxTextCtrl::GetEditable() const
     return GTK_EDITABLE(m_text);
 }
 
+GtkEntry *wxTextCtrl::GetEntry() const
+{
+    return GTK_ENTRY(m_text);
+}
+
 // ----------------------------------------------------------------------------
 // flags handling
 // ----------------------------------------------------------------------------
@@ -966,6 +1011,22 @@ void wxTextCtrl::DoSetValue( const wxString &value, int flags )
         return;
     }
 
+    if (value.IsEmpty())
+    {
+        if ( !(flags & SetValue_SendEvent) )
+            EnableTextChangedEvents(false);
+
+        gtk_text_buffer_set_text( m_buffer, "", 0 );
+
+        if ( !(flags & SetValue_SendEvent) )
+            EnableTextChangedEvents(true);
+
+        return;
+    }
+
+#if wxUSE_UNICODE
+    const wxCharBuffer buffer(value.utf8_str());
+#else
     wxFontEncoding enc = m_defaultStyle.HasFont()
                             ? m_defaultStyle.GetFont().GetEncoding()
                             : wxFONTENCODING_SYSTEM;
@@ -980,6 +1041,7 @@ void wxTextCtrl::DoSetValue( const wxString &value, int flags )
         wxLogWarning(_("Failed to set text in the text control."));
         return;
     }
+#endif
 
     if ( !(flags & SetValue_SendEvent) )
     {
@@ -1000,9 +1062,6 @@ void wxTextCtrl::DoSetValue( const wxString &value, int flags )
     {
         EnableTextChangedEvents(true);
     }
-
-    // This was added after discussion on the list
-    SetInsertionPoint(0);
 }
 
 void wxTextCtrl::WriteText( const wxString &text )
@@ -1018,6 +1077,9 @@ void wxTextCtrl::WriteText( const wxString &text )
         return;
     }
 
+#if wxUSE_UNICODE
+    const wxCharBuffer buffer(text.utf8_str());
+#else
     // check if we have a specific style for the current position
     wxFontEncoding enc = wxFONTENCODING_SYSTEM;
     wxTextAttr style;
@@ -1038,6 +1100,7 @@ void wxTextCtrl::WriteText( const wxString &text )
         wxLogWarning(_("Failed to insert text in the control."));
         return;
     }
+#endif
 
     // First remove the selection if there is one
     // TODO:  Is there an easier GTK specific way to do this?
@@ -1049,13 +1112,19 @@ void wxTextCtrl::WriteText( const wxString &text )
     // Insert the text
     wxGtkTextInsert( m_text, m_buffer, m_defaultStyle, buffer );
 
-    GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment( GTK_SCROLLED_WINDOW(m_widget) );
     // Scroll to cursor, but only if scrollbar thumb is at the very bottom
     // won't work when frozen, text view is not using m_buffer then
-    if (!IsFrozen() && wxIsSameDouble(adj->value, adj->upper - adj->page_size))
+    if (!IsFrozen())
     {
-        gtk_text_view_scroll_to_mark( GTK_TEXT_VIEW(m_text),
-                gtk_text_buffer_get_insert( m_buffer ), 0.0, FALSE, 0.0, 1.0 );
+        GtkAdjustment* adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(m_widget));
+        const double value = gtk_adjustment_get_value(adj);
+        const double upper = gtk_adjustment_get_upper(adj);
+        const double page_size = gtk_adjustment_get_page_size(adj);
+        if (wxIsSameDouble(value, upper - page_size))
+        {
+            gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(m_text),
+                gtk_text_buffer_get_insert(m_buffer), 0, false, 0, 1);
+        }
     }
 }
 
@@ -1108,7 +1177,7 @@ bool wxTextCtrl::PositionToXY(long pos, long *x, long *y ) const
     }
     else // single line control
     {
-        if ( pos <= GTK_ENTRY(m_text)->text_length )
+        if (pos <= gtk_entry_get_text_length(GTK_ENTRY(m_text)))
         {
             if ( y )
                 *y = 0;
@@ -1161,6 +1230,37 @@ int wxTextCtrl::GetLineLength(long lineNo) const
     }
 }
 
+wxPoint wxTextCtrl::DoPositionToCoords(long pos) const
+{
+    if ( !IsMultiLine() )
+    {
+        // Single line text entry (GtkTextEntry) doesn't have support for
+        // getting the coordinates for the given offset. Perhaps we could
+        // find them ourselves by using GetTextExtent() but for now just leave
+        // it unimplemented, this function is more useful for multiline
+        // controls anyhow.
+        return wxDefaultPosition;
+    }
+
+    // Window coordinates for the given position is calculated by getting
+    // the buffer coordinates and converting them to window coordinates.
+    GtkTextView *textview = GTK_TEXT_VIEW(m_text);
+
+    GtkTextIter iter;
+    gtk_text_buffer_get_iter_at_offset(m_buffer, &iter, pos);
+
+    GdkRectangle bufferCoords;
+    gtk_text_view_get_iter_location(textview, &iter, &bufferCoords);
+
+    gint winCoordX = 0,
+         winCoordY = 0;
+    gtk_text_view_buffer_to_window_coords(textview, GTK_TEXT_WINDOW_WIDGET,
+                                          bufferCoords.x, bufferCoords.y,
+                                          &winCoordX, &winCoordY);
+
+    return wxPoint(winCoordX, winCoordY);
+}
+
 int wxTextCtrl::GetNumberOfLines() const
 {
     if ( IsMultiLine() )
@@ -1218,6 +1318,7 @@ bool wxTextCtrl::Enable( bool enable )
     }
 
     gtk_widget_set_sensitive( m_text, enable );
+    SetCursor(enable ? wxCursor(wxCURSOR_IBEAM) : wxCursor());
 
     return true;
 }
@@ -1231,7 +1332,7 @@ void wxTextCtrl::OnEnabled(bool WXUNUSED(enable))
     // disabled and enabled mode, or we end up with a different colour under the
     // text.
     wxColour oldColour = GetBackgroundColour();
-    if (oldColour.Ok())
+    if (oldColour.IsOk())
     {
         // Need to set twice or it'll optimize the useful stuff out
         if (oldColour == * wxWHITE)
@@ -1298,20 +1399,19 @@ void wxTextCtrl::SetSelection( long from, long to )
 {
     wxCHECK_RET( m_text != NULL, wxT("invalid text ctrl") );
 
-    if (from == -1 && to == -1)
-    {
-        from = 0;
-        to = GetValue().length();
-    }
-
     if ( IsMultiLine() )
     {
+        if (from == -1 && to == -1)
+        {
+            from = 0;
+            to = GetValue().length();
+        }
+
         GtkTextIter fromi, toi;
         gtk_text_buffer_get_iter_at_offset( m_buffer, &fromi, from );
         gtk_text_buffer_get_iter_at_offset( m_buffer, &toi, to );
 
-        gtk_text_buffer_place_cursor( m_buffer, &toi );
-        gtk_text_buffer_move_mark_by_name( m_buffer, "selection_bound", &fromi );
+        gtk_text_buffer_select_range( m_buffer, &fromi, &toi );
     }
     else // single line
     {
@@ -1499,7 +1599,7 @@ bool wxTextCtrl::IsEditable() const
 
     if ( IsMultiLine() )
     {
-        return gtk_text_view_get_editable(GTK_TEXT_VIEW(m_text));
+        return gtk_text_view_get_editable(GTK_TEXT_VIEW(m_text)) != 0;
     }
     else
     {
@@ -1523,7 +1623,7 @@ void wxTextCtrl::OnChar( wxKeyEvent &key_event )
             wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
             event.SetEventObject(this);
             event.SetString(GetValue());
-            if ( GetEventHandler()->ProcessEvent(event) )
+            if ( HandleWindowEvent(event) )
                 return;
         }
     }
@@ -1545,7 +1645,7 @@ GdkWindow *wxTextCtrl::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
     }
     else
     {
-        return GTK_ENTRY(m_text)->text_area;
+        return gtk_entry_get_text_window(GTK_ENTRY(m_text));
     }
 }
 
@@ -1579,7 +1679,7 @@ void wxTextCtrl::ChangeFontGlobally()
     //
     // TODO: it can be implemented much more efficiently for GTK2
     wxASSERT_MSG( IsMultiLine(),
-                  _T("shouldn't be called for single line controls") );
+                  wxT("shouldn't be called for single line controls") );
 
     wxString value = GetValue();
     if ( !value.empty() )
@@ -1609,7 +1709,7 @@ bool wxTextCtrl::SetBackgroundColour( const wxColour &colour )
     if ( !wxControl::SetBackgroundColour( colour ) )
         return false;
 
-    if (!m_backgroundColour.Ok())
+    if (!m_backgroundColour.IsOk())
         return false;
 
     // change active background color too
@@ -1631,7 +1731,7 @@ bool wxTextCtrl::SetStyle( long start, long end, const wxTextAttr& style )
         gint l = gtk_text_buffer_get_char_count( m_buffer );
 
         wxCHECK_MSG( start >= 0 && end <= l, false,
-                     _T("invalid range in wxTextCtrl::SetStyle") );
+                     wxT("invalid range in wxTextCtrl::SetStyle") );
 
         GtkTextIter starti, endi;
         gtk_text_buffer_get_iter_at_offset( m_buffer, &starti, start );
@@ -1646,6 +1746,50 @@ bool wxTextCtrl::SetStyle( long start, long end, const wxTextAttr& style )
     return false;
 }
 
+bool wxTextCtrl::GetStyle(long position, wxTextAttr& style)
+{
+    if ( !IsMultiLine() )
+    {
+        // no styles for GtkEntry
+        return false;
+    }
+
+    gint l = gtk_text_buffer_get_char_count( m_buffer );
+
+    wxCHECK_MSG( position >= 0 && position <= l, false,
+                 wxT("invalid range in wxTextCtrl::GetStyle") );
+
+    GtkTextIter positioni;
+    gtk_text_buffer_get_iter_at_offset(m_buffer, &positioni, position);
+
+    // Obtain a copy of the default attributes
+    GtkTextAttributes * const
+        pattr = gtk_text_view_get_default_attributes(GTK_TEXT_VIEW(m_text));
+    wxON_BLOCK_EXIT1(gtk_text_attributes_unref, pattr);
+
+    // And query GTK for the attributes at the given position using it as base
+    if ( !gtk_text_iter_get_attributes(&positioni, pattr) )
+    {
+        style = m_defaultStyle;
+    }
+    else // have custom attributes
+    {
+        style.SetBackgroundColour(pattr->appearance.bg_color);
+        style.SetTextColour(pattr->appearance.fg_color);
+
+        const wxGtkString
+            pangoFontString(pango_font_description_to_string(pattr->font));
+
+        wxFont font;
+        if ( font.SetNativeFontInfo(wxString(pangoFontString)) )
+            style.SetFont(font);
+
+        // TODO: set alignment, tabs and indents
+    }
+
+    return true;
+}
+
 void wxTextCtrl::DoApplyWidgetStyle(GtkRcStyle *style)
 {
     gtk_widget_modify_style(m_text, style);
@@ -1714,60 +1858,62 @@ wxSize wxTextCtrl::DoGetBestSize() const
 // freeze/thaw
 // ----------------------------------------------------------------------------
 
-void wxTextCtrl::Freeze()
+void wxTextCtrl::DoFreeze()
 {
     wxCHECK_RET(m_text != NULL, wxT("invalid text ctrl"));
 
+    wxWindow::DoFreeze();
+
     if ( HasFlag(wxTE_MULTILINE) )
     {
-        if (m_freezeCount++ == 0)
+        GTKFreezeWidget(m_text);
+
+        // removing buffer dramatically speeds up insertion:
+        g_object_ref(m_buffer);
+        GtkTextBuffer* buf_new = gtk_text_buffer_new(NULL);
+        gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text), buf_new);
+        // gtk_text_view_set_buffer adds its own reference
+        g_object_unref(buf_new);
+        // These marks should be deleted when the buffer is changed,
+        // but they are not (in GTK+ up to at least 3.0.1).
+        // Otherwise these anonymous marks start to build up in the buffer,
+        // and Freeze takes longer and longer each time it is called.
+        if (m_anonymousMarkList)
         {
-            // freeze textview updates and remove buffer
-            g_signal_connect (m_text, "expose_event",
-                              G_CALLBACK (gtk_text_exposed_callback), this);
-            g_signal_connect (m_widget, "expose_event",
-                              G_CALLBACK (gtk_text_exposed_callback), this);
-            gtk_widget_set_sensitive(m_widget, false);
-            g_object_ref(m_buffer);
-            GtkTextBuffer* buf_new = gtk_text_buffer_new(NULL);
-            GtkTextMark* mark = GTK_TEXT_VIEW(m_text)->first_para_mark;
-            gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text), buf_new);
-            // gtk_text_view_set_buffer adds its own reference
-            g_object_unref(buf_new);
-            // This mark should be deleted when the buffer is changed,
-            // but it's not (in GTK+ up to at least 2.10.6).
-            // Otherwise these anonymous marks start to build up in the buffer,
-            // and Freeze takes longer and longer each time it is called.
-            if (GTK_IS_TEXT_MARK(mark) && !gtk_text_mark_get_deleted(mark))
-                gtk_text_buffer_delete_mark(m_buffer, mark);
+            for (GSList* item = m_anonymousMarkList; item; item = item->next)
+            {
+                GtkTextMark* mark = static_cast<GtkTextMark*>(item->data);
+                if (GTK_IS_TEXT_MARK(mark) && !gtk_text_mark_get_deleted(mark))
+                    gtk_text_buffer_delete_mark(m_buffer, mark);
+            }
+            g_slist_free(m_anonymousMarkList);
+            m_anonymousMarkList = NULL;
         }
     }
 }
 
-void wxTextCtrl::Thaw()
+void wxTextCtrl::DoThaw()
 {
     if ( HasFlag(wxTE_MULTILINE) )
     {
-        wxCHECK_RET(m_freezeCount != 0, _T("Thaw() without matching Freeze()"));
+        // reattach buffer:
+        gulong sig_id = g_signal_connect(m_buffer, "mark_set", G_CALLBACK(mark_set), &m_anonymousMarkList);
+        gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text), m_buffer);
+        g_object_unref(m_buffer);
+        g_signal_handler_disconnect(m_buffer, sig_id);
 
-        if (--m_freezeCount == 0)
+        if (m_showPositionOnThaw != NULL)
         {
-            // Reattach buffer and thaw textview updates
-            gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text), m_buffer);
-            g_object_unref(m_buffer);
-            gtk_widget_set_sensitive(m_widget, true);
-            g_signal_handlers_disconnect_by_func (m_widget,
-                    (gpointer) gtk_text_exposed_callback, this);
-            g_signal_handlers_disconnect_by_func (m_text,
-                    (gpointer) gtk_text_exposed_callback, this);
-            if (m_showPositionOnThaw != NULL)
-            {
-                gtk_text_view_scroll_mark_onscreen(
-                    GTK_TEXT_VIEW(m_text), m_showPositionOnThaw);
-                m_showPositionOnThaw = NULL;
-            }
+            gtk_text_view_scroll_mark_onscreen(
+                GTK_TEXT_VIEW(m_text), m_showPositionOnThaw);
+            m_showPositionOnThaw = NULL;
         }
+
+        // and thaw the window
+        GTKThawWidget(m_text);
     }
+
+    wxWindow::DoThaw();
 }
 
 // ----------------------------------------------------------------------------
@@ -1794,13 +1940,11 @@ void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent& event)
     gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(m_text), &end, x, y);
     if (!gtk_text_iter_has_tag(&end, tag))
     {
-        gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(m_text),
-                              GTK_TEXT_WINDOW_TEXT), m_gdkXTermCursor);
+        SetCursor(wxCursor(wxCURSOR_IBEAM));
         return;
     }
 
-    gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(m_text),
-                          GTK_TEXT_WINDOW_TEXT), m_gdkHandCursor);
+    SetCursor(wxCursor(wxCURSOR_HAND));
 
     start = end;
     if(!gtk_text_iter_begins_tag(&start, tag))
@@ -1809,7 +1953,7 @@ void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent& event)
         gtk_text_iter_forward_to_tag_toggle(&end, tag);
 
     // Native context menu is probably not desired on an URL.
-    // Consider making this dependant on ProcessEvent(wxTextUrlEvent) return value
+    // Consider making this dependent on ProcessEvent(wxTextUrlEvent) return value
     if(event.GetEventType() == wxEVT_RIGHT_DOWN)
         event.Skip(false);
 
@@ -1819,8 +1963,8 @@ void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent& event)
 
     InitCommandEvent(url_event);
     // Is that a good idea? Seems not (pleasure with gtk_text_view_start_selection_drag)
-    //event.Skip(!GetEventHandler()->ProcessEvent(url_event));
-    GetEventHandler()->ProcessEvent(url_event);
+    //event.Skip(!HandleWindowEvent(url_event));
+    HandleWindowEvent(url_event);
 }
 
 bool wxTextCtrl::GTKProcessEvent(wxEvent& event) const