1 /* gtktextbufferserialize.c
3 * Copyright (C) 2001 Havoc Pennington
4 * Copyright (C) 2004 Nokia Corporation
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
22 /* FIXME: We should use other error codes for the
23 * parts that deal with the format errors
32 #include "gdk-pixbuf/gdk-pixdata.h"
33 #include "gtktextbufferserialize.h"
40 GString *tag_table_str;
43 GtkTextIter start, end;
48 GHashTable *tag_id_tags;
49 } SerializationContext;
52 serialize_value (GValue *value)
54 if (g_value_type_transformable (value->g_type, G_TYPE_STRING))
56 GValue text_value = { 0 };
59 g_value_init (&text_value, G_TYPE_STRING);
60 g_value_transform (value, &text_value);
62 tmp = g_markup_escape_text (g_value_get_string (&text_value), -1);
63 g_value_unset (&text_value);
67 else if (value->g_type == GDK_TYPE_COLOR)
69 GdkColor *color = g_value_get_boxed (value);
71 return g_strdup_printf ("%x:%x:%x", color->red, color->green, color->blue);
73 else if (g_type_is_a (value->g_type, GDK_TYPE_DRAWABLE))
75 /* Don't do anything */
79 g_warning ("Type %s is not serializable\n", g_type_name (value->g_type));
86 deserialize_value (const gchar *str,
89 if (g_value_type_transformable (G_TYPE_STRING, value->g_type))
91 GValue text_value = { 0 };
94 g_value_init (&text_value, G_TYPE_STRING);
95 g_value_set_static_string (&text_value, str);
97 retval = g_value_transform (&text_value, value);
98 g_value_unset (&text_value);
102 else if (value->g_type == G_TYPE_BOOLEAN)
106 v = strcmp (str, "TRUE") == 0;
108 g_value_set_boolean (value, v);
112 else if (value->g_type == G_TYPE_INT)
117 v = strtol (str, &tmp, 10);
119 if (tmp == NULL || tmp == str)
122 g_value_set_int (value, v);
126 else if (value->g_type == G_TYPE_DOUBLE)
131 v = g_ascii_strtod (str, &tmp);
133 if (tmp == NULL || tmp == str)
136 g_value_set_double (value, v);
140 else if (value->g_type == GDK_TYPE_COLOR)
147 color.red = strtol (old, &tmp, 16);
149 if (tmp == NULL || tmp == old)
156 color.green = strtol (old, &tmp, 16);
157 if (tmp == NULL || tmp == old)
164 color.blue = strtol (old, &tmp, 16);
166 if (tmp == NULL || tmp == old || *tmp != '\0')
169 g_value_set_boxed (value, &color);
173 else if (G_VALUE_HOLDS_ENUM (value))
175 GEnumClass *class = G_ENUM_CLASS (g_type_class_peek (value->g_type));
176 GEnumValue *enum_value;
178 enum_value = g_enum_get_value_by_name (class, str);
182 g_value_set_enum (value, enum_value->value);
190 g_warning ("Type %s can not be deserialized\n", g_type_name (value->g_type));
196 /* Checks if a param is set, or if it's the default value */
198 is_param_set (GObject *object,
202 /* We need to special case some attributes here */
203 if (strcmp (pspec->name, "background-gdk") == 0)
207 g_object_get (object, "background-set", &is_set, NULL);
211 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
213 g_object_get_property (object, pspec->name, value);
220 else if (strcmp (pspec->name, "foreground-gdk") == 0)
224 g_object_get (object, "foreground-set", &is_set, NULL);
228 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
230 g_object_get_property (object, pspec->name, value);
242 is_set_name = g_strdup_printf ("%s-set", pspec->name);
244 if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), is_set_name) == NULL)
246 g_free (is_set_name);
251 g_object_get (object, is_set_name, &is_set, NULL);
255 g_free (is_set_name);
259 g_free (is_set_name);
261 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
263 g_object_get_property (object, pspec->name, value);
265 if (g_param_value_defaults (pspec, value))
267 g_value_unset (value);
277 serialize_tag (gpointer key,
281 SerializationContext *context = user_data;
282 GtkTextTag *tag = data;
289 g_string_append (context->tag_table_str, " <tag ");
291 /* Handle anonymous tags */
294 tag_name = g_markup_escape_text (tag->name, -1);
295 g_string_append_printf (context->tag_table_str, "name=\"%s\"", tag_name);
300 tag_id = GPOINTER_TO_INT (g_hash_table_lookup (context->tag_id_tags, tag));
302 g_string_append_printf (context->tag_table_str, "id=\"%d\"", tag_id);
305 g_string_append_printf (context->tag_table_str, " priority=\"%d\">\n", tag->priority);
307 /* Serialize properties */
308 pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (tag), &n_pspecs);
310 for (i = 0; i < n_pspecs; i++)
312 GValue value = { 0 };
315 if (!(pspecs[i]->flags & G_PARAM_READABLE) ||
316 !(pspecs[i]->flags & G_PARAM_WRITABLE))
319 if (!is_param_set (G_OBJECT (tag), pspecs[i], &value))
322 /* Now serialize the attr */
323 tmp2 = serialize_value (&value);
327 tmp = g_markup_escape_text (pspecs[i]->name, -1);
328 g_string_append_printf (context->tag_table_str, " <attr name=\"%s\" ", tmp);
331 tmp = g_markup_escape_text (g_type_name (pspecs[i]->value_type), -1);
332 g_string_append_printf (context->tag_table_str, "type=\"%s\" value=\"%s\" />\n", tmp, tmp2);
338 g_value_unset (&value);
343 g_string_append (context->tag_table_str, " </tag>\n");
347 serialize_tags (SerializationContext *context)
349 g_string_append (context->tag_table_str, " <text_view_markup>\n");
350 g_string_append (context->tag_table_str, " <tags>\n");
351 g_hash_table_foreach (context->tags, serialize_tag, context);
352 g_string_append (context->tag_table_str, " </tags>\n");
357 dump_tag_list (const gchar *str,
360 g_print ("%s: ", str);
368 g_print ("%s ", ((GtkTextTag *)list->data)->name);
378 find_list_delta (GSList *old_list,
384 GList *tmp_added, *tmp_removed;
389 /* Find added tags */
393 if (!g_slist_find (old_list, tmp->data))
394 tmp_added = g_list_prepend (tmp_added, tmp->data);
401 /* Find removed tags */
405 if (!g_slist_find (new_list, tmp->data))
406 tmp_removed = g_list_prepend (tmp_removed, tmp->data);
411 /* We reverse the list here to match the xml semantics */
412 *removed = g_list_reverse (tmp_removed);
416 serialize_section_header (GString *str,
420 g_return_if_fail (strlen (name) == 26);
422 g_string_append (str, name);
424 g_string_append_c (str, length >> 24);
426 g_string_append_c (str, (length >> 16) & 0xff);
427 g_string_append_c (str, (length >> 8) & 0xff);
428 g_string_append_c (str, length & 0xff);
432 serialize_text (GtkTextBuffer *buffer,
433 SerializationContext *context)
435 GtkTextIter iter, old_iter;
436 GSList *tag_list, *new_tag_list;
440 g_string_append (context->text_str, "<text>");
442 iter = context->start;
444 active_tags = g_queue_new ();
448 GList *added, *removed;
450 gchar *tmp_text, *escaped_text;
452 new_tag_list = gtk_text_iter_get_tags (&iter);
453 find_list_delta (tag_list, new_tag_list, &added, &removed);
455 /* Handle removed tags */
459 GtkTextTag *tag = tmp->data;
461 g_string_append (context->text_str, "</apply_tag>");
463 /* We might need to drop some of the tags and re-add them afterwards */
464 while (g_queue_peek_head (active_tags) != tag &&
465 !g_queue_is_empty (active_tags))
467 added = g_list_prepend (added, g_queue_pop_head (active_tags));
468 g_string_append_printf (context->text_str, "</apply_tag>");
471 g_queue_pop_head (active_tags);
476 /* Handle added tags */
480 GtkTextTag *tag = tmp->data;
483 /* Add it to the tag hash table */
484 g_hash_table_insert (context->tags, tag, tag);
488 tag_name = g_markup_escape_text (tag->name, -1);
490 g_string_append_printf (context->text_str, "<apply_tag name=\"%s\">", tag_name);
497 /* We've got an anonymous tag, find out if it's been
499 if (!g_hash_table_lookup_extended (context->tag_id_tags, tag, NULL, &tag_id))
501 tag_id = GINT_TO_POINTER (context->tag_id++);
503 g_hash_table_insert (context->tag_id_tags, tag, tag_id);
506 g_string_append_printf (context->text_str, "<apply_tag id=\"%d\">", GPOINTER_TO_INT (tag_id));
508 g_queue_push_head (active_tags, tag);
513 g_slist_free (tag_list);
514 tag_list = new_tag_list;
518 /* Now try to go to either the next tag toggle, or if a pixbuf appears */
521 gunichar ch = gtk_text_iter_get_char (&iter);
525 GdkPixbuf *pixbuf = gtk_text_iter_get_pixbuf (&iter);
529 /* Append the text before the pixbuf */
530 tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
531 escaped_text = g_markup_escape_text (tmp_text, -1);
534 /* Forward so we don't get the 0xfffc char */
535 gtk_text_iter_forward_char (&iter);
538 g_string_append (context->text_str, escaped_text);
539 g_free (escaped_text);
541 g_string_append_printf (context->text_str, "<pixbuf index=\"%d\" />", context->n_pixbufs);
543 context->n_pixbufs++;
544 context->pixbufs = g_list_prepend (context->pixbufs, pixbuf);
552 gtk_text_iter_forward_char (&iter);
554 if (gtk_text_iter_toggles_tag (&iter, NULL))
558 /* We might have moved too far */
559 if (gtk_text_iter_compare (&iter, &context->end) > 0)
562 /* Append the text */
563 tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
564 escaped_text = g_markup_escape_text (tmp_text, -1);
567 g_string_append (context->text_str, escaped_text);
568 g_free (escaped_text);
570 while (!gtk_text_iter_equal (&iter, &context->end));
572 /* Close any open tags */
573 for (i = 0; i < g_queue_get_length (active_tags); i++) {
574 g_string_append (context->text_str, "</apply_tag>");
576 g_queue_free (active_tags);
577 g_string_append (context->text_str, "</text>\n</text_view_markup>\n");
581 serialize_pixbufs (SerializationContext *context,
586 for (list = context->pixbufs; list != NULL; list = list->next)
588 GdkPixbuf *pixbuf = list->data;
593 gdk_pixdata_from_pixbuf (&pixdata, pixbuf, FALSE);
594 tmp = gdk_pixdata_serialize (&pixdata, &len);
596 serialize_section_header (text, "GTKTEXTBUFFERPIXBDATA-0001", len);
597 g_string_append_len (text, (gchar *) tmp, len);
603 _gtk_text_buffer_serialize_rich_text (GtkTextBuffer *register_buffer,
604 GtkTextBuffer *content_buffer,
605 const GtkTextIter *start,
606 const GtkTextIter *end,
610 SerializationContext context;
613 context.tags = g_hash_table_new (NULL, NULL);
614 context.text_str = g_string_new (NULL);
615 context.tag_table_str = g_string_new (NULL);
616 context.start = *start;
618 context.n_pixbufs = 0;
619 context.pixbufs = NULL;
621 context.tag_id_tags = g_hash_table_new (NULL, NULL);
623 /* We need to serialize the text before the tag table so we know
624 what tags are used */
625 serialize_text (content_buffer, &context);
626 serialize_tags (&context);
628 text = g_string_new (NULL);
629 serialize_section_header (text, "GTKTEXTBUFFERCONTENTS-0001",
630 context.tag_table_str->len + context.text_str->len);
632 g_string_append_len (text, context.tag_table_str->str, context.tag_table_str->len);
633 g_string_append_len (text, context.text_str->str, context.text_str->len);
635 context.pixbufs = g_list_reverse (context.pixbufs);
636 serialize_pixbufs (&context, text);
638 g_hash_table_destroy (context.tags);
639 g_list_free (context.pixbufs);
640 g_string_free (context.text_str, TRUE);
641 g_string_free (context.tag_table_str, TRUE);
642 g_hash_table_destroy (context.tag_id_tags);
646 return (guint8 *) g_string_free (text, FALSE);
652 STATE_TEXT_VIEW_MARKUP,
680 GtkTextBuffer *buffer;
682 /* Tags that are defined in <tag> elements */
683 GHashTable *defined_tags;
685 /* Tags that are anonymous */
686 GHashTable *anonymous_tags;
688 /* Tag name substitutions */
689 GHashTable *substitutions;
692 GtkTextTag *current_tag;
694 /* Priority of current tag */
695 gint current_tag_prio;
697 /* Id of current tag */
700 /* Tags and their priorities */
701 GList *tag_priorities;
707 gboolean create_tags;
709 gboolean parsed_text;
710 gboolean parsed_tags;
714 set_error (GError **err,
715 GMarkupParseContext *context,
725 g_markup_parse_context_get_position (context, &line, &ch);
727 va_start (args, format);
728 str = g_strdup_vprintf (format, args);
731 g_set_error (err, error_domain, error_code,
732 ("Line %d character %d: %s"),
739 push_state (ParseInfo *info,
742 info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
746 pop_state (ParseInfo *info)
748 g_return_if_fail (info->states != NULL);
750 info->states = g_slist_remove (info->states, info->states->data);
754 peek_state (ParseInfo *info)
756 g_return_val_if_fail (info->states != NULL, STATE_START);
758 return GPOINTER_TO_INT (info->states->data);
761 #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
765 check_id_or_name (GMarkupParseContext *context,
766 const gchar *element_name,
767 const gchar **attribute_names,
768 const gchar **attribute_values,
773 gboolean has_id = FALSE;
774 gboolean has_name = FALSE;
780 for (i = 0; attribute_names[i] != NULL; i++)
782 if (strcmp (attribute_names[i], "name") == 0)
784 *name = attribute_values[i];
788 set_error (error, context,
790 G_MARKUP_ERROR_PARSE,
791 _("Both \"id\" and \"name\" were found on the <%s> element"),
798 set_error (error, context,
800 G_MARKUP_ERROR_PARSE,
801 _("The attribute \"%s\" was found twice on the <%s> element"),
802 "name", element_name);
808 else if (strcmp (attribute_names[i], "id") == 0)
814 set_error (error, context,
816 G_MARKUP_ERROR_PARSE,
817 _("Both \"id\" and \"name\" were found on the <%s> element"),
824 set_error (error, context,
826 G_MARKUP_ERROR_PARSE,
827 _("The attribute \"%s\" was found twice on the <%s> element"),
834 /* Try parsing the integer */
835 *id = strtol (attribute_values[i], &tmp, 10);
837 if (tmp == NULL || tmp == attribute_values[i])
839 set_error (error, context,
840 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
841 _("<%s> element has invalid id \"%s\""), attribute_values[i]);
847 if (!has_id && !has_name)
849 set_error (error, context,
850 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
851 _("<%s> element has neither a \"name\" nor an \"id\" element"), element_name);
865 locate_attributes (GMarkupParseContext *context,
866 const char *element_name,
867 const char **attribute_names,
868 const char **attribute_values,
869 gboolean allow_unknown_attrs,
871 const char *first_attribute_name,
872 const char **first_attribute_retloc,
880 LocateAttr attrs[MAX_ATTRS];
884 g_return_val_if_fail (first_attribute_name != NULL, FALSE);
885 g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
890 attrs[0].name = first_attribute_name;
891 attrs[0].retloc = first_attribute_retloc;
892 *first_attribute_retloc = NULL;
894 va_start (args, first_attribute_retloc);
896 name = va_arg (args, const char*);
897 retloc = va_arg (args, const char**);
901 g_return_val_if_fail (retloc != NULL, FALSE);
903 g_assert (n_attrs < MAX_ATTRS);
905 attrs[n_attrs].name = name;
906 attrs[n_attrs].retloc = retloc;
910 name = va_arg (args, const char*);
911 retloc = va_arg (args, const char**);
920 while (attribute_names[i])
929 if (strcmp (attrs[j].name, attribute_names[i]) == 0)
931 retloc = attrs[j].retloc;
935 set_error (error, context,
937 G_MARKUP_ERROR_PARSE,
938 _("Attribute \"%s\" repeated twice on the same <%s> element"),
939 attrs[j].name, element_name);
944 *retloc = attribute_values[i];
951 if (!found && !allow_unknown_attrs)
953 set_error (error, context,
955 G_MARKUP_ERROR_PARSE,
956 _("Attribute \"%s\" is invalid on <%s> element in this context"),
957 attribute_names[i], element_name);
970 check_no_attributes (GMarkupParseContext *context,
971 const char *element_name,
972 const char **attribute_names,
973 const char **attribute_values,
976 if (attribute_names[0] != NULL)
978 set_error (error, context,
980 G_MARKUP_ERROR_PARSE,
981 _("Attribute \"%s\" is invalid on <%s> element in this context"),
982 attribute_names[0], element_name);
990 tag_exists (GMarkupParseContext *context,
996 const gchar *real_name;
998 if (info->create_tags)
1000 /* If we have an anonymous tag, just return it directly */
1002 return g_hash_table_lookup (info->anonymous_tags,
1003 GINT_TO_POINTER (id));
1005 /* First, try the substitutions */
1006 real_name = g_hash_table_lookup (info->substitutions, name);
1009 return gtk_text_tag_table_lookup (info->buffer->tag_table, real_name);
1011 /* Next, try the list of defined tags */
1012 if (g_hash_table_lookup (info->defined_tags, name) != NULL)
1013 return gtk_text_tag_table_lookup (info->buffer->tag_table, name);
1015 set_error (error, context,
1016 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1017 _("Tag \"%s\" has not been defined."), name);
1027 set_error (error, context,
1028 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1029 _("Anonymous tag found and tags can not be created."));
1033 tag = gtk_text_tag_table_lookup (info->buffer->tag_table, name);
1038 set_error (error, context,
1039 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1040 _("Tag \"%s\" does not exist in buffer and tags can not be created."), name);
1054 get_pixbuf_from_headers (GList *headers,
1062 header = g_list_nth_data (headers, id);
1067 if (!gdk_pixdata_deserialize (&pixdata, header->length,
1068 (const guint8 *) header->start, error))
1071 pixbuf = gdk_pixbuf_from_pixdata (&pixdata, TRUE, error);
1077 parse_apply_tag_element (GMarkupParseContext *context,
1078 const gchar *element_name,
1079 const gchar **attribute_names,
1080 const gchar **attribute_values,
1084 const gchar *name, *priority;
1088 g_assert (peek_state (info) == STATE_TEXT ||
1089 peek_state (info) == STATE_APPLY_TAG);
1091 if (ELEMENT_IS ("apply_tag"))
1093 if (!locate_attributes (context, element_name, attribute_names, attribute_values, TRUE, error,
1094 "priority", &priority, NULL))
1097 if (!check_id_or_name (context, element_name, attribute_names, attribute_values,
1102 tag = tag_exists (context, name, id, info, error);
1107 info->tag_stack = g_slist_prepend (info->tag_stack, tag);
1109 push_state (info, STATE_APPLY_TAG);
1111 else if (ELEMENT_IS ("pixbuf"))
1116 const gchar *pixbuf_id;
1118 if (!locate_attributes (context, element_name, attribute_names, attribute_values, FALSE, error,
1119 "index", &pixbuf_id, NULL))
1122 int_id = atoi (pixbuf_id);
1123 pixbuf = get_pixbuf_from_headers (info->headers, int_id, error);
1125 span = g_new0 (TextSpan, 1);
1126 span->pixbuf = pixbuf;
1129 info->spans = g_list_prepend (info->spans, span);
1134 push_state (info, STATE_PIXBUF);
1137 set_error (error, context,
1138 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1139 _("Element <%s> is not allowed below <%s>"),
1140 element_name, peek_state(info) == STATE_TEXT ? "text" : "apply_tag");
1144 parse_attr_element (GMarkupParseContext *context,
1145 const gchar *element_name,
1146 const gchar **attribute_names,
1147 const gchar **attribute_values,
1151 const gchar *name, *type, *value;
1153 GValue gvalue = { 0 };
1156 g_assert (peek_state (info) == STATE_TAG);
1158 if (ELEMENT_IS ("attr"))
1160 if (!locate_attributes (context, element_name, attribute_names, attribute_values, FALSE, error,
1161 "name", &name, "type", &type, "value", &value, NULL))
1164 gtype = g_type_from_name (type);
1166 if (gtype == G_TYPE_INVALID)
1168 set_error (error, context,
1169 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1170 _("\"%s\" is not a valid attribute type"), type);
1174 if (!(pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info->current_tag), name)))
1176 set_error (error, context,
1177 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1178 _("\"%s\" is not a valid attribute name"), name);
1182 g_value_init (&gvalue, gtype);
1184 if (!deserialize_value (value, &gvalue))
1186 set_error (error, context,
1187 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1188 _("\"%s\" could not be converted to a value of type \"%s\" for attribute \"%s\""),
1193 if (g_param_value_validate (pspec, &gvalue))
1195 set_error (error, context,
1196 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1197 _("\"%s\" is not a valid value for attribute \"%s\""),
1199 g_value_unset (&gvalue);
1203 g_object_set_property (G_OBJECT (info->current_tag),
1206 g_value_unset (&gvalue);
1208 push_state (info, STATE_ATTR);
1212 set_error (error, context,
1213 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1214 _("Element <%s> is not allowed below <%s>"),
1215 element_name, "tag");
1221 get_tag_name (ParseInfo *info,
1222 const gchar *tag_name)
1227 name = g_strdup (tag_name);
1229 if (!info->create_tags)
1234 while (gtk_text_tag_table_lookup (info->buffer->tag_table, name) != NULL)
1237 name = g_strdup_printf ("%s-%d", tag_name, ++i);
1242 g_hash_table_insert (info->substitutions, g_strdup (tag_name), g_strdup (name));
1249 parse_tag_element (GMarkupParseContext *context,
1250 const gchar *element_name,
1251 const gchar **attribute_names,
1252 const gchar **attribute_values,
1256 const gchar *name, *priority;
1262 g_assert (peek_state (info) == STATE_TAGS);
1264 if (ELEMENT_IS ("tag"))
1266 if (!locate_attributes (context, element_name, attribute_names, attribute_values, TRUE, error,
1267 "priority", &priority, NULL))
1270 if (!check_id_or_name (context, element_name, attribute_names, attribute_values,
1276 if (g_hash_table_lookup (info->defined_tags, name) != NULL)
1278 set_error (error, context,
1279 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1280 _("Tag \"%s\" already defined"), name);
1285 prio = strtol (priority, &tmp, 10);
1287 if (tmp == NULL || tmp == priority)
1289 set_error (error, context,
1290 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1291 _("Tag \"%s\" has invalid priority \"%s\""), name, priority);
1297 tag_name = get_tag_name (info, name);
1298 info->current_tag = gtk_text_tag_new (tag_name);
1303 info->current_tag = gtk_text_tag_new (NULL);
1304 info->current_tag_id = id;
1307 info->current_tag_prio = prio;
1309 push_state (info, STATE_TAG);
1313 set_error (error, context,
1314 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1315 _("Element <%s> is not allowed below <%s>"),
1316 element_name, "tags");
1321 start_element_handler (GMarkupParseContext *context,
1322 const gchar *element_name,
1323 const gchar **attribute_names,
1324 const gchar **attribute_values,
1328 ParseInfo *info = user_data;
1330 switch (peek_state (info))
1333 if (ELEMENT_IS ("text_view_markup"))
1335 if (!check_no_attributes (context, element_name,
1336 attribute_names, attribute_values, error))
1339 push_state (info, STATE_TEXT_VIEW_MARKUP);
1343 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1344 _("Outermost element in text must be <text_view_markup> not <%s>"),
1347 case STATE_TEXT_VIEW_MARKUP:
1348 if (ELEMENT_IS ("tags"))
1350 if (info->parsed_tags)
1352 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1353 _("A <tags> element has already been specified"));
1357 if (!check_no_attributes (context, element_name,
1358 attribute_names, attribute_values, error))
1361 push_state (info, STATE_TAGS);
1364 else if (ELEMENT_IS ("text"))
1366 if (info->parsed_text)
1368 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1369 _("A <text> element has already been specified"));
1372 else if (!info->parsed_tags)
1374 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1375 _("A <text> element can't occur before a <tags> element"));
1379 if (!check_no_attributes (context, element_name,
1380 attribute_names, attribute_values, error))
1383 push_state (info, STATE_TEXT);
1387 set_error (error, context,
1388 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1389 _("Element <%s> is not allowed below <%s>"),
1390 element_name, "text_view_markup");
1393 parse_tag_element (context, element_name,
1394 attribute_names, attribute_values,
1398 parse_attr_element (context, element_name,
1399 attribute_names, attribute_values,
1403 case STATE_APPLY_TAG:
1404 parse_apply_tag_element (context, element_name,
1405 attribute_names, attribute_values,
1409 g_assert_not_reached ();
1415 sort_tag_prio (TextTagPrio *a,
1418 if (a->prio < b->prio)
1420 else if (a->prio > b->prio)
1427 end_element_handler (GMarkupParseContext *context,
1428 const gchar *element_name,
1432 ParseInfo *info = user_data;
1436 switch (peek_state (info))
1440 g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
1442 info->parsed_tags = TRUE;
1444 /* Sort list and add the tags */
1445 info->tag_priorities = g_list_sort (info->tag_priorities,
1446 (GCompareFunc)sort_tag_prio);
1447 list = info->tag_priorities;
1450 TextTagPrio *prio = list->data;
1452 if (info->create_tags)
1453 gtk_text_tag_table_add (info->buffer->tag_table, prio->tag);
1455 g_object_unref (prio->tag);
1464 g_assert (peek_state (info) == STATE_TAGS);
1466 if (info->current_tag->name)
1468 /* Add tag to defined tags hash */
1469 tmp = g_strdup (info->current_tag->name);
1470 g_hash_table_insert (info->defined_tags,
1475 g_hash_table_insert (info->anonymous_tags,
1476 GINT_TO_POINTER (info->current_tag_id),
1480 if (info->create_tags)
1484 /* add the tag to the list */
1485 prio = g_new0 (TextTagPrio, 1);
1486 prio->prio = info->current_tag_prio;
1487 prio->tag = info->current_tag;
1489 info->tag_priorities = g_list_prepend (info->tag_priorities, prio);
1492 info->current_tag = NULL;
1496 g_assert (peek_state (info) == STATE_TAG);
1498 case STATE_APPLY_TAG:
1500 g_assert (peek_state (info) == STATE_APPLY_TAG ||
1501 peek_state (info) == STATE_TEXT);
1504 info->tag_stack = g_slist_delete_link (info->tag_stack,
1510 g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
1512 info->spans = g_list_reverse (info->spans);
1513 info->parsed_text = TRUE;
1515 case STATE_TEXT_VIEW_MARKUP:
1517 g_assert (peek_state (info) == STATE_START);
1521 g_assert (peek_state (info) == STATE_APPLY_TAG ||
1522 peek_state (info) == STATE_TEXT);
1525 g_assert_not_reached ();
1531 all_whitespace (const char *text,
1538 end = text + text_len;
1542 if (!g_ascii_isspace (*p))
1545 p = g_utf8_next_char (p);
1552 text_handler (GMarkupParseContext *context,
1558 ParseInfo *info = user_data;
1561 if (all_whitespace (text, text_len) &&
1562 peek_state (info) != STATE_TEXT &&
1563 peek_state (info) != STATE_APPLY_TAG)
1566 switch (peek_state (info))
1569 g_assert_not_reached (); /* gmarkup shouldn't do this */
1572 case STATE_APPLY_TAG:
1576 span = g_new0 (TextSpan, 1);
1577 span->text = g_strndup (text, text_len);
1578 span->tags = g_slist_copy (info->tag_stack);
1580 info->spans = g_list_prepend (info->spans, span);
1583 g_assert_not_reached ();
1589 parse_info_init (ParseInfo *info,
1590 GtkTextBuffer *buffer,
1591 gboolean create_tags,
1594 info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
1596 info->create_tags = create_tags;
1597 info->headers = headers;
1598 info->defined_tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1599 info->substitutions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1600 info->anonymous_tags = g_hash_table_new_full (NULL, NULL, NULL, NULL);
1601 info->tag_stack = NULL;
1603 info->parsed_text = FALSE;
1604 info->parsed_tags = FALSE;
1605 info->current_tag = NULL;
1606 info->current_tag_prio = -1;
1607 info->tag_priorities = NULL;
1609 info->buffer = buffer;
1613 text_span_free (TextSpan *span)
1615 g_free (span->text);
1616 g_slist_free (span->tags);
1621 parse_info_free (ParseInfo *info)
1626 slist = info->tag_stack;
1629 g_free (slist->data);
1631 slist = slist->next;
1634 g_slist_free (info->tag_stack);
1635 g_slist_free (info->states);
1637 g_hash_table_destroy (info->substitutions);
1638 g_hash_table_destroy (info->defined_tags);
1640 if (info->current_tag)
1641 g_object_unref (info->current_tag);
1646 text_span_free (list->data);
1650 g_list_free (info->spans);
1652 list = info->tag_priorities;
1655 TextTagPrio *prio = list->data;
1658 g_object_unref (prio->tag);
1663 g_list_free (info->tag_priorities);
1668 insert_text (ParseInfo *info,
1671 GtkTextIter start_iter;
1678 mark = gtk_text_buffer_create_mark (info->buffer, "deserialize_insert_point",
1684 TextSpan *span = tmp->data;
1687 gtk_text_buffer_insert (info->buffer, iter, span->text, -1);
1690 gtk_text_buffer_insert_pixbuf (info->buffer, iter, span->pixbuf);
1691 g_object_unref (span->pixbuf);
1693 gtk_text_buffer_get_iter_at_mark (info->buffer, &start_iter, mark);
1699 GtkTextTag *tag = tags->data;
1701 gtk_text_buffer_apply_tag (info->buffer, tag,
1707 gtk_text_buffer_move_mark (info->buffer, mark, iter);
1712 gtk_text_buffer_delete_mark (info->buffer, mark);
1718 read_int (const guchar *start)
1732 header_is (Header *header,
1735 return (strncmp (header->id, id, strlen (id)) == 0);
1739 read_headers (const gchar *start,
1746 GList *headers = NULL;
1753 if (strncmp (start + i, "GTKTEXTBUFFERCONTENTS-0001", 26) == 0 ||
1754 strncmp (start + i, "GTKTEXTBUFFERPIXBDATA-0001", 26) == 0)
1756 section_len = read_int ((const guchar *) start + i + 26);
1758 if (i + 30 + section_len > len)
1761 header = g_new0 (Header, 1);
1762 header->id = start + i;
1763 header->length = section_len;
1764 header->start = start + i + 30;
1766 i += 30 + section_len;
1768 headers = g_list_prepend (headers, header);
1774 return g_list_reverse (headers);
1777 g_list_foreach (headers, (GFunc) g_free, NULL);
1778 g_list_free (headers);
1782 G_MARKUP_ERROR_PARSE,
1783 _("Serialized data is malformed"));
1789 deserialize_text (GtkTextBuffer *buffer,
1793 gboolean create_tags,
1797 GMarkupParseContext *context;
1799 gboolean retval = FALSE;
1801 static const GMarkupParser rich_text_parser = {
1802 start_element_handler,
1803 end_element_handler,
1809 parse_info_init (&info, buffer, create_tags, headers);
1811 context = g_markup_parse_context_new (&rich_text_parser,
1814 if (!g_markup_parse_context_parse (context,
1820 if (!g_markup_parse_context_end_parse (context, error))
1825 /* Now insert the text */
1826 insert_text (&info, iter);
1829 parse_info_free (&info);
1831 g_markup_parse_context_free (context);
1837 _gtk_text_buffer_deserialize_rich_text (GtkTextBuffer *register_buffer,
1838 GtkTextBuffer *content_buffer,
1842 gboolean create_tags,
1850 headers = read_headers ((gchar *) text, length, error);
1855 header = headers->data;
1856 if (!header_is (header, "GTKTEXTBUFFERCONTENTS-0001"))
1860 G_MARKUP_ERROR_PARSE,
1861 _("Serialized data is malformed. First section isn't GTKTEXTBUFFERCONTENTS-0001"));
1867 retval = deserialize_text (content_buffer, iter,
1868 header->start, header->length,
1869 create_tags, error, headers->next);
1872 g_list_foreach (headers, (GFunc)g_free, NULL);
1873 g_list_free (headers);