+extern "C" {
+static gboolean
+pred_nonpunct (gunichar ch, gpointer user_data)
+{
+    return !g_unichar_ispunct(ch);
+}
+}
+
+extern "C" {
+static gboolean
+pred_nonpunct_or_slash (gunichar ch, gpointer user_data)
+{
+    return !g_unichar_ispunct(ch) || ch == '/';
+}
+}
+
+//-----------------------------------------------------------------------------
+//  Check for links between s and e and correct tags as necessary
+//-----------------------------------------------------------------------------
+
+// This function should be made match better while being efficient at one point.
+// Most probably with a row of regular expressions.
+extern "C" {
+static void
+au_check_word( GtkTextIter *s, GtkTextIter *e )
+{
+    static const char *URIPrefixes[] =
+    {
+        "http://",
+        "ftp://",
+        "www.",
+        "ftp.",
+        "mailto://",
+        "https://",
+        "file://",
+        "nntp://",
+        "news://",
+        "telnet://",
+        "mms://",
+        "gopher://",
+        "prospero://",
+        "wais://",
+    };
+
+    GtkTextIter start = *s, end = *e;
+    GtkTextBuffer *buffer = gtk_text_iter_get_buffer(s);
+    
+    // Get our special link tag
+    GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "wxUrl");
+
+    // Get rid of punctuation from beginning and end.
+    // Might want to move this to au_check_range if an improved link checking doesn't
+    // use some intelligent punctuation checking itself (beware of undesired iter modifications).
+    if(g_unichar_ispunct( gtk_text_iter_get_char( &start ) ) )
+        gtk_text_iter_forward_find_char( &start, pred_nonpunct, NULL, e );
+
+    gtk_text_iter_backward_find_char( &end, pred_nonpunct_or_slash, NULL, &start );
+    gtk_text_iter_forward_char(&end);
+
+    gchar* text = gtk_text_iter_get_text( &start, &end );
+    size_t len = strlen(text), prefix_len;
+    size_t n;
+
+    for( n = 0; n < WXSIZEOF(URIPrefixes); ++n )
+    {
+        prefix_len = strlen(URIPrefixes[n]);
+        if((len > prefix_len) && !strncasecmp(text, URIPrefixes[n], prefix_len))
+            break;
+    }
+
+    if(n < WXSIZEOF(URIPrefixes))
+    {
+        gulong signal_id = g_signal_handler_find(buffer,
+                                                 (GSignalMatchType) (G_SIGNAL_MATCH_FUNC),
+                                                 0, 0, NULL,
+                                                 (gpointer)au_apply_tag_callback, NULL);
+
+        g_signal_handler_block(buffer, signal_id);
+        gtk_text_buffer_apply_tag(buffer, tag, &start, &end);
+        g_signal_handler_unblock(buffer, signal_id);
+    }
+}
+}
+
+extern "C" {
+static void
+au_check_range(GtkTextIter *s,
+               GtkTextIter *range_end)
+{
+    GtkTextIter range_start = *s;
+    GtkTextIter word_end;
+    GtkTextBuffer *buffer = gtk_text_iter_get_buffer(s);
+    GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "wxUrl");
+
+    gtk_text_buffer_remove_tag(buffer, tag, s, range_end);
+
+    if(g_unichar_isspace(gtk_text_iter_get_char(&range_start)))
+        gtk_text_iter_forward_find_char(&range_start, pred_non_whitespace, NULL, range_end);
+
+    while(!gtk_text_iter_equal(&range_start, range_end))
+    {
+        word_end = range_start;
+        gtk_text_iter_forward_find_char(&word_end, pred_whitespace, NULL, range_end);
+
+        // Now we should have a word delimited by range_start and word_end, correct link tags
+        au_check_word(&range_start, &word_end);
+
+        range_start = word_end;
+        gtk_text_iter_forward_find_char(&range_start, pred_non_whitespace, NULL, range_end);
+    }
+}
+}
+
+//-----------------------------------------------------------------------------
+//  "insert-text" for GtkTextBuffer
+//-----------------------------------------------------------------------------
+
+extern "C" {
+static void
+au_insert_text_callback(GtkTextBuffer *buffer,
+                        GtkTextIter *end,
+                        gchar *text,
+                        gint len,
+                        wxTextCtrl *win)
+{
+    if (!len || !(win->GetWindowStyleFlag() & wxTE_AUTO_URL) )
+        return;
+
+    GtkTextIter start = *end;
+    gtk_text_iter_backward_chars(&start, g_utf8_strlen(text, len));
+
+    GtkTextIter line_start = start;
+    GtkTextIter line_end = *end;
+    GtkTextIter words_start = start;
+    GtkTextIter words_end = *end;
+
+    gtk_text_iter_set_line(&line_start, gtk_text_iter_get_line(&start));
+    gtk_text_iter_forward_to_line_end(&line_end);
+    gtk_text_iter_backward_find_char(&words_start, pred_whitespace, NULL, &line_start);
+    gtk_text_iter_forward_find_char(&words_end, pred_whitespace, NULL, &line_end);
+
+    au_check_range(&words_start, &words_end);
+}
+}
+
+//-----------------------------------------------------------------------------
+//  "delete-range" for GtkTextBuffer
+//-----------------------------------------------------------------------------
+
+extern "C" {
+static void
+au_delete_range_callback(GtkTextBuffer *buffer,
+                         GtkTextIter *start,
+                         GtkTextIter *end,
+                         wxTextCtrl *win)
+{
+    if( !(win->GetWindowStyleFlag() & wxTE_AUTO_URL) )
+        return;
+
+    GtkTextIter line_start = *start, line_end = *end;
+
+    gtk_text_iter_set_line(&line_start, gtk_text_iter_get_line(start));
+    gtk_text_iter_forward_to_line_end(&line_end);
+    gtk_text_iter_backward_find_char(start, pred_whitespace, NULL, &line_start);
+    gtk_text_iter_forward_find_char(end, pred_whitespace, NULL, &line_end);
+
+    au_check_range(start, end);
+}
+}
+
+
+#endif
+
+//-----------------------------------------------------------------------------
+//  "changed"
+//-----------------------------------------------------------------------------
+
+extern "C" {
+static void
+gtk_text_changed_callback( GtkWidget *widget, wxTextCtrl *win )
+{
+    if ( win->IgnoreTextUpdate() )
+        return;
+
+    if (!win->m_hasVMT) return;
+
+    if (g_isIdle)
+        wxapp_install_idle_handler();
+
+    win->SetModified();
+#ifndef __WXGTK20__
+    win->UpdateFontIfNeeded();
+#endif // !__WXGTK20__
+
+    wxCommandEvent event( wxEVT_COMMAND_TEXT_UPDATED, win->GetId() );
+    event.SetEventObject( win );
+    win->GetEventHandler()->ProcessEvent( event );
+}
+}
+
+//-----------------------------------------------------------------------------
+// "expose_event" from scrolled window and textview
+//-----------------------------------------------------------------------------
+
+#ifdef __WXGTK20__
+extern "C" {
+static gboolean
+gtk_text_exposed_callback( GtkWidget *widget, GdkEventExpose *event, wxTextCtrl *win )
+{
+    return TRUE;
+}
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// "changed" from vertical scrollbar
+//-----------------------------------------------------------------------------
+
+#ifndef __WXGTK20__
+extern "C" {
+static void
+gtk_scrollbar_changed_callback( GtkWidget *WXUNUSED(widget), wxTextCtrl *win )
+{
+    if (!win->m_hasVMT) return;
+
+    if (g_isIdle)
+        wxapp_install_idle_handler();
+
+    win->CalculateScrollbar();
+}
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// redraw callback for multiline text
+// ----------------------------------------------------------------------------
+
+#ifndef __WXGTK20__
+
+// redrawing a GtkText from inside a wxYield() call results in crashes (the
+// text sample shows it in its "Add lines" command which shows wxProgressDialog
+// which implicitly calls wxYield()) so we override GtkText::draw() and simply
+// don't do anything if we're inside wxYield()
+
+extern bool wxIsInsideYield;
+
+extern "C" {
+    typedef void (*GtkDrawCallback)(GtkWidget *widget, GdkRectangle *rect);
+}
+
+static GtkDrawCallback gs_gtk_text_draw = NULL;
+
+extern "C" {
+static void wxgtk_text_draw( GtkWidget *widget, GdkRectangle *rect)
+{
+    if ( !wxIsInsideYield )
+    {
+        wxCHECK_RET( gs_gtk_text_draw != wxgtk_text_draw,
+                     _T("infinite recursion in wxgtk_text_draw aborted") );
+
+        gs_gtk_text_draw(widget, rect);
+    }
+}
+}
+
+#endif // __WXGTK20__
+
+//-----------------------------------------------------------------------------
+//  wxTextCtrl
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl,wxControl)