1 --- gtk+-2.6.4/gtk/gtktextbufferserialize.c 1970-01-01 02:00:00.000000000 +0200
2 +++ gtk+-2.6.4/gtk/gtktextbufferserialize.c 2005-04-06 16:19:38.024757720 +0300
4 +/* gtktextbufferserialize.c
6 + * Copyright (C) 2001 Havoc Pennington
7 + * Copyright (C) 2004 Nokia
9 + * This library is free software; you can redistribute it and/or
10 + * modify it under the terms of the GNU Library General Public
11 + * License as published by the Free Software Foundation; either
12 + * version 2 of the License, or (at your option) any later version.
14 + * This library is distributed in the hope that it will be useful,
15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 + * Library General Public License for more details.
19 + * You should have received a copy of the GNU Library General Public
20 + * License along with this library; if not, write to the
21 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 + * Boston, MA 02111-1307, USA.
25 +/* FIXME: We should use other error codes for the
26 + * parts that deal with the format errors
32 +#include "gdk-pixbuf/gdk-pixdata.h"
33 +#include "gtktextbufferserialize.h"
41 + GString *tag_table_str;
44 + GtkTextIter start, end;
48 +} SerializationContext;
51 +serialize_value (GValue *value)
53 + if (g_value_type_transformable (value->g_type, G_TYPE_STRING))
55 + GValue text_value = { 0 };
58 + g_value_init (&text_value, G_TYPE_STRING);
59 + g_value_transform (value, &text_value);
61 + tmp = g_markup_escape_text (g_value_get_string (&text_value), -1);
62 + g_value_unset (&text_value);
66 + else if (value->g_type == GDK_TYPE_COLOR)
68 + GdkColor *color = g_value_get_boxed (value);
70 + return g_strdup_printf ("%x:%x:%x", color->red, color->green, color->blue);
74 + g_warning ("Type %s is not serializable\n", g_type_name (value->g_type));
81 +deserialize_value (const gchar *str, GValue *value)
83 + if (g_value_type_transformable (G_TYPE_STRING, value->g_type))
85 + GValue text_value = { 0 };
88 + g_value_init (&text_value, G_TYPE_STRING);
89 + g_value_set_static_string (&text_value, str);
91 + retval = g_value_transform (&text_value, value);
92 + g_value_unset (&text_value);
96 + else if (value->g_type == G_TYPE_BOOLEAN)
100 + v = strcmp (str, "TRUE") == 0;
102 + g_value_set_boolean (value, v);
106 + else if (value->g_type == G_TYPE_INT)
111 + v = strtol (str, &tmp, 10);
113 + if (tmp == NULL || tmp == str)
116 + g_value_set_int (value, v);
120 + else if (value->g_type == G_TYPE_DOUBLE)
125 + v = g_ascii_strtod (str, &tmp);
127 + if (tmp == NULL || tmp == str)
130 + g_value_set_double (value, v);
134 + else if (value->g_type == GDK_TYPE_COLOR)
141 + color.red = strtol (old, &tmp, 16);
143 + if (tmp == NULL || tmp == old)
150 + color.green = strtol (old, &tmp, 16);
151 + if (tmp == NULL || tmp == old)
158 + color.blue = strtol (old, &tmp, 16);
160 + if (tmp == NULL || tmp == old || *tmp != '\0')
163 + g_value_set_boxed (value, &color);
167 + else if (G_VALUE_HOLDS_ENUM (value))
169 + GEnumClass *class = G_ENUM_CLASS (g_type_class_peek (value->g_type));
170 + GEnumValue *enum_value;
172 + enum_value = g_enum_get_value_by_name (class, str);
176 + g_value_set_enum (value, enum_value->value);
184 + g_warning ("Type %s can not be deserialized\n", g_type_name (value->g_type));
190 +/* Checks if a param is set, or if it's the default value */
192 +is_param_set (GObject *object, GParamSpec *pspec, GValue *value)
194 + /* We need to special case some attributes here */
195 + if (strcmp (pspec->name, "background-gdk") == 0)
199 + g_object_get (object, "background-set", &is_set, NULL);
203 + g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
205 + g_object_get_property (object, pspec->name, value);
212 + else if (strcmp (pspec->name, "foreground-gdk") == 0)
216 + g_object_get (object, "foreground-set", &is_set, NULL);
220 + g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
222 + g_object_get_property (object, pspec->name, value);
232 + gchar *is_set_name;
234 + is_set_name = g_strdup_printf ("%s-set", pspec->name);
236 + if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), is_set_name) == NULL)
238 + g_free (is_set_name);
243 + g_object_get (object, is_set_name, &is_set, NULL);
247 + g_free (is_set_name);
251 + g_free (is_set_name);
253 + g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
255 + g_object_get_property (object, pspec->name, value);
257 + if (g_param_value_defaults (pspec, value))
259 + g_value_unset (value);
269 +serialize_tag (gpointer key, gpointer data, gpointer user_data)
271 + SerializationContext *context = user_data;
272 + GtkTextTag *tag = data;
274 + GParamSpec **pspecs;
278 + tag_name = g_markup_escape_text (tag->name, -1);
279 + g_string_append_printf (context->tag_table_str, " <tag name=\"%s\" priority=\"%d\">\n", tag_name, tag->priority);
281 + /* Serialize properties */
282 + pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (tag), &n_pspecs);
284 + for (i = 0; i < n_pspecs; i++)
286 + GValue value = { 0 };
289 + if (!(pspecs[i]->flags & G_PARAM_READABLE) ||
290 + !(pspecs[i]->flags & G_PARAM_WRITABLE))
293 + if (!is_param_set (G_OBJECT (tag), pspecs[i], &value))
296 + /* Now serialize the attr */
297 + tmp = g_markup_escape_text (pspecs[i]->name, -1);
298 + g_string_append_printf (context->tag_table_str, " <attr name=\"%s\" ", tmp);
301 + tmp = g_markup_escape_text (g_type_name (pspecs[i]->value_type), -1);
302 + tmp2 = serialize_value (&value);
303 + g_string_append_printf (context->tag_table_str, "type=\"%s\" value=\"%s\" />\n", tmp, tmp2);
308 + g_value_unset (&value);
313 + g_string_append (context->tag_table_str, " </tag>\n");
318 +serialize_tags (SerializationContext *context)
320 + g_string_append (context->tag_table_str, " <text_view_markup>\n");
321 + g_string_append (context->tag_table_str, " <tags>\n");
322 + g_hash_table_foreach (context->tags, serialize_tag, context);
323 + g_string_append (context->tag_table_str, " </tags>\n");
328 +dump_tag_list (const gchar *str, GList *list)
330 + g_print ("%s: ", str);
333 + g_print ("(empty)");
338 + g_print ("%s ", ((GtkTextTag *)list->data)->name);
348 +find_list_delta (GSList *old_list, GSList *new_list,
349 + GList **added, GList **removed)
352 + GList *tmp_added, *tmp_removed;
355 + tmp_removed = NULL;
357 + /* Find added tags */
361 + if (!g_slist_find (old_list, tmp->data))
362 + tmp_added = g_list_prepend (tmp_added, tmp->data);
367 + *added = tmp_added;
369 + /* Find removed tags */
373 + if (!g_slist_find (new_list, tmp->data))
374 + tmp_removed = g_list_prepend (tmp_removed, tmp->data);
379 + /* We reverse the list here to match the xml semantics */
380 + *removed = g_list_reverse (tmp_removed);
384 +serialize_section_header (GString *str,
388 + g_return_if_fail (strlen (name) == 8);
390 + g_string_append (str, name);
392 + g_string_append_c (str, length >> 24);
394 + g_string_append_c (str, (length >> 16) & 0xff);
395 + g_string_append_c (str, (length >> 8) & 0xff);
396 + g_string_append_c (str, length & 0xff);
400 +serialize_text (GtkTextBuffer *buffer, SerializationContext *context)
402 + GtkTextIter iter, old_iter;
403 + GSList *tag_list, *new_tag_list;
404 + GQueue *active_tags;
407 + g_string_append (context->text_str, "<text>");
409 + iter = context->start;
411 + active_tags = g_queue_new ();
415 + GList *added, *removed;
417 + gchar *tmp_text, *escaped_text;
419 + new_tag_list = gtk_text_iter_get_tags (&iter);
420 + find_list_delta (tag_list, new_tag_list, &added, &removed);
422 + /* Handle removed tags */
426 + GtkTextTag *tag = tmp->data;
428 + g_string_append (context->text_str, "</apply_tag>");
430 + /* We might need to drop some of the tags and re-add them afterwards */
431 + while (g_queue_peek_head (active_tags) != tag &&
432 + !g_queue_is_empty (active_tags))
434 + added = g_list_prepend (added, g_queue_pop_head (active_tags));
435 + g_string_append_printf (context->text_str, "</apply_tag>");
438 + g_queue_pop_head (active_tags);
443 + /* Handle added tags */
447 + GtkTextTag *tag = tmp->data;
450 + /* Add it to the tag hash table */
451 + g_hash_table_insert (context->tags, tag, tag);
453 + tag_name = g_markup_escape_text (tag->name, -1);
455 + g_string_append_printf (context->text_str, "<apply_tag name=\"%s\">", tag_name);
458 + g_queue_push_head (active_tags, tag);
463 + g_slist_free (tag_list);
464 + tag_list = new_tag_list;
468 + /* Now try to go to either the next tag toggle, or if a pixbuf appears */
471 + gunichar ch = gtk_text_iter_get_char (&iter);
475 + GdkPixbuf *pixbuf = gtk_text_iter_get_pixbuf (&iter);
478 + g_string_append_printf (context->text_str, "<pixbuf index=\"%d\" />", context->n_pixbufs);
480 + context->n_pixbufs++;
481 + context->pixbufs = g_list_prepend (context->pixbufs, pixbuf);
485 + gtk_text_iter_forward_char (&iter);
487 + if (gtk_text_iter_toggles_tag (&iter, NULL))
491 + /* We might have moved too far */
492 + if (gtk_text_iter_compare (&iter, &context->end) > 0)
493 + iter = context->end;
495 + /* Append the text */
496 + tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
497 + escaped_text = g_markup_escape_text (tmp_text, -1);
500 + g_string_append (context->text_str, escaped_text);
501 + g_free (escaped_text);
503 + while (!gtk_text_iter_equal (&iter, &context->end));
505 + /* Close any open tags */
506 + for (i = 0; i < g_queue_get_length (active_tags); i++) {
507 + g_string_append (context->text_str, "</apply_tag>");
509 + g_queue_free (active_tags);
510 + g_string_append (context->text_str, "</text>\n</text_view_markup>\n");
514 +serialize_pixbufs (SerializationContext *context,
519 + for (list = context->pixbufs; list != NULL; list = list->next)
521 + GdkPixbuf *pixbuf = list->data;
522 + GdkPixdata pixdata;
526 + gdk_pixdata_from_pixbuf (&pixdata, pixbuf, FALSE);
527 + tmp = gdk_pixdata_serialize (&pixdata, &len);
529 + serialize_section_header (text, "PDPIXBUF", len);
530 + g_string_append_len (text, tmp, len);
536 +gtk_text_buffer_serialize_rich_text (GtkTextBuffer *buffer,
537 + const GtkTextIter *start,
538 + const GtkTextIter *end,
541 + SerializationContext context;
544 + context.tags = g_hash_table_new (NULL, NULL);
545 + context.text_str = g_string_new (NULL);
546 + context.tag_table_str = g_string_new (NULL);
547 + context.start = *start;
548 + context.end = *end;
549 + context.n_pixbufs = 0;
550 + context.pixbufs = NULL;
552 + /* We need to serialize the text before the tag table so we know
553 + what tags are used */
554 + serialize_text (buffer, &context);
555 + serialize_tags (&context);
557 + text = g_string_new (NULL);
558 + serialize_section_header (text, "RICHTEXT", context.tag_table_str->len + context.text_str->len);
560 + g_print ("when serializing length is: %d\n", context.tag_table_str->len + context.text_str->len);
562 + g_string_append_len (text, context.tag_table_str->str, context.tag_table_str->len);
563 + g_string_append_len (text, context.text_str->str, context.text_str->len);
565 + context.pixbufs = g_list_reverse (context.pixbufs);
566 + serialize_pixbufs (&context, text);
568 + g_hash_table_destroy (context.tags);
569 + g_list_free (context.pixbufs);
570 + g_string_free (context.text_str, TRUE);
571 + g_string_free (context.tag_table_str, TRUE);
575 + return g_string_free (text, FALSE);
581 + STATE_TEXT_VIEW_MARKUP,
609 + GtkTextBuffer *buffer;
611 + /* Tags that are defined in <tag> elements */
612 + GHashTable *defined_tags;
614 + /* Tag name substitutions */
615 + GHashTable *substitutions;
618 + GtkTextTag *current_tag;
620 + /* Priority of current tag */
621 + gint current_tag_prio;
623 + /* Tags and their priorities */
624 + GList *tag_priorities;
630 + gboolean create_tags;
632 + gboolean parsed_text;
633 + gboolean parsed_tags;
637 +set_error (GError **err,
638 + GMarkupParseContext *context,
641 + const char *format,
648 + g_markup_parse_context_get_position (context, &line, &ch);
650 + va_start (args, format);
651 + str = g_strdup_vprintf (format, args);
654 + g_set_error (err, error_domain, error_code,
655 + ("Line %d character %d: %s"),
662 +push_state (ParseInfo *info,
665 + info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
669 +pop_state (ParseInfo *info)
671 + g_return_if_fail (info->states != NULL);
673 + info->states = g_slist_remove (info->states, info->states->data);
677 +peek_state (ParseInfo *info)
679 + g_return_val_if_fail (info->states != NULL, STATE_START);
681 + return GPOINTER_TO_INT (info->states->data);
684 +#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
689 + const char **retloc;
693 +locate_attributes (GMarkupParseContext *context,
694 + const char *element_name,
695 + const char **attribute_names,
696 + const char **attribute_values,
698 + const char *first_attribute_name,
699 + const char **first_attribute_retloc,
704 + const char **retloc;
706 +#define MAX_ATTRS 24
707 + LocateAttr attrs[MAX_ATTRS];
711 + g_return_val_if_fail (first_attribute_name != NULL, FALSE);
712 + g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
717 + attrs[0].name = first_attribute_name;
718 + attrs[0].retloc = first_attribute_retloc;
719 + *first_attribute_retloc = NULL;
721 + va_start (args, first_attribute_retloc);
723 + name = va_arg (args, const char*);
724 + retloc = va_arg (args, const char**);
726 + while (name != NULL)
728 + g_return_val_if_fail (retloc != NULL, FALSE);
730 + g_assert (n_attrs < MAX_ATTRS);
732 + attrs[n_attrs].name = name;
733 + attrs[n_attrs].retloc = retloc;
737 + name = va_arg (args, const char*);
738 + retloc = va_arg (args, const char**);
747 + while (attribute_names[i])
754 + while (j < n_attrs)
756 + if (strcmp (attrs[j].name, attribute_names[i]) == 0)
758 + retloc = attrs[j].retloc;
760 + if (*retloc != NULL)
762 + set_error (error, context,
764 + G_MARKUP_ERROR_PARSE,
765 + _("Attribute \"%s\" repeated twice on the same <%s> element"),
766 + attrs[j].name, element_name);
771 + *retloc = attribute_values[i];
780 + set_error (error, context,
782 + G_MARKUP_ERROR_PARSE,
783 + _("Attribute \"%s\" is invalid on <%s> element in this context"),
784 + attribute_names[i], element_name);
797 +check_no_attributes (GMarkupParseContext *context,
798 + const char *element_name,
799 + const char **attribute_names,
800 + const char **attribute_values,
803 + if (attribute_names[0] != NULL)
805 + set_error (error, context,
807 + G_MARKUP_ERROR_PARSE,
808 + _("Attribute \"%s\" is invalid on <%s> element in this context"),
809 + attribute_names[0], element_name);
816 +static const gchar *
817 +tag_exists (GMarkupParseContext *context,
822 + const gchar *real_name;
824 + if (info->create_tags)
826 + /* First, try the substitutions */
827 + real_name = g_hash_table_lookup (info->substitutions, name);
832 + /* Next, try the list of defined tags */
833 + if (g_hash_table_lookup (info->defined_tags, name) != NULL)
836 + set_error (error, context,
837 + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
838 + _("Tag \"%s\" has not been defined."), name);
844 + if (gtk_text_tag_table_lookup (info->buffer->tag_table, name) != NULL)
847 + set_error (error, context,
848 + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
849 + _("Tag \"%s\" does not exist in buffer and tags can not be created."), name);
859 + const gchar *start;
863 +get_pixbuf_from_headers (GList *headers, int id, GError **error)
866 + GdkPixdata pixdata;
869 + header = g_list_nth_data (headers, id);
874 + if (!gdk_pixdata_deserialize (&pixdata, header->length, header->start, error))
877 + pixbuf = gdk_pixbuf_from_pixdata (&pixdata, TRUE, error);
879 + g_print ("pixbuf is: %p\n", pixbuf);
885 +parse_apply_tag_element (GMarkupParseContext *context,
886 + const gchar *element_name,
887 + const gchar **attribute_names,
888 + const gchar **attribute_values,
892 + const gchar *name, *tag_name, *id;
894 + g_assert (peek_state (info) == STATE_TEXT ||
895 + peek_state (info) == STATE_APPLY_TAG);
897 + if (ELEMENT_IS ("apply_tag"))
899 + if (!locate_attributes (context, element_name, attribute_names, attribute_values, error,
900 + "name", &name, NULL))
903 + tag_name = tag_exists (context, name, info, error);
908 + info->tag_stack = g_slist_prepend (info->tag_stack, g_strdup (tag_name));
910 + push_state (info, STATE_APPLY_TAG);
912 + else if (ELEMENT_IS ("pixbuf"))
918 + if (!locate_attributes (context, element_name, attribute_names, attribute_values, error,
919 + "index", &id, NULL))
922 + int_id = atoi (id);
923 + pixbuf = get_pixbuf_from_headers (info->headers, int_id, error);
925 + span = g_new0 (TextSpan, 1);
926 + span->pixbuf = pixbuf;
929 + info->spans = g_list_prepend (info->spans, span);
934 + push_state (info, STATE_PIXBUF);
937 + set_error (error, context,
938 + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
939 + _("Element <%s> is not allowed below <%s>"),
940 + element_name, peek_state(info) == STATE_TEXT ? "text" : "apply_tag");
944 +parse_attr_element (GMarkupParseContext *context,
945 + const gchar *element_name,
946 + const gchar **attribute_names,
947 + const gchar **attribute_values,
951 + const gchar *name, *type, *value;
953 + GValue gvalue = { 0 };
956 + g_assert (peek_state (info) == STATE_TAG);
958 + if (ELEMENT_IS ("attr"))
960 + if (!locate_attributes (context, element_name, attribute_names, attribute_values, error,
961 + "name", &name, "type", &type, "value", &value, NULL))
964 + gtype = g_type_from_name (type);
966 + if (gtype == G_TYPE_INVALID)
968 + set_error (error, context,
969 + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
970 + _("\"%s\" is not a valid attribute type"), type);
974 + if (!(pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info->current_tag), name)))
976 + set_error (error, context,
977 + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
978 + _("\"%s\" is not a valid attribute name"), name);
982 + g_value_init (&gvalue, gtype);
984 + if (!deserialize_value (value, &gvalue))
986 + set_error (error, context,
987 + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
988 + _("\"%s\" could not be converted to a value of type \"%s\" for attribute \"%s\""),
989 + value, type, name);
993 + if (g_param_value_validate (pspec, &gvalue))
995 + set_error (error, context,
996 + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
997 + _("\"%s\" is not a valid value of for attribute \"%s\""),
999 + g_value_unset (&gvalue);
1003 + g_object_set_property (G_OBJECT (info->current_tag),
1006 + g_value_unset (&gvalue);
1008 + push_state (info, STATE_ATTR);
1012 + set_error (error, context,
1013 + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1014 + _("Element <%s> is not allowed below <%s>"),
1015 + element_name, "tag");
1021 +get_tag_name (ParseInfo *info,
1022 + const gchar *tag_name)
1027 + name = g_strdup (tag_name);
1029 + if (!info->create_tags)
1034 + while (gtk_text_tag_table_lookup (info->buffer->tag_table, name) != NULL)
1037 + name = g_strdup_printf ("%s-%d", tag_name, ++i);
1042 + g_hash_table_insert (info->substitutions, g_strdup (tag_name), g_strdup (name));
1049 +parse_tag_element (GMarkupParseContext *context,
1050 + const gchar *element_name,
1051 + const gchar **attribute_names,
1052 + const gchar **attribute_values,
1056 + const gchar *name, *priority;
1061 + g_assert (peek_state (info) == STATE_TAGS);
1063 + if (ELEMENT_IS ("tag"))
1065 + if (!locate_attributes (context, element_name, attribute_names, attribute_values, error,
1066 + "name", &name, "priority", &priority, NULL))
1069 + if (g_hash_table_lookup (info->defined_tags, name) != NULL)
1071 + set_error (error, context,
1072 + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1073 + _("Tag \"%s\" already defined"), name);
1077 + prio = strtol (priority, &tmp, 10);
1079 + if (tmp == NULL || tmp == priority)
1081 + set_error (error, context,
1082 + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1083 + _("Tag \"%s\" has invalid priority \"%s\""), name, priority);
1087 + tag_name = get_tag_name (info, name);
1088 + info->current_tag = gtk_text_tag_new (tag_name);
1089 + info->current_tag_prio = prio;
1091 + g_free (tag_name);
1093 + push_state (info, STATE_TAG);
1097 + set_error (error, context,
1098 + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1099 + _("Element <%s> is not allowed below <%s>"),
1100 + element_name, "tags");
1105 +start_element_handler (GMarkupParseContext *context,
1106 + const gchar *element_name,
1107 + const gchar **attribute_names,
1108 + const gchar **attribute_values,
1109 + gpointer user_data,
1112 + ParseInfo *info = user_data;
1114 + switch (peek_state (info))
1117 + if (ELEMENT_IS ("text_view_markup"))
1119 + if (!check_no_attributes (context, element_name,
1120 + attribute_names, attribute_values, error))
1123 + push_state (info, STATE_TEXT_VIEW_MARKUP);
1127 + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1128 + _("Outermost element in text must be <text_view_markup> not <%s>"),
1131 + case STATE_TEXT_VIEW_MARKUP:
1132 + if (ELEMENT_IS ("tags"))
1134 + if (info->parsed_tags)
1136 + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1137 + _("A <tags> element has already been specified"));
1141 + if (!check_no_attributes (context, element_name,
1142 + attribute_names, attribute_values, error))
1145 + push_state (info, STATE_TAGS);
1148 + else if (ELEMENT_IS ("text"))
1150 + if (info->parsed_text)
1152 + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1153 + _("A <text> element has already been specified"));
1156 + else if (!info->parsed_tags)
1158 + set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1159 + _("A <text> element can't occur before a <tags> element"));
1163 + if (!check_no_attributes (context, element_name,
1164 + attribute_names, attribute_values, error))
1167 + push_state (info, STATE_TEXT);
1171 + set_error (error, context,
1172 + G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1173 + _("Element <%s> is not allowed below <%s>"),
1174 + element_name, "text_view_markup");
1177 + parse_tag_element (context, element_name,
1178 + attribute_names, attribute_values,
1182 + parse_attr_element (context, element_name,
1183 + attribute_names, attribute_values,
1187 + case STATE_APPLY_TAG:
1188 + parse_apply_tag_element (context, element_name,
1189 + attribute_names, attribute_values,
1193 + g_assert_not_reached ();
1199 +sort_tag_prio (TextTagPrio *a,
1202 + if (a->prio < b->prio)
1204 + else if (a->prio > b->prio)
1211 +end_element_handler (GMarkupParseContext *context,
1212 + const gchar *element_name,
1213 + gpointer user_data,
1216 + ParseInfo *info = user_data;
1220 + switch (peek_state (info))
1224 + g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
1226 + info->parsed_tags = TRUE;
1228 + /* Sort list and add the tags */
1229 + info->tag_priorities = g_list_sort (info->tag_priorities,
1230 + (GCompareFunc)sort_tag_prio);
1231 + list = info->tag_priorities;
1234 + TextTagPrio *prio = list->data;
1236 + if (info->create_tags)
1237 + gtk_text_tag_table_add (info->buffer->tag_table, prio->tag);
1239 + g_object_unref (prio->tag);
1242 + list = list->next;
1248 + g_assert (peek_state (info) == STATE_TAGS);
1250 + /* Add tag to defined tags hash */
1251 + tmp = g_strdup (info->current_tag->name);
1252 + g_hash_table_insert (info->defined_tags,
1255 + if (info->create_tags)
1257 + TextTagPrio *prio;
1259 + /* add the tag to the list */
1260 + prio = g_new0 (TextTagPrio, 1);
1261 + prio->prio = info->current_tag_prio;
1262 + prio->tag = info->current_tag;
1264 + info->tag_priorities = g_list_prepend (info->tag_priorities, prio);
1267 + info->current_tag = NULL;
1271 + g_assert (peek_state (info) == STATE_TAG);
1273 + case STATE_APPLY_TAG:
1275 + g_assert (peek_state (info) == STATE_APPLY_TAG ||
1276 + peek_state (info) == STATE_TEXT);
1279 + g_free (info->tag_stack->data);
1280 + info->tag_stack = g_slist_delete_link (info->tag_stack,
1286 + g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
1288 + info->spans = g_list_reverse (info->spans);
1289 + info->parsed_text = TRUE;
1291 + case STATE_TEXT_VIEW_MARKUP:
1293 + g_assert (peek_state (info) == STATE_START);
1295 + case STATE_PIXBUF:
1297 + g_assert (peek_state (info) == STATE_APPLY_TAG ||
1298 + peek_state (info) == STATE_TEXT);
1301 + g_assert_not_reached ();
1307 +all_whitespace (const char *text,
1314 + end = text + text_len;
1318 + if (!g_ascii_isspace (*p))
1321 + p = g_utf8_next_char (p);
1328 +copy_tag_list (GSList *tag_list)
1330 + GSList *tmp = NULL;
1334 + tmp = g_slist_prepend (tmp, g_strdup (tag_list->data));
1336 + tag_list = tag_list->next;
1343 +text_handler (GMarkupParseContext *context,
1344 + const gchar *text,
1346 + gpointer user_data,
1349 + ParseInfo *info = user_data;
1352 + if (all_whitespace (text, text_len) &&
1353 + peek_state (info) != STATE_TEXT &&
1354 + peek_state (info) != STATE_APPLY_TAG)
1357 + switch (peek_state (info))
1360 + g_assert_not_reached (); /* gmarkup shouldn't do this */
1363 + case STATE_APPLY_TAG:
1364 + if (text_len == 0)
1367 + span = g_new0 (TextSpan, 1);
1368 + span->text = g_strndup (text, text_len);
1369 + span->tags = copy_tag_list (info->tag_stack);
1371 + info->spans = g_list_prepend (info->spans, span);
1374 + g_assert_not_reached ();
1380 +parse_info_init (ParseInfo *info,
1381 + GtkTextBuffer *buffer,
1382 + gboolean create_tags,
1385 + info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
1387 + info->create_tags = create_tags;
1388 + info->headers = headers;
1389 + info->defined_tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1390 + info->substitutions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1391 + info->tag_stack = NULL;
1392 + info->spans = NULL;
1393 + info->parsed_text = FALSE;
1394 + info->parsed_tags = FALSE;
1395 + info->current_tag = NULL;
1396 + info->current_tag_prio = -1;
1397 + info->tag_priorities = NULL;
1399 + info->buffer = buffer;
1403 +text_span_free (TextSpan *span)
1407 + g_free (span->text);
1412 + g_free (tmp->data);
1416 + g_slist_free (span->tags);
1421 +parse_info_free (ParseInfo *info)
1426 + slist = info->tag_stack;
1429 + g_free (slist->data);
1431 + slist = slist->next;
1434 + g_slist_free (info->tag_stack);
1435 + g_slist_free (info->states);
1437 + g_hash_table_destroy (info->substitutions);
1438 + g_hash_table_destroy (info->defined_tags);
1440 + if (info->current_tag)
1441 + g_object_unref (info->current_tag);
1443 + list = info->spans;
1446 + text_span_free (list->data);
1448 + list = list->next;
1450 + g_list_free (info->spans);
1452 + list = info->tag_priorities;
1455 + TextTagPrio *prio = list->data;
1458 + g_object_unref (prio->tag);
1461 + list = list->next;
1463 + g_list_free (info->tag_priorities);
1467 +static const gchar *
1468 +get_tag_substitution (ParseInfo *info,
1469 + const gchar *name)
1473 + if ((subst = g_hash_table_lookup (info->substitutions, name)))
1480 +insert_text (ParseInfo *info,
1481 + GtkTextIter *iter)
1483 + GtkTextIter start_iter;
1484 + GtkTextMark *mark;
1488 + start_iter = *iter;
1490 + mark = gtk_text_buffer_create_mark (info->buffer, "deserialize_insert_point",
1491 + &start_iter, TRUE);
1493 + tmp = info->spans;
1496 + TextSpan *span = tmp->data;
1499 + gtk_text_buffer_insert (info->buffer, iter, span->text, -1);
1502 + gtk_text_buffer_insert_pixbuf (info->buffer, iter, span->pixbuf);
1503 + g_object_unref (span->pixbuf);
1505 + gtk_text_buffer_get_iter_at_mark (info->buffer, &start_iter, mark);
1508 + tags = span->tags;
1511 + const gchar *tag_name = get_tag_substitution (info, tags->data);
1513 + gtk_text_buffer_apply_tag_by_name (info->buffer, tag_name,
1514 + &start_iter, iter);
1516 + tags = tags->next;
1519 + gtk_text_buffer_move_mark (info->buffer, mark, iter);
1524 + gtk_text_buffer_delete_mark (info->buffer, mark);
1530 +read_int (const guchar *start)
1544 +header_is (Header *header, const gchar *id)
1546 + return (strncmp (header->id, id, 8) == 0);
1550 +read_headers (const gchar *start,
1557 + GList *headers = NULL;
1561 + if (i + 12 >= len)
1564 + if (strncmp (start + i, "RICHTEXT", 8) == 0 ||
1565 + strncmp (start + i, "PIXBDATA", 8) == 0)
1568 + section_len = read_int (start + i + 8);
1570 + if (i + 12 + section_len > len)
1573 + header = g_new0 (Header, 1);
1574 + header->id = start + i;
1575 + header->length = section_len;
1576 + header->start = start + i + 12;
1578 + i += 12 + section_len;
1580 + headers = g_list_prepend (headers, header);
1587 + return g_list_reverse (headers);
1590 + g_list_foreach (headers, (GFunc) g_free, NULL);
1591 + g_list_free (headers);
1593 + g_set_error (error,
1595 + G_MARKUP_ERROR_PARSE,
1596 + _("Serialized data is malformed"));
1602 +deserialize_text (GtkTextBuffer *buffer,
1603 + GtkTextIter *iter,
1604 + const gchar *text,
1606 + gboolean create_tags,
1610 + GMarkupParseContext *context;
1612 + gboolean retval = FALSE;
1615 + static GMarkupParser rich_text_parser = {
1616 + start_element_handler,
1617 + end_element_handler,
1623 + parse_info_init (&info, buffer, create_tags, headers);
1625 + context = g_markup_parse_context_new (&rich_text_parser,
1628 + if (!g_markup_parse_context_parse (context,
1634 + if (!g_markup_parse_context_end_parse (context, error))
1639 + /* Now insert the text */
1640 + insert_text (&info, iter);
1643 + parse_info_free (&info);
1645 + g_markup_parse_context_free (context);
1651 +gtk_text_buffer_deserialize_rich_text (GtkTextBuffer *buffer,
1652 + GtkTextIter *iter,
1653 + const gchar *text,
1655 + gboolean create_tags,
1662 + headers = read_headers (text, len, error);
1667 + header = headers->data;
1668 + if (!header_is (header, "RICHTEXT"))
1670 + g_set_error (error,
1672 + G_MARKUP_ERROR_PARSE,
1673 + _("Serialized data is malformed. First section isn't RICHTEXT"));
1679 + retval = deserialize_text (buffer, iter,
1680 + header->start, header->length,
1681 + create_tags, error, headers->next);
1684 + g_list_foreach (headers, (GFunc)g_free, NULL);
1685 + g_list_free (headers);