aboutsummaryrefslogtreecommitdiffstats
path: root/packages/gtk+/gtk+-2.6.4-1.osso7/gtktextbufferserialize.c.diff
diff options
context:
space:
mode:
Diffstat (limited to 'packages/gtk+/gtk+-2.6.4-1.osso7/gtktextbufferserialize.c.diff')
-rw-r--r--packages/gtk+/gtk+-2.6.4-1.osso7/gtktextbufferserialize.c.diff1688
1 files changed, 0 insertions, 1688 deletions
diff --git a/packages/gtk+/gtk+-2.6.4-1.osso7/gtktextbufferserialize.c.diff b/packages/gtk+/gtk+-2.6.4-1.osso7/gtktextbufferserialize.c.diff
deleted file mode 100644
index 39c8f748de..0000000000
--- a/packages/gtk+/gtk+-2.6.4-1.osso7/gtktextbufferserialize.c.diff
+++ /dev/null
@@ -1,1688 +0,0 @@
---- gtk+-2.6.4/gtk/gtktextbufferserialize.c 1970-01-01 02:00:00.000000000 +0200
-+++ gtk+-2.6.4/gtk/gtktextbufferserialize.c 2005-04-06 16:19:38.024757720 +0300
-@@ -0,0 +1,1685 @@
-+/* gtktextbufferserialize.c
-+ *
-+ * Copyright (C) 2001 Havoc Pennington
-+ * Copyright (C) 2004 Nokia
-+ *
-+ * This library is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU Library General Public
-+ * License as published by the Free Software Foundation; either
-+ * version 2 of the License, or (at your option) any later version.
-+ *
-+ * This library is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-+ * Library General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU Library General Public
-+ * License along with this library; if not, write to the
-+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-+ * Boston, MA 02111-1307, USA.
-+ */
-+
-+/* FIXME: We should use other error codes for the
-+ * parts that deal with the format errors
-+ */
-+
-+#include <config.h>
-+
-+#include <stdio.h>
-+#include "gdk-pixbuf/gdk-pixdata.h"
-+#include "gtktextbufferserialize.h"
-+#include "gtkintl.h"
-+
-+#include <string.h>
-+#include <stdlib.h>
-+
-+typedef struct
-+{
-+ GString *tag_table_str;
-+ GString *text_str;
-+ GHashTable *tags;
-+ GtkTextIter start, end;
-+
-+ gint n_pixbufs;
-+ GList *pixbufs;
-+} SerializationContext;
-+
-+static gchar *
-+serialize_value (GValue *value)
-+{
-+ if (g_value_type_transformable (value->g_type, G_TYPE_STRING))
-+ {
-+ GValue text_value = { 0 };
-+ gchar *tmp;
-+
-+ g_value_init (&text_value, G_TYPE_STRING);
-+ g_value_transform (value, &text_value);
-+
-+ tmp = g_markup_escape_text (g_value_get_string (&text_value), -1);
-+ g_value_unset (&text_value);
-+
-+ return tmp;
-+ }
-+ else if (value->g_type == GDK_TYPE_COLOR)
-+ {
-+ GdkColor *color = g_value_get_boxed (value);
-+
-+ return g_strdup_printf ("%x:%x:%x", color->red, color->green, color->blue);
-+ }
-+ else
-+ {
-+ g_warning ("Type %s is not serializable\n", g_type_name (value->g_type));
-+ }
-+
-+ return NULL;
-+}
-+
-+static gboolean
-+deserialize_value (const gchar *str, GValue *value)
-+{
-+ if (g_value_type_transformable (G_TYPE_STRING, value->g_type))
-+ {
-+ GValue text_value = { 0 };
-+ gboolean retval;
-+
-+ g_value_init (&text_value, G_TYPE_STRING);
-+ g_value_set_static_string (&text_value, str);
-+
-+ retval = g_value_transform (&text_value, value);
-+ g_value_unset (&text_value);
-+
-+ return retval;
-+ }
-+ else if (value->g_type == G_TYPE_BOOLEAN)
-+ {
-+ gboolean v;
-+
-+ v = strcmp (str, "TRUE") == 0;
-+
-+ g_value_set_boolean (value, v);
-+
-+ return TRUE;
-+ }
-+ else if (value->g_type == G_TYPE_INT)
-+ {
-+ gchar *tmp;
-+ int v;
-+
-+ v = strtol (str, &tmp, 10);
-+
-+ if (tmp == NULL || tmp == str)
-+ return FALSE;
-+
-+ g_value_set_int (value, v);
-+
-+ return TRUE;
-+ }
-+ else if (value->g_type == G_TYPE_DOUBLE)
-+ {
-+ gchar *tmp;
-+ gdouble v;
-+
-+ v = g_ascii_strtod (str, &tmp);
-+
-+ if (tmp == NULL || tmp == str)
-+ return FALSE;
-+
-+ g_value_set_double (value, v);
-+
-+ return TRUE;
-+ }
-+ else if (value->g_type == GDK_TYPE_COLOR)
-+ {
-+ GdkColor color;
-+ const gchar *old;
-+ gchar *tmp;
-+
-+ old = str;
-+ color.red = strtol (old, &tmp, 16);
-+
-+ if (tmp == NULL || tmp == old)
-+ return FALSE;
-+
-+ old = tmp;
-+ if (*old++ != ':')
-+ return FALSE;
-+
-+ color.green = strtol (old, &tmp, 16);
-+ if (tmp == NULL || tmp == old)
-+ return FALSE;
-+
-+ old = tmp;
-+ if (*old++ != ':')
-+ return FALSE;
-+
-+ color.blue = strtol (old, &tmp, 16);
-+
-+ if (tmp == NULL || tmp == old || *tmp != '\0')
-+ return FALSE;
-+
-+ g_value_set_boxed (value, &color);
-+
-+ return TRUE;
-+ }
-+ else if (G_VALUE_HOLDS_ENUM (value))
-+ {
-+ GEnumClass *class = G_ENUM_CLASS (g_type_class_peek (value->g_type));
-+ GEnumValue *enum_value;
-+
-+ enum_value = g_enum_get_value_by_name (class, str);
-+
-+ if (enum_value)
-+ {
-+ g_value_set_enum (value, enum_value->value);
-+ return TRUE;
-+ }
-+
-+ return FALSE;
-+ }
-+ else
-+ {
-+ g_warning ("Type %s can not be deserialized\n", g_type_name (value->g_type));
-+ }
-+
-+ return FALSE;
-+}
-+
-+/* Checks if a param is set, or if it's the default value */
-+static gboolean
-+is_param_set (GObject *object, GParamSpec *pspec, GValue *value)
-+{
-+ /* We need to special case some attributes here */
-+ if (strcmp (pspec->name, "background-gdk") == 0)
-+ {
-+ gboolean is_set;
-+
-+ g_object_get (object, "background-set", &is_set, NULL);
-+
-+ if (is_set)
-+ {
-+ g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
-+
-+ g_object_get_property (object, pspec->name, value);
-+
-+ return TRUE;
-+ }
-+
-+ return FALSE;
-+ }
-+ else if (strcmp (pspec->name, "foreground-gdk") == 0)
-+ {
-+ gboolean is_set;
-+
-+ g_object_get (object, "foreground-set", &is_set, NULL);
-+
-+ if (is_set)
-+ {
-+ g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
-+
-+ g_object_get_property (object, pspec->name, value);
-+
-+ return TRUE;
-+ }
-+
-+ return FALSE;
-+ }
-+ else
-+ {
-+ gboolean is_set;
-+ gchar *is_set_name;
-+
-+ is_set_name = g_strdup_printf ("%s-set", pspec->name);
-+
-+ if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), is_set_name) == NULL)
-+ {
-+ g_free (is_set_name);
-+ return FALSE;
-+ }
-+ else
-+ {
-+ g_object_get (object, is_set_name, &is_set, NULL);
-+
-+ if (!is_set)
-+ {
-+ g_free (is_set_name);
-+ return FALSE;
-+ }
-+
-+ g_free (is_set_name);
-+
-+ g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
-+
-+ g_object_get_property (object, pspec->name, value);
-+
-+ if (g_param_value_defaults (pspec, value))
-+ {
-+ g_value_unset (value);
-+
-+ return FALSE;
-+ }
-+ }
-+ return TRUE;
-+ }
-+}
-+
-+static void
-+serialize_tag (gpointer key, gpointer data, gpointer user_data)
-+{
-+ SerializationContext *context = user_data;
-+ GtkTextTag *tag = data;
-+ gchar *tag_name;
-+ GParamSpec **pspecs;
-+ guint n_pspecs;
-+ int i;
-+
-+ tag_name = g_markup_escape_text (tag->name, -1);
-+ g_string_append_printf (context->tag_table_str, " <tag name=\"%s\" priority=\"%d\">\n", tag_name, tag->priority);
-+
-+ /* Serialize properties */
-+ pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (tag), &n_pspecs);
-+
-+ for (i = 0; i < n_pspecs; i++)
-+ {
-+ GValue value = { 0 };
-+ gchar *tmp, *tmp2;
-+
-+ if (!(pspecs[i]->flags & G_PARAM_READABLE) ||
-+ !(pspecs[i]->flags & G_PARAM_WRITABLE))
-+ continue;
-+
-+ if (!is_param_set (G_OBJECT (tag), pspecs[i], &value))
-+ continue;
-+
-+ /* Now serialize the attr */
-+ tmp = g_markup_escape_text (pspecs[i]->name, -1);
-+ g_string_append_printf (context->tag_table_str, " <attr name=\"%s\" ", tmp);
-+ g_free (tmp);
-+
-+ tmp = g_markup_escape_text (g_type_name (pspecs[i]->value_type), -1);
-+ tmp2 = serialize_value (&value);
-+ g_string_append_printf (context->tag_table_str, "type=\"%s\" value=\"%s\" />\n", tmp, tmp2);
-+
-+ g_free (tmp);
-+ g_free (tmp2);
-+
-+ g_value_unset (&value);
-+ }
-+
-+ g_free (pspecs);
-+
-+ g_string_append (context->tag_table_str, " </tag>\n");
-+ g_free (tag_name);
-+}
-+
-+static void
-+serialize_tags (SerializationContext *context)
-+{
-+ g_string_append (context->tag_table_str, " <text_view_markup>\n");
-+ g_string_append (context->tag_table_str, " <tags>\n");
-+ g_hash_table_foreach (context->tags, serialize_tag, context);
-+ g_string_append (context->tag_table_str, " </tags>\n");
-+}
-+
-+#if 0
-+static void
-+dump_tag_list (const gchar *str, GList *list)
-+{
-+ g_print ("%s: ", str);
-+
-+ if (!list)
-+ g_print ("(empty)");
-+ else
-+ {
-+ while (list)
-+ {
-+ g_print ("%s ", ((GtkTextTag *)list->data)->name);
-+ list = list->next;
-+ }
-+ }
-+
-+ g_print ("\n");
-+}
-+#endif
-+
-+static void
-+find_list_delta (GSList *old_list, GSList *new_list,
-+ GList **added, GList **removed)
-+{
-+ GSList *tmp;
-+ GList *tmp_added, *tmp_removed;
-+
-+ tmp_added = NULL;
-+ tmp_removed = NULL;
-+
-+ /* Find added tags */
-+ tmp = new_list;
-+ while (tmp)
-+ {
-+ if (!g_slist_find (old_list, tmp->data))
-+ tmp_added = g_list_prepend (tmp_added, tmp->data);
-+
-+ tmp = tmp->next;
-+ }
-+
-+ *added = tmp_added;
-+
-+ /* Find removed tags */
-+ tmp = old_list;
-+ while (tmp)
-+ {
-+ if (!g_slist_find (new_list, tmp->data))
-+ tmp_removed = g_list_prepend (tmp_removed, tmp->data);
-+
-+ tmp = tmp->next;
-+ }
-+
-+ /* We reverse the list here to match the xml semantics */
-+ *removed = g_list_reverse (tmp_removed);
-+}
-+
-+static void
-+serialize_section_header (GString *str,
-+ const gchar *name,
-+ gint length)
-+{
-+ g_return_if_fail (strlen (name) == 8);
-+
-+ g_string_append (str, name);
-+
-+ g_string_append_c (str, length >> 24);
-+
-+ g_string_append_c (str, (length >> 16) & 0xff);
-+ g_string_append_c (str, (length >> 8) & 0xff);
-+ g_string_append_c (str, length & 0xff);
-+}
-+
-+static void
-+serialize_text (GtkTextBuffer *buffer, SerializationContext *context)
-+{
-+ GtkTextIter iter, old_iter;
-+ GSList *tag_list, *new_tag_list;
-+ GQueue *active_tags;
-+ int i;
-+
-+ g_string_append (context->text_str, "<text>");
-+
-+ iter = context->start;
-+ tag_list = NULL;
-+ active_tags = g_queue_new ();
-+
-+ do
-+ {
-+ GList *added, *removed;
-+ GList *tmp;
-+ gchar *tmp_text, *escaped_text;
-+
-+ new_tag_list = gtk_text_iter_get_tags (&iter);
-+ find_list_delta (tag_list, new_tag_list, &added, &removed);
-+
-+ /* Handle removed tags */
-+ tmp = removed;
-+ while (tmp)
-+ {
-+ GtkTextTag *tag = tmp->data;
-+
-+ g_string_append (context->text_str, "</apply_tag>");
-+
-+ /* We might need to drop some of the tags and re-add them afterwards */
-+ while (g_queue_peek_head (active_tags) != tag &&
-+ !g_queue_is_empty (active_tags))
-+ {
-+ added = g_list_prepend (added, g_queue_pop_head (active_tags));
-+ g_string_append_printf (context->text_str, "</apply_tag>");
-+ }
-+
-+ g_queue_pop_head (active_tags);
-+
-+ tmp = tmp->next;
-+ }
-+
-+ /* Handle added tags */
-+ tmp = added;
-+ while (tmp)
-+ {
-+ GtkTextTag *tag = tmp->data;
-+ gchar *tag_name;
-+
-+ /* Add it to the tag hash table */
-+ g_hash_table_insert (context->tags, tag, tag);
-+
-+ tag_name = g_markup_escape_text (tag->name, -1);
-+
-+ g_string_append_printf (context->text_str, "<apply_tag name=\"%s\">", tag_name);
-+ g_free (tag_name);
-+
-+ g_queue_push_head (active_tags, tag);
-+
-+ tmp = tmp->next;
-+ }
-+
-+ g_slist_free (tag_list);
-+ tag_list = new_tag_list;
-+
-+ old_iter = iter;
-+
-+ /* Now try to go to either the next tag toggle, or if a pixbuf appears */
-+ while (TRUE)
-+ {
-+ gunichar ch = gtk_text_iter_get_char (&iter);
-+
-+ if (ch == 0xFFFC)
-+ {
-+ GdkPixbuf *pixbuf = gtk_text_iter_get_pixbuf (&iter);
-+
-+ if (pixbuf) {
-+ g_string_append_printf (context->text_str, "<pixbuf index=\"%d\" />", context->n_pixbufs);
-+
-+ context->n_pixbufs++;
-+ context->pixbufs = g_list_prepend (context->pixbufs, pixbuf);
-+ }
-+ }
-+
-+ gtk_text_iter_forward_char (&iter);
-+
-+ if (gtk_text_iter_toggles_tag (&iter, NULL))
-+ break;
-+ }
-+
-+ /* We might have moved too far */
-+ if (gtk_text_iter_compare (&iter, &context->end) > 0)
-+ iter = context->end;
-+
-+ /* Append the text */
-+ tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
-+ escaped_text = g_markup_escape_text (tmp_text, -1);
-+ g_free (tmp_text);
-+
-+ g_string_append (context->text_str, escaped_text);
-+ g_free (escaped_text);
-+ }
-+ while (!gtk_text_iter_equal (&iter, &context->end));
-+
-+ /* Close any open tags */
-+ for (i = 0; i < g_queue_get_length (active_tags); i++) {
-+ g_string_append (context->text_str, "</apply_tag>");
-+ }
-+ g_queue_free (active_tags);
-+ g_string_append (context->text_str, "</text>\n</text_view_markup>\n");
-+}
-+
-+static void
-+serialize_pixbufs (SerializationContext *context,
-+ GString *text)
-+{
-+ GList *list;
-+
-+ for (list = context->pixbufs; list != NULL; list = list->next)
-+ {
-+ GdkPixbuf *pixbuf = list->data;
-+ GdkPixdata pixdata;
-+ guint8 *tmp;
-+ guint len;
-+
-+ gdk_pixdata_from_pixbuf (&pixdata, pixbuf, FALSE);
-+ tmp = gdk_pixdata_serialize (&pixdata, &len);
-+
-+ serialize_section_header (text, "PDPIXBUF", len);
-+ g_string_append_len (text, tmp, len);
-+ g_free (tmp);
-+ }
-+}
-+
-+gchar *
-+gtk_text_buffer_serialize_rich_text (GtkTextBuffer *buffer,
-+ const GtkTextIter *start,
-+ const GtkTextIter *end,
-+ gint *len)
-+{
-+ SerializationContext context;
-+ GString *text;
-+
-+ context.tags = g_hash_table_new (NULL, NULL);
-+ context.text_str = g_string_new (NULL);
-+ context.tag_table_str = g_string_new (NULL);
-+ context.start = *start;
-+ context.end = *end;
-+ context.n_pixbufs = 0;
-+ context.pixbufs = NULL;
-+
-+ /* We need to serialize the text before the tag table so we know
-+ what tags are used */
-+ serialize_text (buffer, &context);
-+ serialize_tags (&context);
-+
-+ text = g_string_new (NULL);
-+ serialize_section_header (text, "RICHTEXT", context.tag_table_str->len + context.text_str->len);
-+
-+ g_print ("when serializing length is: %d\n", context.tag_table_str->len + context.text_str->len);
-+
-+ g_string_append_len (text, context.tag_table_str->str, context.tag_table_str->len);
-+ g_string_append_len (text, context.text_str->str, context.text_str->len);
-+
-+ context.pixbufs = g_list_reverse (context.pixbufs);
-+ serialize_pixbufs (&context, text);
-+
-+ g_hash_table_destroy (context.tags);
-+ g_list_free (context.pixbufs);
-+ g_string_free (context.text_str, TRUE);
-+ g_string_free (context.tag_table_str, TRUE);
-+
-+ *len = text->len;
-+
-+ return g_string_free (text, FALSE);
-+}
-+
-+typedef enum
-+{
-+ STATE_START,
-+ STATE_TEXT_VIEW_MARKUP,
-+ STATE_TAGS,
-+ STATE_TAG,
-+ STATE_ATTR,
-+ STATE_TEXT,
-+ STATE_APPLY_TAG,
-+ STATE_PIXBUF
-+} ParseState;
-+
-+typedef struct
-+{
-+ gchar *text;
-+ GdkPixbuf *pixbuf;
-+ GSList *tags;
-+} TextSpan;
-+
-+typedef struct
-+{
-+ GtkTextTag *tag;
-+ gint prio;
-+} TextTagPrio;
-+
-+typedef struct
-+{
-+ GSList *states;
-+
-+ GList *headers;
-+
-+ GtkTextBuffer *buffer;
-+
-+ /* Tags that are defined in <tag> elements */
-+ GHashTable *defined_tags;
-+
-+ /* Tag name substitutions */
-+ GHashTable *substitutions;
-+
-+ /* Current tag */
-+ GtkTextTag *current_tag;
-+
-+ /* Priority of current tag */
-+ gint current_tag_prio;
-+
-+ /* Tags and their priorities */
-+ GList *tag_priorities;
-+
-+ GSList *tag_stack;
-+
-+ GList *spans;
-+
-+ gboolean create_tags;
-+
-+ gboolean parsed_text;
-+ gboolean parsed_tags;
-+} ParseInfo;
-+
-+static void
-+set_error (GError **err,
-+ GMarkupParseContext *context,
-+ int error_domain,
-+ int error_code,
-+ const char *format,
-+ ...)
-+{
-+ int line, ch;
-+ va_list args;
-+ char *str;
-+
-+ g_markup_parse_context_get_position (context, &line, &ch);
-+
-+ va_start (args, format);
-+ str = g_strdup_vprintf (format, args);
-+ va_end (args);
-+
-+ g_set_error (err, error_domain, error_code,
-+ ("Line %d character %d: %s"),
-+ line, ch, str);
-+
-+ g_free (str);
-+}
-+
-+static void
-+push_state (ParseInfo *info,
-+ ParseState state)
-+{
-+ info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
-+}
-+
-+static void
-+pop_state (ParseInfo *info)
-+{
-+ g_return_if_fail (info->states != NULL);
-+
-+ info->states = g_slist_remove (info->states, info->states->data);
-+}
-+
-+static ParseState
-+peek_state (ParseInfo *info)
-+{
-+ g_return_val_if_fail (info->states != NULL, STATE_START);
-+
-+ return GPOINTER_TO_INT (info->states->data);
-+}
-+
-+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
-+
-+typedef struct
-+{
-+ const char *name;
-+ const char **retloc;
-+} LocateAttr;
-+
-+static gboolean
-+locate_attributes (GMarkupParseContext *context,
-+ const char *element_name,
-+ const char **attribute_names,
-+ const char **attribute_values,
-+ GError **error,
-+ const char *first_attribute_name,
-+ const char **first_attribute_retloc,
-+ ...)
-+{
-+ va_list args;
-+ const char *name;
-+ const char **retloc;
-+ int n_attrs;
-+#define MAX_ATTRS 24
-+ LocateAttr attrs[MAX_ATTRS];
-+ gboolean retval;
-+ int i;
-+
-+ g_return_val_if_fail (first_attribute_name != NULL, FALSE);
-+ g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
-+
-+ retval = TRUE;
-+
-+ n_attrs = 1;
-+ attrs[0].name = first_attribute_name;
-+ attrs[0].retloc = first_attribute_retloc;
-+ *first_attribute_retloc = NULL;
-+
-+ va_start (args, first_attribute_retloc);
-+
-+ name = va_arg (args, const char*);
-+ retloc = va_arg (args, const char**);
-+
-+ while (name != NULL)
-+ {
-+ g_return_val_if_fail (retloc != NULL, FALSE);
-+
-+ g_assert (n_attrs < MAX_ATTRS);
-+
-+ attrs[n_attrs].name = name;
-+ attrs[n_attrs].retloc = retloc;
-+ n_attrs += 1;
-+ *retloc = NULL;
-+
-+ name = va_arg (args, const char*);
-+ retloc = va_arg (args, const char**);
-+ }
-+
-+ va_end (args);
-+
-+ if (!retval)
-+ return retval;
-+
-+ i = 0;
-+ while (attribute_names[i])
-+ {
-+ int j;
-+ gboolean found;
-+
-+ found = FALSE;
-+ j = 0;
-+ while (j < n_attrs)
-+ {
-+ if (strcmp (attrs[j].name, attribute_names[i]) == 0)
-+ {
-+ retloc = attrs[j].retloc;
-+
-+ if (*retloc != NULL)
-+ {
-+ set_error (error, context,
-+ G_MARKUP_ERROR,
-+ G_MARKUP_ERROR_PARSE,
-+ _("Attribute \"%s\" repeated twice on the same <%s> element"),
-+ attrs[j].name, element_name);
-+ retval = FALSE;
-+ goto out;
-+ }
-+
-+ *retloc = attribute_values[i];
-+ found = TRUE;
-+ }
-+
-+ ++j;
-+ }
-+
-+ if (!found)
-+ {
-+ set_error (error, context,
-+ G_MARKUP_ERROR,
-+ G_MARKUP_ERROR_PARSE,
-+ _("Attribute \"%s\" is invalid on <%s> element in this context"),
-+ attribute_names[i], element_name);
-+ retval = FALSE;
-+ goto out;
-+ }
-+
-+ ++i;
-+ }
-+
-+ out:
-+ return retval;
-+}
-+
-+static gboolean
-+check_no_attributes (GMarkupParseContext *context,
-+ const char *element_name,
-+ const char **attribute_names,
-+ const char **attribute_values,
-+ GError **error)
-+{
-+ if (attribute_names[0] != NULL)
-+ {
-+ set_error (error, context,
-+ G_MARKUP_ERROR,
-+ G_MARKUP_ERROR_PARSE,
-+ _("Attribute \"%s\" is invalid on <%s> element in this context"),
-+ attribute_names[0], element_name);
-+ return FALSE;
-+ }
-+
-+ return TRUE;
-+}
-+
-+static const gchar *
-+tag_exists (GMarkupParseContext *context,
-+ const gchar *name,
-+ ParseInfo *info,
-+ GError **error)
-+{
-+ const gchar *real_name;
-+
-+ if (info->create_tags)
-+ {
-+ /* First, try the substitutions */
-+ real_name = g_hash_table_lookup (info->substitutions, name);
-+
-+ if (real_name)
-+ return real_name;
-+
-+ /* Next, try the list of defined tags */
-+ if (g_hash_table_lookup (info->defined_tags, name) != NULL)
-+ return name;
-+
-+ set_error (error, context,
-+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("Tag \"%s\" has not been defined."), name);
-+
-+ return NULL;
-+ }
-+ else
-+ {
-+ if (gtk_text_tag_table_lookup (info->buffer->tag_table, name) != NULL)
-+ return name;
-+
-+ set_error (error, context,
-+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("Tag \"%s\" does not exist in buffer and tags can not be created."), name);
-+
-+ return NULL;
-+ }
-+}
-+
-+typedef struct
-+{
-+ const gchar *id;
-+ gint length;
-+ const gchar *start;
-+} Header;
-+
-+static GdkPixbuf *
-+get_pixbuf_from_headers (GList *headers, int id, GError **error)
-+{
-+ Header *header;
-+ GdkPixdata pixdata;
-+ GdkPixbuf *pixbuf;
-+
-+ header = g_list_nth_data (headers, id);
-+
-+ if (!header)
-+ return NULL;
-+
-+ if (!gdk_pixdata_deserialize (&pixdata, header->length, header->start, error))
-+ return NULL;
-+
-+ pixbuf = gdk_pixbuf_from_pixdata (&pixdata, TRUE, error);
-+
-+ g_print ("pixbuf is: %p\n", pixbuf);
-+
-+ return pixbuf;
-+}
-+
-+static void
-+parse_apply_tag_element (GMarkupParseContext *context,
-+ const gchar *element_name,
-+ const gchar **attribute_names,
-+ const gchar **attribute_values,
-+ ParseInfo *info,
-+ GError **error)
-+{
-+ const gchar *name, *tag_name, *id;
-+
-+ g_assert (peek_state (info) == STATE_TEXT ||
-+ peek_state (info) == STATE_APPLY_TAG);
-+
-+ if (ELEMENT_IS ("apply_tag"))
-+ {
-+ if (!locate_attributes (context, element_name, attribute_names, attribute_values, error,
-+ "name", &name, NULL))
-+ return;
-+
-+ tag_name = tag_exists (context, name, info, error);
-+
-+ if (!tag_name)
-+ return;
-+
-+ info->tag_stack = g_slist_prepend (info->tag_stack, g_strdup (tag_name));
-+
-+ push_state (info, STATE_APPLY_TAG);
-+ }
-+ else if (ELEMENT_IS ("pixbuf"))
-+ {
-+ int int_id;
-+ GdkPixbuf *pixbuf;
-+ TextSpan *span;
-+
-+ if (!locate_attributes (context, element_name, attribute_names, attribute_values, error,
-+ "index", &id, NULL))
-+ return;
-+
-+ int_id = atoi (id);
-+ pixbuf = get_pixbuf_from_headers (info->headers, int_id, error);
-+
-+ span = g_new0 (TextSpan, 1);
-+ span->pixbuf = pixbuf;
-+ span->tags = NULL;
-+
-+ info->spans = g_list_prepend (info->spans, span);
-+
-+ if (!pixbuf)
-+ return;
-+
-+ push_state (info, STATE_PIXBUF);
-+ }
-+ else
-+ set_error (error, context,
-+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("Element <%s> is not allowed below <%s>"),
-+ element_name, peek_state(info) == STATE_TEXT ? "text" : "apply_tag");
-+}
-+
-+static void
-+parse_attr_element (GMarkupParseContext *context,
-+ const gchar *element_name,
-+ const gchar **attribute_names,
-+ const gchar **attribute_values,
-+ ParseInfo *info,
-+ GError **error)
-+{
-+ const gchar *name, *type, *value;
-+ GType gtype;
-+ GValue gvalue = { 0 };
-+ GParamSpec *pspec;
-+
-+ g_assert (peek_state (info) == STATE_TAG);
-+
-+ if (ELEMENT_IS ("attr"))
-+ {
-+ if (!locate_attributes (context, element_name, attribute_names, attribute_values, error,
-+ "name", &name, "type", &type, "value", &value, NULL))
-+ return;
-+
-+ gtype = g_type_from_name (type);
-+
-+ if (gtype == G_TYPE_INVALID)
-+ {
-+ set_error (error, context,
-+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("\"%s\" is not a valid attribute type"), type);
-+ return;
-+ }
-+
-+ if (!(pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info->current_tag), name)))
-+ {
-+ set_error (error, context,
-+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("\"%s\" is not a valid attribute name"), name);
-+ return;
-+ }
-+
-+ g_value_init (&gvalue, gtype);
-+
-+ if (!deserialize_value (value, &gvalue))
-+ {
-+ set_error (error, context,
-+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("\"%s\" could not be converted to a value of type \"%s\" for attribute \"%s\""),
-+ value, type, name);
-+ return;
-+ }
-+
-+ if (g_param_value_validate (pspec, &gvalue))
-+ {
-+ set_error (error, context,
-+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("\"%s\" is not a valid value of for attribute \"%s\""),
-+ value, name);
-+ g_value_unset (&gvalue);
-+ return;
-+ }
-+
-+ g_object_set_property (G_OBJECT (info->current_tag),
-+ name, &gvalue);
-+
-+ g_value_unset (&gvalue);
-+
-+ push_state (info, STATE_ATTR);
-+ }
-+ else
-+ {
-+ set_error (error, context,
-+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("Element <%s> is not allowed below <%s>"),
-+ element_name, "tag");
-+ }
-+}
-+
-+
-+static gchar *
-+get_tag_name (ParseInfo *info,
-+ const gchar *tag_name)
-+{
-+ gchar *name;
-+ gint i;
-+
-+ name = g_strdup (tag_name);
-+
-+ if (!info->create_tags)
-+ return name;
-+
-+ i = 0;
-+
-+ while (gtk_text_tag_table_lookup (info->buffer->tag_table, name) != NULL)
-+ {
-+ g_free (name);
-+ name = g_strdup_printf ("%s-%d", tag_name, ++i);
-+ }
-+
-+ if (i != 0)
-+ {
-+ g_hash_table_insert (info->substitutions, g_strdup (tag_name), g_strdup (name));
-+ }
-+
-+ return name;
-+}
-+
-+static void
-+parse_tag_element (GMarkupParseContext *context,
-+ const gchar *element_name,
-+ const gchar **attribute_names,
-+ const gchar **attribute_values,
-+ ParseInfo *info,
-+ GError **error)
-+{
-+ const gchar *name, *priority;
-+ gchar *tag_name;
-+ gint prio;
-+ gchar *tmp;
-+
-+ g_assert (peek_state (info) == STATE_TAGS);
-+
-+ if (ELEMENT_IS ("tag"))
-+ {
-+ if (!locate_attributes (context, element_name, attribute_names, attribute_values, error,
-+ "name", &name, "priority", &priority, NULL))
-+ return;
-+
-+ if (g_hash_table_lookup (info->defined_tags, name) != NULL)
-+ {
-+ set_error (error, context,
-+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("Tag \"%s\" already defined"), name);
-+ return;
-+ }
-+
-+ prio = strtol (priority, &tmp, 10);
-+
-+ if (tmp == NULL || tmp == priority)
-+ {
-+ set_error (error, context,
-+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("Tag \"%s\" has invalid priority \"%s\""), name, priority);
-+ return;
-+ }
-+
-+ tag_name = get_tag_name (info, name);
-+ info->current_tag = gtk_text_tag_new (tag_name);
-+ info->current_tag_prio = prio;
-+
-+ g_free (tag_name);
-+
-+ push_state (info, STATE_TAG);
-+ }
-+ else
-+ {
-+ set_error (error, context,
-+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("Element <%s> is not allowed below <%s>"),
-+ element_name, "tags");
-+ }
-+}
-+
-+static void
-+start_element_handler (GMarkupParseContext *context,
-+ const gchar *element_name,
-+ const gchar **attribute_names,
-+ const gchar **attribute_values,
-+ gpointer user_data,
-+ GError **error)
-+{
-+ ParseInfo *info = user_data;
-+
-+ switch (peek_state (info))
-+ {
-+ case STATE_START:
-+ if (ELEMENT_IS ("text_view_markup"))
-+ {
-+ if (!check_no_attributes (context, element_name,
-+ attribute_names, attribute_values, error))
-+ return;
-+
-+ push_state (info, STATE_TEXT_VIEW_MARKUP);
-+ break;
-+ }
-+ else
-+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("Outermost element in text must be <text_view_markup> not <%s>"),
-+ element_name);
-+ break;
-+ case STATE_TEXT_VIEW_MARKUP:
-+ if (ELEMENT_IS ("tags"))
-+ {
-+ if (info->parsed_tags)
-+ {
-+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("A <tags> element has already been specified"));
-+ return;
-+ }
-+
-+ if (!check_no_attributes (context, element_name,
-+ attribute_names, attribute_values, error))
-+ return;
-+
-+ push_state (info, STATE_TAGS);
-+ break;
-+ }
-+ else if (ELEMENT_IS ("text"))
-+ {
-+ if (info->parsed_text)
-+ {
-+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("A <text> element has already been specified"));
-+ return;
-+ }
-+ else if (!info->parsed_tags)
-+ {
-+ set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("A <text> element can't occur before a <tags> element"));
-+ return;
-+ }
-+
-+ if (!check_no_attributes (context, element_name,
-+ attribute_names, attribute_values, error))
-+ return;
-+
-+ push_state (info, STATE_TEXT);
-+ break;
-+ }
-+ else
-+ set_error (error, context,
-+ G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
-+ _("Element <%s> is not allowed below <%s>"),
-+ element_name, "text_view_markup");
-+ break;
-+ case STATE_TAGS:
-+ parse_tag_element (context, element_name,
-+ attribute_names, attribute_values,
-+ info, error);
-+ break;
-+ case STATE_TAG:
-+ parse_attr_element (context, element_name,
-+ attribute_names, attribute_values,
-+ info, error);
-+ break;
-+ case STATE_TEXT:
-+ case STATE_APPLY_TAG:
-+ parse_apply_tag_element (context, element_name,
-+ attribute_names, attribute_values,
-+ info, error);
-+ break;
-+ default:
-+ g_assert_not_reached ();
-+ break;
-+ }
-+}
-+
-+static gint
-+sort_tag_prio (TextTagPrio *a,
-+ TextTagPrio *b)
-+{
-+ if (a->prio < b->prio)
-+ return -1;
-+ else if (a->prio > b->prio)
-+ return 1;
-+ else
-+ return 0;
-+}
-+
-+static void
-+end_element_handler (GMarkupParseContext *context,
-+ const gchar *element_name,
-+ gpointer user_data,
-+ GError **error)
-+{
-+ ParseInfo *info = user_data;
-+ gchar *tmp;
-+ GList *list;
-+
-+ switch (peek_state (info))
-+ {
-+ case STATE_TAGS:
-+ pop_state (info);
-+ g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
-+
-+ info->parsed_tags = TRUE;
-+
-+ /* Sort list and add the tags */
-+ info->tag_priorities = g_list_sort (info->tag_priorities,
-+ (GCompareFunc)sort_tag_prio);
-+ list = info->tag_priorities;
-+ while (list)
-+ {
-+ TextTagPrio *prio = list->data;
-+
-+ if (info->create_tags)
-+ gtk_text_tag_table_add (info->buffer->tag_table, prio->tag);
-+
-+ g_object_unref (prio->tag);
-+ prio->tag = NULL;
-+
-+ list = list->next;
-+ }
-+
-+ break;
-+ case STATE_TAG:
-+ pop_state (info);
-+ g_assert (peek_state (info) == STATE_TAGS);
-+
-+ /* Add tag to defined tags hash */
-+ tmp = g_strdup (info->current_tag->name);
-+ g_hash_table_insert (info->defined_tags,
-+ tmp, tmp);
-+
-+ if (info->create_tags)
-+ {
-+ TextTagPrio *prio;
-+
-+ /* add the tag to the list */
-+ prio = g_new0 (TextTagPrio, 1);
-+ prio->prio = info->current_tag_prio;
-+ prio->tag = info->current_tag;
-+
-+ info->tag_priorities = g_list_prepend (info->tag_priorities, prio);
-+ }
-+
-+ info->current_tag = NULL;
-+ break;
-+ case STATE_ATTR:
-+ pop_state (info);
-+ g_assert (peek_state (info) == STATE_TAG);
-+ break;
-+ case STATE_APPLY_TAG:
-+ pop_state (info);
-+ g_assert (peek_state (info) == STATE_APPLY_TAG ||
-+ peek_state (info) == STATE_TEXT);
-+
-+ /* Pop tag */
-+ g_free (info->tag_stack->data);
-+ info->tag_stack = g_slist_delete_link (info->tag_stack,
-+ info->tag_stack);
-+
-+ break;
-+ case STATE_TEXT:
-+ pop_state (info);
-+ g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
-+
-+ info->spans = g_list_reverse (info->spans);
-+ info->parsed_text = TRUE;
-+ break;
-+ case STATE_TEXT_VIEW_MARKUP:
-+ pop_state (info);
-+ g_assert (peek_state (info) == STATE_START);
-+ break;
-+ case STATE_PIXBUF:
-+ pop_state (info);
-+ g_assert (peek_state (info) == STATE_APPLY_TAG ||
-+ peek_state (info) == STATE_TEXT);
-+ break;
-+ default:
-+ g_assert_not_reached ();
-+ break;
-+ }
-+}
-+
-+static gboolean
-+all_whitespace (const char *text,
-+ int text_len)
-+{
-+ const char *p;
-+ const char *end;
-+
-+ p = text;
-+ end = text + text_len;
-+
-+ while (p != end)
-+ {
-+ if (!g_ascii_isspace (*p))
-+ return FALSE;
-+
-+ p = g_utf8_next_char (p);
-+ }
-+
-+ return TRUE;
-+}
-+
-+static GSList *
-+copy_tag_list (GSList *tag_list)
-+{
-+ GSList *tmp = NULL;
-+
-+ while (tag_list)
-+ {
-+ tmp = g_slist_prepend (tmp, g_strdup (tag_list->data));
-+
-+ tag_list = tag_list->next;
-+ }
-+
-+ return tmp;
-+}
-+
-+static void
-+text_handler (GMarkupParseContext *context,
-+ const gchar *text,
-+ gsize text_len,
-+ gpointer user_data,
-+ GError **error)
-+{
-+ ParseInfo *info = user_data;
-+ TextSpan *span;
-+
-+ if (all_whitespace (text, text_len) &&
-+ peek_state (info) != STATE_TEXT &&
-+ peek_state (info) != STATE_APPLY_TAG)
-+ return;
-+
-+ switch (peek_state (info))
-+ {
-+ case STATE_START:
-+ g_assert_not_reached (); /* gmarkup shouldn't do this */
-+ break;
-+ case STATE_TEXT:
-+ case STATE_APPLY_TAG:
-+ if (text_len == 0)
-+ return;
-+
-+ span = g_new0 (TextSpan, 1);
-+ span->text = g_strndup (text, text_len);
-+ span->tags = copy_tag_list (info->tag_stack);
-+
-+ info->spans = g_list_prepend (info->spans, span);
-+ break;
-+ default:
-+ g_assert_not_reached ();
-+ break;
-+ }
-+}
-+
-+static void
-+parse_info_init (ParseInfo *info,
-+ GtkTextBuffer *buffer,
-+ gboolean create_tags,
-+ GList *headers)
-+{
-+ info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
-+
-+ info->create_tags = create_tags;
-+ info->headers = headers;
-+ info->defined_tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-+ info->substitutions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-+ info->tag_stack = NULL;
-+ info->spans = NULL;
-+ info->parsed_text = FALSE;
-+ info->parsed_tags = FALSE;
-+ info->current_tag = NULL;
-+ info->current_tag_prio = -1;
-+ info->tag_priorities = NULL;
-+
-+ info->buffer = buffer;
-+}
-+
-+static void
-+text_span_free (TextSpan *span)
-+{
-+ GSList *tmp;
-+
-+ g_free (span->text);
-+
-+ tmp = span->tags;
-+ while (tmp)
-+ {
-+ g_free (tmp->data);
-+
-+ tmp = tmp->next;
-+ }
-+ g_slist_free (span->tags);
-+ g_free (span);
-+}
-+
-+static void
-+parse_info_free (ParseInfo *info)
-+{
-+ GSList *slist;
-+ GList *list;
-+
-+ slist = info->tag_stack;
-+ while (slist)
-+ {
-+ g_free (slist->data);
-+
-+ slist = slist->next;
-+ }
-+
-+ g_slist_free (info->tag_stack);
-+ g_slist_free (info->states);
-+
-+ g_hash_table_destroy (info->substitutions);
-+ g_hash_table_destroy (info->defined_tags);
-+
-+ if (info->current_tag)
-+ g_object_unref (info->current_tag);
-+
-+ list = info->spans;
-+ while (list)
-+ {
-+ text_span_free (list->data);
-+
-+ list = list->next;
-+ }
-+ g_list_free (info->spans);
-+
-+ list = info->tag_priorities;
-+ while (list)
-+ {
-+ TextTagPrio *prio = list->data;
-+
-+ if (prio->tag)
-+ g_object_unref (prio->tag);
-+ g_free (prio);
-+
-+ list = list->next;
-+ }
-+ g_list_free (info->tag_priorities);
-+
-+}
-+
-+static const gchar *
-+get_tag_substitution (ParseInfo *info,
-+ const gchar *name)
-+{
-+ gchar *subst;
-+
-+ if ((subst = g_hash_table_lookup (info->substitutions, name)))
-+ return subst;
-+ else
-+ return name;
-+}
-+
-+static void
-+insert_text (ParseInfo *info,
-+ GtkTextIter *iter)
-+{
-+ GtkTextIter start_iter;
-+ GtkTextMark *mark;
-+ GList *tmp;
-+ GSList *tags;
-+
-+ start_iter = *iter;
-+
-+ mark = gtk_text_buffer_create_mark (info->buffer, "deserialize_insert_point",
-+ &start_iter, TRUE);
-+
-+ tmp = info->spans;
-+ while (tmp)
-+ {
-+ TextSpan *span = tmp->data;
-+
-+ if (span->text)
-+ gtk_text_buffer_insert (info->buffer, iter, span->text, -1);
-+ else
-+ {
-+ gtk_text_buffer_insert_pixbuf (info->buffer, iter, span->pixbuf);
-+ g_object_unref (span->pixbuf);
-+ }
-+ gtk_text_buffer_get_iter_at_mark (info->buffer, &start_iter, mark);
-+
-+ /* Apply tags */
-+ tags = span->tags;
-+ while (tags)
-+ {
-+ const gchar *tag_name = get_tag_substitution (info, tags->data);
-+
-+ gtk_text_buffer_apply_tag_by_name (info->buffer, tag_name,
-+ &start_iter, iter);
-+
-+ tags = tags->next;
-+ }
-+
-+ gtk_text_buffer_move_mark (info->buffer, mark, iter);
-+
-+ tmp = tmp->next;
-+ }
-+
-+ gtk_text_buffer_delete_mark (info->buffer, mark);
-+}
-+
-+
-+
-+static int
-+read_int (const guchar *start)
-+{
-+ int result;
-+
-+ result =
-+ start[0] << 24 |
-+ start[1] << 16 |
-+ start[2] << 8 |
-+ start[3];
-+
-+ return result;
-+}
-+
-+static gboolean
-+header_is (Header *header, const gchar *id)
-+{
-+ return (strncmp (header->id, id, 8) == 0);
-+}
-+
-+static GList *
-+read_headers (const gchar *start,
-+ gint len,
-+ GError **error)
-+{
-+ int i = 0;
-+ int section_len;
-+ Header *header;
-+ GList *headers = NULL;
-+
-+ while (i < len)
-+ {
-+ if (i + 12 >= len)
-+ goto error;
-+
-+ if (strncmp (start + i, "RICHTEXT", 8) == 0 ||
-+ strncmp (start + i, "PIXBDATA", 8) == 0)
-+ {
-+
-+ section_len = read_int (start + i + 8);
-+
-+ if (i + 12 + section_len > len)
-+ goto error;
-+
-+ header = g_new0 (Header, 1);
-+ header->id = start + i;
-+ header->length = section_len;
-+ header->start = start + i + 12;
-+
-+ i += 12 + section_len;
-+
-+ headers = g_list_prepend (headers, header);
-+ }
-+ else
-+ break;
-+
-+ }
-+
-+ return g_list_reverse (headers);
-+
-+ error:
-+ g_list_foreach (headers, (GFunc) g_free, NULL);
-+ g_list_free (headers);
-+
-+ g_set_error (error,
-+ G_MARKUP_ERROR,
-+ G_MARKUP_ERROR_PARSE,
-+ _("Serialized data is malformed"));
-+
-+ return NULL;
-+}
-+
-+static gboolean
-+deserialize_text (GtkTextBuffer *buffer,
-+ GtkTextIter *iter,
-+ const gchar *text,
-+ gint len,
-+ gboolean create_tags,
-+ GError **error,
-+ GList *headers)
-+{
-+ GMarkupParseContext *context;
-+ ParseInfo info;
-+ gboolean retval = FALSE;
-+
-+
-+ static GMarkupParser rich_text_parser = {
-+ start_element_handler,
-+ end_element_handler,
-+ text_handler,
-+ NULL,
-+ NULL
-+ };
-+
-+ parse_info_init (&info, buffer, create_tags, headers);
-+
-+ context = g_markup_parse_context_new (&rich_text_parser,
-+ 0, &info, NULL);
-+
-+ if (!g_markup_parse_context_parse (context,
-+ text,
-+ len,
-+ error))
-+ goto out;
-+
-+ if (!g_markup_parse_context_end_parse (context, error))
-+ goto out;
-+
-+ retval = TRUE;
-+
-+ /* Now insert the text */
-+ insert_text (&info, iter);
-+
-+ out:
-+ parse_info_free (&info);
-+
-+ g_markup_parse_context_free (context);
-+
-+ return retval;
-+}
-+
-+gboolean
-+gtk_text_buffer_deserialize_rich_text (GtkTextBuffer *buffer,
-+ GtkTextIter *iter,
-+ const gchar *text,
-+ gint len,
-+ gboolean create_tags,
-+ GError **error)
-+{
-+ GList *headers;
-+ Header *header;
-+ gboolean retval;
-+
-+ headers = read_headers (text, len, error);
-+
-+ if (!headers)
-+ return FALSE;
-+
-+ header = headers->data;
-+ if (!header_is (header, "RICHTEXT"))
-+ {
-+ g_set_error (error,
-+ G_MARKUP_ERROR,
-+ G_MARKUP_ERROR_PARSE,
-+ _("Serialized data is malformed. First section isn't RICHTEXT"));
-+
-+ retval = FALSE;
-+ goto out;
-+ }
-+
-+ retval = deserialize_text (buffer, iter,
-+ header->start, header->length,
-+ create_tags, error, headers->next);
-+
-+ out:
-+ g_list_foreach (headers, (GFunc)g_free, NULL);
-+ g_list_free (headers);
-+
-+ return retval;
-+}