+    wxSize best(80, ret.y);
+    CacheBestSize(best);
+    return best;
+}
+
+// ----------------------------------------------------------------------------
+// freeze/thaw
+// ----------------------------------------------------------------------------
+
+void wxTextCtrl::Freeze()
+{
+    if ( HasFlag(wxTE_MULTILINE) )
+    {
+#ifdef __WXGTK20__
+        if ( !m_frozenness++ )
+        {
+            // freeze textview updates and remove buffer
+            g_signal_connect( G_OBJECT(m_text), "expose_event",
+                GTK_SIGNAL_FUNC(gtk_text_exposed_callback), (gpointer)this);
+            g_signal_connect( G_OBJECT(m_widget), "expose_event",
+                GTK_SIGNAL_FUNC(gtk_text_exposed_callback), (gpointer)this);
+            gtk_widget_set_sensitive(m_widget, false);
+            g_object_ref(m_buffer);
+            gtk_text_view_set_buffer(GTK_TEXT_VIEW(m_text), gtk_text_buffer_new(NULL));
+    }
+#else
+        gtk_text_freeze(GTK_TEXT(m_text));
+#endif
+    }
+}
+
+void wxTextCtrl::Thaw()
+{
+    if ( HasFlag(wxTE_MULTILINE) )
+    {
+#ifdef __WXGTK20__
+        wxASSERT_MSG( m_frozenness > 0, _T("Thaw() without matching Freeze()") );
+
+        if ( !--m_frozenness )
+        {
+            // 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);
+        }
+#else
+        GTK_TEXT(m_text)->vadj->value = 0.0;
+
+        gtk_text_thaw(GTK_TEXT(m_text));
+#endif
+    }
+}
+
+// ----------------------------------------------------------------------------
+// wxTextUrlEvent passing if style & wxTE_AUTO_URL
+// ----------------------------------------------------------------------------
+
+#ifdef __WXGTK20__
+
+// FIXME: when dragging on a link the sample gets an "Unknown event".
+// This might be an excessive event from us or a buggy wxMouseEvent::Moving() or
+// a buggy sample, or something else
+void wxTextCtrl::OnUrlMouseEvent(wxMouseEvent& event)
+{
+    event.Skip();
+    if(!(m_windowStyle & wxTE_AUTO_URL))
+        return;
+
+    gint x, y;
+    GtkTextIter start, end;
+    GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(m_buffer),
+                                                "wxUrl");
+
+    gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(m_text), GTK_TEXT_WINDOW_WIDGET,
+                                          event.GetX(), event.GetY(), &x, &y);
+
+    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);
+        return;
+    }
+
+    gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(m_text),
+                          GTK_TEXT_WINDOW_TEXT), m_gdkHandCursor);
+
+    start = end;
+    if(!gtk_text_iter_begins_tag(&start, tag))
+        gtk_text_iter_backward_to_tag_toggle(&start, tag);
+    if(!gtk_text_iter_ends_tag(&end, tag))
+        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
+    if(event.GetEventType() == wxEVT_RIGHT_DOWN)
+        event.Skip(false);
+
+    wxTextUrlEvent url_event(m_windowId, event,
+                             gtk_text_iter_get_offset(&start),
+                             gtk_text_iter_get_offset(&end));
+
+    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);
+}
+#endif // gtk2
+
+// ----------------------------------------------------------------------------
+// scrolling
+// ----------------------------------------------------------------------------
+
+GtkAdjustment *wxTextCtrl::GetVAdj() const
+{
+    if ( !IsMultiLine() )
+        return NULL;
+
+#ifdef __WXGTK20__
+    return gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(m_widget));
+#else
+    return GTK_TEXT(m_text)->vadj;
+#endif
+}
+
+bool wxTextCtrl::DoScroll(GtkAdjustment *adj, int diff)
+{
+    float value = adj->value + diff;
+
+    if ( value < 0 )
+        value = 0;
+
+    float upper = adj->upper - adj->page_size;
+    if ( value > upper )
+        value = upper;
+
+    // did we noticeably change the scroll position?
+    if ( fabs(adj->value - value) < 0.2 )
+    {
+        // well, this is what Robert does in wxScrollBar, so it must be good...
+        return false;
+    }
+
+    adj->value = value;
+
+#ifdef __WXGTK20__
+    gtk_adjustment_value_changed(GTK_ADJUSTMENT(adj));
+#else
+    gtk_signal_emit_by_name(GTK_OBJECT(adj), "value_changed");
+#endif
+
+    return true;
+}
+
+bool wxTextCtrl::ScrollLines(int lines)
+{
+    GtkAdjustment *adj = GetVAdj();
+    if ( !adj )
+        return false;
+
+#ifdef __WXGTK20__
+    int diff = (int)ceil(lines*adj->step_increment);
+#else
+    // this is hardcoded to 10 in GTK+ 1.2 (great idea)
+    int diff = 10*lines;
+#endif
+
+    return DoScroll(adj, diff);
+}
+
+bool wxTextCtrl::ScrollPages(int pages)
+{
+    GtkAdjustment *adj = GetVAdj();
+    if ( !adj )
+        return false;
+
+    return DoScroll(adj, (int)ceil(pages*adj->page_increment));
+}
+
+
+// static
+wxVisualAttributes
+wxTextCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
+{
+    return GetDefaultAttributesFromGTKWidget(gtk_entry_new, true);