+wxGtkTextRemoveTagsWithPrefix(GtkTextBuffer *text_buffer,
+                              const char *prefix,
+                              GtkTextIter *start,
+                              GtkTextIter *end)
+{
+    gulong remove_handler_id = g_signal_connect
+                               (
+                                text_buffer,
+                                "remove_tag",
+                                G_CALLBACK(wxGtkOnRemoveTag),
+                                gpointer(prefix)
+                               );
+    gtk_text_buffer_remove_all_tags(text_buffer, start, end);
+    g_signal_handler_disconnect(text_buffer, remove_handler_id);
+}
+
+static void wxGtkTextApplyTagsFromAttr(GtkWidget *text,
+                                       GtkTextBuffer *text_buffer,
+                                       const wxTextAttr& attr,
+                                       GtkTextIter *start,
+                                       GtkTextIter *end)
+{
+    static gchar buf[1024];
+    GtkTextTag *tag;
+
+    if (attr.HasFont())
+    {
+        wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXFONT", start, end);
+
+        PangoFontDescription *font_description = attr.GetFont().GetNativeFontInfo()->description;
+        wxGtkString font_string(pango_font_description_to_string(font_description));
+        g_snprintf(buf, sizeof(buf), "WXFONT %s", font_string.c_str());
+        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,
+                                              "font-desc", font_description,
+                                              NULL );
+        gtk_text_buffer_apply_tag (text_buffer, tag, start, end);
+
+        if (attr.GetFont().GetUnderlined())
+        {
+            g_snprintf(buf, sizeof(buf), "WXFONTUNDERLINE");
+            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,
+                                                  "underline-set", TRUE,
+                                                  "underline", PANGO_UNDERLINE_SINGLE,
+                                                  NULL );
+            gtk_text_buffer_apply_tag (text_buffer, tag, start, end);
+        }
+    }
+
+    if (attr.HasTextColour())
+    {
+        wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXFORECOLOR", start, end);
+
+        const GdkColor *colFg = attr.GetTextColour().GetColor();
+        g_snprintf(buf, sizeof(buf), "WXFORECOLOR %d %d %d",
+                   colFg->red, colFg->green, colFg->blue);
+        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,
+                                              "foreground-gdk", colFg, NULL );
+        gtk_text_buffer_apply_tag (text_buffer, tag, start, end);
+    }
+
+    if (attr.HasBackgroundColour())
+    {
+        wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXBACKCOLOR", start, end);
+
+        const GdkColor *colBg = attr.GetBackgroundColour().GetColor();
+        g_snprintf(buf, sizeof(buf), "WXBACKCOLOR %d %d %d",
+                   colBg->red, colBg->green, colBg->blue);
+        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,
+                                              "background-gdk", colBg, NULL );
+        gtk_text_buffer_apply_tag (text_buffer, tag, start, end);
+    }
+
+    if (attr.HasAlignment())
+    {
+        GtkTextIter para_start, para_end = *end;
+        gtk_text_buffer_get_iter_at_line( text_buffer,
+                                          ¶_start,
+                                          gtk_text_iter_get_line(start) );
+        gtk_text_iter_forward_line(¶_end);
+
+        wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXALIGNMENT", start, end);
+
+        GtkJustification align;
+        switch (attr.GetAlignment())
+        {
+            default:
+                align = GTK_JUSTIFY_LEFT;
+                break;
+            case wxTEXT_ALIGNMENT_RIGHT:
+                align = GTK_JUSTIFY_RIGHT;
+                break;
+            case wxTEXT_ALIGNMENT_CENTER:
+                align = GTK_JUSTIFY_CENTER;
+                break;
+// gtk+ doesn't support justify before gtk+-2.11.0 with pango-1.17 being available
+// (but if new enough pango isn't available it's a mere gtk warning)
+#if GTK_CHECK_VERSION(2,11,0)
+            case wxTEXT_ALIGNMENT_JUSTIFIED:
+                if (!gtk_check_version(2,11,0))
+                    align = GTK_JUSTIFY_FILL;
+                else
+                    align = GTK_JUSTIFY_LEFT;
+                break;
+#endif
+        }
+
+        g_snprintf(buf, sizeof(buf), "WXALIGNMENT %d", align);
+        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,
+                                              "justification", align, NULL );
+        gtk_text_buffer_apply_tag( text_buffer, tag, ¶_start, ¶_end );
+    }
+
+    if (attr.HasLeftIndent())
+    {
+        // Indentation attribute
+
+        // Clear old indentation tags
+        GtkTextIter para_start, para_end = *end;
+        gtk_text_buffer_get_iter_at_line( text_buffer,
+                                          ¶_start,
+                                          gtk_text_iter_get_line(start) );
+        gtk_text_iter_forward_line(¶_end);
+
+        wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXINDENT", start, end);
+
+        // Convert indent from 1/10th of a mm into pixels
+        float factor;
+#if GTK_CHECK_VERSION(2,2,0)
+        if (!gtk_check_version(2,2,0))
+            factor = (float)gdk_screen_get_width(gtk_widget_get_screen(text)) /
+                      gdk_screen_get_width_mm(gtk_widget_get_screen(text)) / 10;
+        else
+#endif
+            factor = (float)gdk_screen_width() / gdk_screen_width_mm() / 10;
+
+        const int indent = (int)(factor * attr.GetLeftIndent());
+        const int subIndent = (int)(factor * attr.GetLeftSubIndent());
+
+        gint gindent;
+        gint gsubindent;
+
+        if (subIndent >= 0)
+        {
+            gindent = indent;
+            gsubindent = -subIndent;
+        }
+        else
+        {
+            gindent = -subIndent;
+            gsubindent = indent;
+        }
+
+        g_snprintf(buf, sizeof(buf), "WXINDENT %d %d", gindent, gsubindent);
+        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,
+                                              "left-margin", gindent, "indent", gsubindent, NULL );
+        gtk_text_buffer_apply_tag (text_buffer, tag, ¶_start, ¶_end);
+    }
+
+    if (attr.HasTabs())
+    {
+        // Set tab stops
+
+        // Clear old tabs
+        GtkTextIter para_start, para_end = *end;
+        gtk_text_buffer_get_iter_at_line( text_buffer,
+                                          ¶_start,
+                                          gtk_text_iter_get_line(start) );
+        gtk_text_iter_forward_line(¶_end);
+
+        wxGtkTextRemoveTagsWithPrefix(text_buffer, "WXTABS", start, end);
+
+        const wxArrayInt& tabs = attr.GetTabs();
+
+        wxString tagname = _T("WXTABS");
+        g_snprintf(buf, sizeof(buf), "WXTABS");
+        for (size_t i = 0; i < tabs.GetCount(); i++)
+            tagname += wxString::Format(_T(" %d"), tabs[i]);
+
+        const wxWX2MBbuf buf = tagname.mb_str(wxConvUTF8);
+
+        tag = gtk_text_tag_table_lookup( gtk_text_buffer_get_tag_table( text_buffer ),
+                                        buf );
+        if (!tag)
+        {
+            // Factor to convert from 1/10th of a mm into pixels
+            float factor;
+#if GTK_CHECK_VERSION(2,2,0)
+            if (!gtk_check_version(2,2,0))
+                factor = (float)gdk_screen_get_width(gtk_widget_get_screen(text)) /
+                          gdk_screen_get_width_mm(gtk_widget_get_screen(text)) / 10;
+            else
+#endif
+                factor = (float)gdk_screen_width() / gdk_screen_width_mm() / 10;
+
+            PangoTabArray* tabArray = pango_tab_array_new(tabs.GetCount(), TRUE);
+            for (size_t i = 0; i < tabs.GetCount(); i++)
+                pango_tab_array_set_tab(tabArray, i, PANGO_TAB_LEFT, (gint)(tabs[i] * factor));
+            tag = gtk_text_buffer_create_tag( text_buffer, buf,
+                                              "tabs", tabArray, NULL );
+            pango_tab_array_free(tabArray);
+        }
+        gtk_text_buffer_apply_tag (text_buffer, tag, ¶_start, ¶_end);
+    }
+}
+
+static void wxGtkTextInsert(GtkWidget *text,
+                            GtkTextBuffer *text_buffer,
+                            const wxTextAttr& attr,
+                            const wxCharBuffer& buffer)
+