summaryrefslogtreecommitdiff
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, 1688 insertions, 0 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
index e69de29bb2..39c8f748de 100644
--- a/packages/gtk+/gtk+-2.6.4-1.osso7/gtktextbufferserialize.c.diff
+++ b/packages/gtk+/gtk+-2.6.4-1.osso7/gtktextbufferserialize.c.diff
@@ -0,0 +1,1688 @@
+--- 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;
++}