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, see <http://www.gnu.org/licenses/>.
20 /* FIXME: We should use other error codes for the
21 * parts that deal with the format errors
31 #include "gdk-pixbuf/gdk-pixdata.h"
32 #include "gtktextbufferserialize.h"
33 #include "gtktexttagprivate.h"
39 GString *tag_table_str;
42 GtkTextIter start, end;
47 GHashTable *tag_id_tags;
48 } SerializationContext;
51 serialize_value (GValue *value)
53 if (g_value_type_transformable (value->g_type, G_TYPE_STRING))
55 GValue text_value = G_VALUE_INIT;
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,
84 if (g_value_type_transformable (G_TYPE_STRING, value->g_type))
86 GValue text_value = G_VALUE_INIT;
89 g_value_init (&text_value, G_TYPE_STRING);
90 g_value_set_static_string (&text_value, str);
92 retval = g_value_transform (&text_value, value);
93 g_value_unset (&text_value);
97 else if (value->g_type == G_TYPE_BOOLEAN)
101 v = strcmp (str, "TRUE") == 0;
103 g_value_set_boolean (value, v);
107 else if (value->g_type == G_TYPE_INT)
113 v = g_ascii_strtoll (str, &tmp, 10);
115 if (errno || tmp == NULL || tmp == str)
118 g_value_set_int (value, v);
122 else if (value->g_type == G_TYPE_DOUBLE)
127 v = g_ascii_strtod (str, &tmp);
129 if (tmp == NULL || tmp == str)
132 g_value_set_double (value, v);
136 else if (value->g_type == GDK_TYPE_COLOR)
145 color.red = g_ascii_strtoll (old, &tmp, 16);
147 if (errno || tmp == old)
156 color.green = g_ascii_strtoll (old, &tmp, 16);
157 if (errno || tmp == old)
166 color.blue = g_ascii_strtoll (old, &tmp, 16);
168 if (errno || tmp == old || *tmp != '\0')
171 g_value_set_boxed (value, &color);
175 else if (G_VALUE_HOLDS_ENUM (value))
177 GEnumClass *class = G_ENUM_CLASS (g_type_class_peek (value->g_type));
178 GEnumValue *enum_value;
180 enum_value = g_enum_get_value_by_name (class, str);
184 g_value_set_enum (value, enum_value->value);
192 g_warning ("Type %s can not be deserialized\n", g_type_name (value->g_type));
198 /* Checks if a param is set, or if it's the default value */
200 is_param_set (GObject *object,
204 /* We need to special case some attributes here */
205 if (strcmp (pspec->name, "background-gdk") == 0)
209 g_object_get (object, "background-set", &is_set, NULL);
213 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
215 g_object_get_property (object, pspec->name, value);
222 else if (strcmp (pspec->name, "foreground-gdk") == 0)
226 g_object_get (object, "foreground-set", &is_set, NULL);
230 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
232 g_object_get_property (object, pspec->name, value);
244 is_set_name = g_strdup_printf ("%s-set", pspec->name);
246 if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), is_set_name) == NULL)
248 g_free (is_set_name);
253 g_object_get (object, is_set_name, &is_set, NULL);
257 g_free (is_set_name);
261 g_free (is_set_name);
263 g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
265 g_object_get_property (object, pspec->name, value);
267 if (g_param_value_defaults (pspec, value))
269 g_value_unset (value);
279 serialize_tag (gpointer key,
283 SerializationContext *context = user_data;
284 GtkTextTag *tag = data;
291 g_string_append (context->tag_table_str, " <tag ");
293 /* Handle anonymous tags */
296 tag_name = g_markup_escape_text (tag->priv->name, -1);
297 g_string_append_printf (context->tag_table_str, "name=\"%s\"", tag_name);
302 tag_id = GPOINTER_TO_INT (g_hash_table_lookup (context->tag_id_tags, tag));
304 g_string_append_printf (context->tag_table_str, "id=\"%d\"", tag_id);
307 g_string_append_printf (context->tag_table_str, " priority=\"%d\">\n", tag->priv->priority);
309 /* Serialize properties */
310 pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (tag), &n_pspecs);
312 for (i = 0; i < n_pspecs; i++)
314 GValue value = G_VALUE_INIT;
317 if (!(pspecs[i]->flags & G_PARAM_READABLE) ||
318 !(pspecs[i]->flags & G_PARAM_WRITABLE))
321 if (!is_param_set (G_OBJECT (tag), pspecs[i], &value))
324 /* Now serialize the attr */
325 tmp2 = serialize_value (&value);
329 tmp = g_markup_escape_text (pspecs[i]->name, -1);
330 g_string_append_printf (context->tag_table_str, " <attr name=\"%s\" ", tmp);
333 tmp = g_markup_escape_text (g_type_name (pspecs[i]->value_type), -1);
334 g_string_append_printf (context->tag_table_str, "type=\"%s\" value=\"%s\" />\n", tmp, tmp2);
340 g_value_unset (&value);
345 g_string_append (context->tag_table_str, " </tag>\n");
349 serialize_tags (SerializationContext *context)
351 g_string_append (context->tag_table_str, " <text_view_markup>\n");
352 g_string_append (context->tag_table_str, " <tags>\n");
353 g_hash_table_foreach (context->tags, serialize_tag, context);
354 g_string_append (context->tag_table_str, " </tags>\n");
359 dump_tag_list (const gchar *str,
362 g_print ("%s: ", str);
370 g_print ("%s ", ((GtkTextTag *)list->data)->name);
380 find_list_delta (GSList *old_list,
386 GList *tmp_added, *tmp_removed;
391 /* Find added tags */
395 if (!g_slist_find (old_list, tmp->data))
396 tmp_added = g_list_prepend (tmp_added, tmp->data);
403 /* Find removed tags */
407 if (!g_slist_find (new_list, tmp->data))
408 tmp_removed = g_list_prepend (tmp_removed, tmp->data);
413 /* We reverse the list here to match the xml semantics */
414 *removed = g_list_reverse (tmp_removed);
418 serialize_section_header (GString *str,
422 g_return_if_fail (strlen (name) == 26);
424 g_string_append (str, name);
426 g_string_append_c (str, length >> 24);
428 g_string_append_c (str, (length >> 16) & 0xff);
429 g_string_append_c (str, (length >> 8) & 0xff);
430 g_string_append_c (str, length & 0xff);
434 serialize_text (GtkTextBuffer *buffer,
435 SerializationContext *context)
437 GtkTextIter iter, old_iter;
438 GSList *tag_list, *new_tag_list;
441 g_string_append (context->text_str, "<text>");
443 iter = context->start;
449 GList *added, *removed;
451 gchar *tmp_text, *escaped_text;
453 new_tag_list = gtk_text_iter_get_tags (&iter);
454 find_list_delta (tag_list, new_tag_list, &added, &removed);
456 /* Handle removed tags */
457 for (tmp = removed; tmp; tmp = tmp->next)
459 GtkTextTag *tag = tmp->data;
461 /* Only close the tag if we didn't close it before (by using
462 * the stack logic in the while() loop below)
464 if (g_slist_find (active_tags, tag))
466 g_string_append (context->text_str, "</apply_tag>");
468 /* Drop all tags that were opened after this one (which are
469 * above this on in the stack)
471 while (active_tags->data != tag)
473 added = g_list_prepend (added, active_tags->data);
474 active_tags = g_slist_remove (active_tags, active_tags->data);
475 g_string_append_printf (context->text_str, "</apply_tag>");
478 active_tags = g_slist_remove (active_tags, active_tags->data);
482 /* Handle added tags */
483 for (tmp = added; tmp; tmp = tmp->next)
485 GtkTextTag *tag = tmp->data;
488 /* Add it to the tag hash table */
489 g_hash_table_insert (context->tags, tag, tag);
493 tag_name = g_markup_escape_text (tag->priv->name, -1);
495 g_string_append_printf (context->text_str, "<apply_tag name=\"%s\">", tag_name);
502 /* We've got an anonymous tag, find out if it's been
504 if (!g_hash_table_lookup_extended (context->tag_id_tags, tag, NULL, &tag_id))
506 tag_id = GINT_TO_POINTER (context->tag_id++);
508 g_hash_table_insert (context->tag_id_tags, tag, tag_id);
511 g_string_append_printf (context->text_str, "<apply_tag id=\"%d\">", GPOINTER_TO_INT (tag_id));
514 active_tags = g_slist_prepend (active_tags, tag);
517 g_slist_free (tag_list);
518 tag_list = new_tag_list;
521 g_list_free (removed);
525 /* Now try to go to either the next tag toggle, or if a pixbuf appears */
528 gunichar ch = gtk_text_iter_get_char (&iter);
532 GdkPixbuf *pixbuf = gtk_text_iter_get_pixbuf (&iter);
536 /* Append the text before the pixbuf */
537 tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
538 escaped_text = g_markup_escape_text (tmp_text, -1);
541 /* Forward so we don't get the 0xfffc char */
542 gtk_text_iter_forward_char (&iter);
545 g_string_append (context->text_str, escaped_text);
546 g_free (escaped_text);
548 g_string_append_printf (context->text_str, "<pixbuf index=\"%d\" />", context->n_pixbufs);
550 context->n_pixbufs++;
551 context->pixbufs = g_list_prepend (context->pixbufs, pixbuf);
559 gtk_text_iter_forward_char (&iter);
561 if (gtk_text_iter_toggles_tag (&iter, NULL))
565 /* We might have moved too far */
566 if (gtk_text_iter_compare (&iter, &context->end) > 0)
569 /* Append the text */
570 tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
571 escaped_text = g_markup_escape_text (tmp_text, -1);
574 g_string_append (context->text_str, escaped_text);
575 g_free (escaped_text);
577 while (!gtk_text_iter_equal (&iter, &context->end));
579 /* Close any open tags */
580 for (tag_list = active_tags; tag_list; tag_list = tag_list->next)
581 g_string_append (context->text_str, "</apply_tag>");
583 g_slist_free (active_tags);
584 g_string_append (context->text_str, "</text>\n</text_view_markup>\n");
588 serialize_pixbufs (SerializationContext *context,
593 for (list = context->pixbufs; list != NULL; list = list->next)
595 GdkPixbuf *pixbuf = list->data;
600 gdk_pixdata_from_pixbuf (&pixdata, pixbuf, FALSE);
601 tmp = gdk_pixdata_serialize (&pixdata, &len);
603 serialize_section_header (text, "GTKTEXTBUFFERPIXBDATA-0001", len);
604 g_string_append_len (text, (gchar *) tmp, len);
610 _gtk_text_buffer_serialize_rich_text (GtkTextBuffer *register_buffer,
611 GtkTextBuffer *content_buffer,
612 const GtkTextIter *start,
613 const GtkTextIter *end,
617 SerializationContext context;
620 context.tags = g_hash_table_new (NULL, NULL);
621 context.text_str = g_string_new (NULL);
622 context.tag_table_str = g_string_new (NULL);
623 context.start = *start;
625 context.n_pixbufs = 0;
626 context.pixbufs = NULL;
628 context.tag_id_tags = g_hash_table_new (NULL, NULL);
630 /* We need to serialize the text before the tag table so we know
631 what tags are used */
632 serialize_text (content_buffer, &context);
633 serialize_tags (&context);
635 text = g_string_new (NULL);
636 serialize_section_header (text, "GTKTEXTBUFFERCONTENTS-0001",
637 context.tag_table_str->len + context.text_str->len);
639 g_string_append_len (text, context.tag_table_str->str, context.tag_table_str->len);
640 g_string_append_len (text, context.text_str->str, context.text_str->len);
642 context.pixbufs = g_list_reverse (context.pixbufs);
643 serialize_pixbufs (&context, text);
645 g_hash_table_destroy (context.tags);
646 g_list_free (context.pixbufs);
647 g_string_free (context.text_str, TRUE);
648 g_string_free (context.tag_table_str, TRUE);
649 g_hash_table_destroy (context.tag_id_tags);
653 return (guint8 *) g_string_free (text, FALSE);
659 STATE_TEXT_VIEW_MARKUP,
687 GtkTextBuffer *buffer;
689 /* Tags that are defined in <tag> elements */
690 GHashTable *defined_tags;
692 /* Tags that are anonymous */
693 GHashTable *anonymous_tags;
695 /* Tag name substitutions */
696 GHashTable *substitutions;
699 GtkTextTag *current_tag;
701 /* Priority of current tag */
702 gint current_tag_prio;
704 /* Id of current tag */
707 /* Tags and their priorities */
708 GList *tag_priorities;
714 gboolean create_tags;
716 gboolean parsed_text;
717 gboolean parsed_tags;
721 set_error (GError **err,
722 GMarkupParseContext *context,
732 g_markup_parse_context_get_position (context, &line, &ch);
734 va_start (args, format);
735 str = g_strdup_vprintf (format, args);
738 g_set_error (err, error_domain, error_code,
739 ("Line %d character %d: %s"),
746 push_state (ParseInfo *info,
749 info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
753 pop_state (ParseInfo *info)
755 g_return_if_fail (info->states != NULL);
757 info->states = g_slist_remove (info->states, info->states->data);
761 peek_state (ParseInfo *info)
763 g_return_val_if_fail (info->states != NULL, STATE_START);
765 return GPOINTER_TO_INT (info->states->data);
768 #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
772 check_id_or_name (GMarkupParseContext *context,
773 const gchar *element_name,
774 const gchar **attribute_names,
775 const gchar **attribute_values,
780 gboolean has_id = FALSE;
781 gboolean has_name = FALSE;
787 for (i = 0; attribute_names[i] != NULL; i++)
789 if (strcmp (attribute_names[i], "name") == 0)
791 *name = attribute_values[i];
795 set_error (error, context,
797 G_MARKUP_ERROR_PARSE,
798 _("Both \"id\" and \"name\" were found on the <%s> element"),
805 set_error (error, context,
807 G_MARKUP_ERROR_PARSE,
808 _("The attribute \"%s\" was found twice on the <%s> element"),
809 "name", element_name);
815 else if (strcmp (attribute_names[i], "id") == 0)
821 set_error (error, context,
823 G_MARKUP_ERROR_PARSE,
824 _("Both \"id\" and \"name\" were found on the <%s> element"),
831 set_error (error, context,
833 G_MARKUP_ERROR_PARSE,
834 _("The attribute \"%s\" was found twice on the <%s> element"),
841 /* Try parsing the integer */
844 *id = g_ascii_strtoll (attribute_values[i], &tmp, 10);
846 if (errno || tmp == attribute_values[i])
848 set_error (error, context,
849 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
850 _("<%s> element has invalid ID \"%s\""), attribute_values[i]);
856 if (!has_id && !has_name)
858 set_error (error, context,
859 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
860 _("<%s> element has neither a \"name\" nor an \"id\" attribute"), element_name);
874 locate_attributes (GMarkupParseContext *context,
875 const char *element_name,
876 const char **attribute_names,
877 const char **attribute_values,
878 gboolean allow_unknown_attrs,
880 const char *first_attribute_name,
881 const char **first_attribute_retloc,
889 LocateAttr attrs[MAX_ATTRS];
893 g_return_val_if_fail (first_attribute_name != NULL, FALSE);
894 g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
899 attrs[0].name = first_attribute_name;
900 attrs[0].retloc = first_attribute_retloc;
901 *first_attribute_retloc = NULL;
903 va_start (args, first_attribute_retloc);
905 name = va_arg (args, const char*);
906 retloc = va_arg (args, const char**);
910 g_return_val_if_fail (retloc != NULL, FALSE);
912 g_assert (n_attrs < MAX_ATTRS);
914 attrs[n_attrs].name = name;
915 attrs[n_attrs].retloc = retloc;
919 name = va_arg (args, const char*);
920 retloc = va_arg (args, const char**);
929 while (attribute_names[i])
938 if (strcmp (attrs[j].name, attribute_names[i]) == 0)
940 retloc = attrs[j].retloc;
944 set_error (error, context,
946 G_MARKUP_ERROR_PARSE,
947 _("Attribute \"%s\" repeated twice on the same <%s> element"),
948 attrs[j].name, element_name);
953 *retloc = attribute_values[i];
960 if (!found && !allow_unknown_attrs)
962 set_error (error, context,
964 G_MARKUP_ERROR_PARSE,
965 _("Attribute \"%s\" is invalid on <%s> element in this context"),
966 attribute_names[i], element_name);
979 check_no_attributes (GMarkupParseContext *context,
980 const char *element_name,
981 const char **attribute_names,
982 const char **attribute_values,
985 if (attribute_names[0] != NULL)
987 set_error (error, context,
989 G_MARKUP_ERROR_PARSE,
990 _("Attribute \"%s\" is invalid on <%s> element in this context"),
991 attribute_names[0], element_name);
999 tag_exists (GMarkupParseContext *context,
1005 GtkTextTagTable *tag_table;
1006 const gchar *real_name;
1008 tag_table = gtk_text_buffer_get_tag_table (info->buffer);
1010 if (info->create_tags)
1012 /* If we have an anonymous tag, just return it directly */
1014 return g_hash_table_lookup (info->anonymous_tags,
1015 GINT_TO_POINTER (id));
1017 /* First, try the substitutions */
1018 real_name = g_hash_table_lookup (info->substitutions, name);
1021 return gtk_text_tag_table_lookup (tag_table, real_name);
1023 /* Next, try the list of defined tags */
1024 if (g_hash_table_lookup (info->defined_tags, name) != NULL)
1025 return gtk_text_tag_table_lookup (tag_table, name);
1027 set_error (error, context,
1028 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1029 _("Tag \"%s\" has not been defined."), name);
1039 set_error (error, context,
1040 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1041 _("Anonymous tag found and tags can not be created."));
1045 tag = gtk_text_tag_table_lookup (tag_table, name);
1050 set_error (error, context,
1051 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1052 _("Tag \"%s\" does not exist in buffer and tags can not be created."), name);
1066 get_pixbuf_from_headers (GList *headers,
1074 header = g_list_nth_data (headers, id);
1079 if (!gdk_pixdata_deserialize (&pixdata, header->length,
1080 (const guint8 *) header->start, error))
1083 pixbuf = gdk_pixbuf_from_pixdata (&pixdata, TRUE, error);
1089 parse_apply_tag_element (GMarkupParseContext *context,
1090 const gchar *element_name,
1091 const gchar **attribute_names,
1092 const gchar **attribute_values,
1096 const gchar *name, *priority;
1100 g_assert (peek_state (info) == STATE_TEXT ||
1101 peek_state (info) == STATE_APPLY_TAG);
1103 if (ELEMENT_IS ("apply_tag"))
1105 if (!locate_attributes (context, element_name, attribute_names, attribute_values, TRUE, error,
1106 "priority", &priority, NULL))
1109 if (!check_id_or_name (context, element_name, attribute_names, attribute_values,
1114 tag = tag_exists (context, name, id, info, error);
1119 info->tag_stack = g_slist_prepend (info->tag_stack, tag);
1121 push_state (info, STATE_APPLY_TAG);
1123 else if (ELEMENT_IS ("pixbuf"))
1128 const gchar *pixbuf_id;
1130 if (!locate_attributes (context, element_name, attribute_names, attribute_values, FALSE, error,
1131 "index", &pixbuf_id, NULL))
1134 int_id = atoi (pixbuf_id);
1135 pixbuf = get_pixbuf_from_headers (info->headers, int_id, error);
1137 span = g_new0 (TextSpan, 1);
1138 span->pixbuf = pixbuf;
1141 info->spans = g_list_prepend (info->spans, span);
1146 push_state (info, STATE_PIXBUF);
1149 set_error (error, context,
1150 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1151 _("Element <%s> is not allowed below <%s>"),
1152 element_name, peek_state(info) == STATE_TEXT ? "text" : "apply_tag");
1156 parse_attr_element (GMarkupParseContext *context,
1157 const gchar *element_name,
1158 const gchar **attribute_names,
1159 const gchar **attribute_values,
1163 const gchar *name, *type, *value;
1165 GValue gvalue = G_VALUE_INIT;
1168 g_assert (peek_state (info) == STATE_TAG);
1170 if (ELEMENT_IS ("attr"))
1172 if (!locate_attributes (context, element_name, attribute_names, attribute_values, FALSE, error,
1173 "name", &name, "type", &type, "value", &value, NULL))
1176 gtype = g_type_from_name (type);
1178 if (gtype == G_TYPE_INVALID)
1180 set_error (error, context,
1181 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1182 _("\"%s\" is not a valid attribute type"), type);
1186 if (!(pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info->current_tag), name)))
1188 set_error (error, context,
1189 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1190 _("\"%s\" is not a valid attribute name"), name);
1194 g_value_init (&gvalue, gtype);
1196 if (!deserialize_value (value, &gvalue))
1198 set_error (error, context,
1199 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1200 _("\"%s\" could not be converted to a value of type \"%s\" for attribute \"%s\""),
1205 if (g_param_value_validate (pspec, &gvalue))
1207 set_error (error, context,
1208 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1209 _("\"%s\" is not a valid value for attribute \"%s\""),
1211 g_value_unset (&gvalue);
1215 g_object_set_property (G_OBJECT (info->current_tag),
1218 g_value_unset (&gvalue);
1220 push_state (info, STATE_ATTR);
1224 set_error (error, context,
1225 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1226 _("Element <%s> is not allowed below <%s>"),
1227 element_name, "tag");
1233 get_tag_name (ParseInfo *info,
1234 const gchar *tag_name)
1236 GtkTextTagTable *tag_table;
1240 name = g_strdup (tag_name);
1242 if (!info->create_tags)
1246 tag_table = gtk_text_buffer_get_tag_table (info->buffer);
1248 while (gtk_text_tag_table_lookup (tag_table, name) != NULL)
1251 name = g_strdup_printf ("%s-%d", tag_name, ++i);
1256 g_hash_table_insert (info->substitutions, g_strdup (tag_name), g_strdup (name));
1263 parse_tag_element (GMarkupParseContext *context,
1264 const gchar *element_name,
1265 const gchar **attribute_names,
1266 const gchar **attribute_values,
1270 const gchar *name, *priority;
1276 g_assert (peek_state (info) == STATE_TAGS);
1278 if (ELEMENT_IS ("tag"))
1280 if (!locate_attributes (context, element_name, attribute_names, attribute_values, TRUE, error,
1281 "priority", &priority, NULL))
1284 if (!check_id_or_name (context, element_name, attribute_names, attribute_values,
1290 if (g_hash_table_lookup (info->defined_tags, name) != NULL)
1292 set_error (error, context,
1293 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1294 _("Tag \"%s\" already defined"), name);
1301 prio = g_ascii_strtoll (priority, &tmp, 10);
1303 if (errno || tmp == priority)
1305 set_error (error, context,
1306 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1307 _("Tag \"%s\" has invalid priority \"%s\""), name, priority);
1313 tag_name = get_tag_name (info, name);
1314 info->current_tag = gtk_text_tag_new (tag_name);
1319 info->current_tag = gtk_text_tag_new (NULL);
1320 info->current_tag_id = id;
1323 info->current_tag_prio = prio;
1325 push_state (info, STATE_TAG);
1329 set_error (error, context,
1330 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1331 _("Element <%s> is not allowed below <%s>"),
1332 element_name, "tags");
1337 start_element_handler (GMarkupParseContext *context,
1338 const gchar *element_name,
1339 const gchar **attribute_names,
1340 const gchar **attribute_values,
1344 ParseInfo *info = user_data;
1346 switch (peek_state (info))
1349 if (ELEMENT_IS ("text_view_markup"))
1351 if (!check_no_attributes (context, element_name,
1352 attribute_names, attribute_values, error))
1355 push_state (info, STATE_TEXT_VIEW_MARKUP);
1359 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1360 _("Outermost element in text must be <text_view_markup> not <%s>"),
1363 case STATE_TEXT_VIEW_MARKUP:
1364 if (ELEMENT_IS ("tags"))
1366 if (info->parsed_tags)
1368 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1369 _("A <%s> element has already been specified"), "tags");
1373 if (!check_no_attributes (context, element_name,
1374 attribute_names, attribute_values, error))
1377 push_state (info, STATE_TAGS);
1380 else if (ELEMENT_IS ("text"))
1382 if (info->parsed_text)
1384 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1385 _("A <%s> element has already been specified"), "text");
1388 else if (!info->parsed_tags)
1390 set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1391 _("A <text> element can't occur before a <tags> element"));
1395 if (!check_no_attributes (context, element_name,
1396 attribute_names, attribute_values, error))
1399 push_state (info, STATE_TEXT);
1403 set_error (error, context,
1404 G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1405 _("Element <%s> is not allowed below <%s>"),
1406 element_name, "text_view_markup");
1409 parse_tag_element (context, element_name,
1410 attribute_names, attribute_values,
1414 parse_attr_element (context, element_name,
1415 attribute_names, attribute_values,
1419 case STATE_APPLY_TAG:
1420 parse_apply_tag_element (context, element_name,
1421 attribute_names, attribute_values,
1425 g_assert_not_reached ();
1431 sort_tag_prio (TextTagPrio *a,
1434 if (a->prio < b->prio)
1436 else if (a->prio > b->prio)
1443 end_element_handler (GMarkupParseContext *context,
1444 const gchar *element_name,
1448 ParseInfo *info = user_data;
1452 switch (peek_state (info))
1456 g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
1458 info->parsed_tags = TRUE;
1460 /* Sort list and add the tags */
1461 info->tag_priorities = g_list_sort (info->tag_priorities,
1462 (GCompareFunc)sort_tag_prio);
1463 list = info->tag_priorities;
1466 TextTagPrio *prio = list->data;
1468 if (info->create_tags)
1469 gtk_text_tag_table_add (gtk_text_buffer_get_tag_table (info->buffer),
1472 g_object_unref (prio->tag);
1481 g_assert (peek_state (info) == STATE_TAGS);
1483 if (info->current_tag->priv->name)
1485 /* Add tag to defined tags hash */
1486 tmp = g_strdup (info->current_tag->priv->name);
1487 g_hash_table_insert (info->defined_tags,
1492 g_hash_table_insert (info->anonymous_tags,
1493 GINT_TO_POINTER (info->current_tag_id),
1497 if (info->create_tags)
1501 /* add the tag to the list */
1502 prio = g_new0 (TextTagPrio, 1);
1503 prio->prio = info->current_tag_prio;
1504 prio->tag = info->current_tag;
1506 info->tag_priorities = g_list_prepend (info->tag_priorities, prio);
1509 info->current_tag = NULL;
1513 g_assert (peek_state (info) == STATE_TAG);
1515 case STATE_APPLY_TAG:
1517 g_assert (peek_state (info) == STATE_APPLY_TAG ||
1518 peek_state (info) == STATE_TEXT);
1521 info->tag_stack = g_slist_delete_link (info->tag_stack,
1527 g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
1529 info->spans = g_list_reverse (info->spans);
1530 info->parsed_text = TRUE;
1532 case STATE_TEXT_VIEW_MARKUP:
1534 g_assert (peek_state (info) == STATE_START);
1538 g_assert (peek_state (info) == STATE_APPLY_TAG ||
1539 peek_state (info) == STATE_TEXT);
1542 g_assert_not_reached ();
1548 all_whitespace (const char *text,
1555 end = text + text_len;
1559 if (!g_ascii_isspace (*p))
1562 p = g_utf8_next_char (p);
1569 text_handler (GMarkupParseContext *context,
1575 ParseInfo *info = user_data;
1578 if (all_whitespace (text, text_len) &&
1579 peek_state (info) != STATE_TEXT &&
1580 peek_state (info) != STATE_APPLY_TAG)
1583 switch (peek_state (info))
1586 g_assert_not_reached (); /* gmarkup shouldn't do this */
1589 case STATE_APPLY_TAG:
1593 span = g_new0 (TextSpan, 1);
1594 span->text = g_strndup (text, text_len);
1595 span->tags = g_slist_copy (info->tag_stack);
1597 info->spans = g_list_prepend (info->spans, span);
1600 g_assert_not_reached ();
1606 parse_info_init (ParseInfo *info,
1607 GtkTextBuffer *buffer,
1608 gboolean create_tags,
1611 info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
1613 info->create_tags = create_tags;
1614 info->headers = headers;
1615 info->defined_tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1616 info->substitutions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1617 info->anonymous_tags = g_hash_table_new_full (NULL, NULL, NULL, NULL);
1618 info->tag_stack = NULL;
1620 info->parsed_text = FALSE;
1621 info->parsed_tags = FALSE;
1622 info->current_tag = NULL;
1623 info->current_tag_prio = -1;
1624 info->tag_priorities = NULL;
1626 info->buffer = buffer;
1630 text_span_free (TextSpan *span)
1632 g_free (span->text);
1633 g_slist_free (span->tags);
1638 parse_info_free (ParseInfo *info)
1642 g_slist_free (info->tag_stack);
1643 g_slist_free (info->states);
1645 g_hash_table_destroy (info->substitutions);
1646 g_hash_table_destroy (info->defined_tags);
1648 if (info->current_tag)
1649 g_object_unref (info->current_tag);
1654 text_span_free (list->data);
1658 g_list_free (info->spans);
1660 list = info->tag_priorities;
1663 TextTagPrio *prio = list->data;
1666 g_object_unref (prio->tag);
1671 g_list_free (info->tag_priorities);
1676 insert_text (ParseInfo *info,
1679 GtkTextIter start_iter;
1686 mark = gtk_text_buffer_create_mark (info->buffer, "deserialize_insert_point",
1692 TextSpan *span = tmp->data;
1695 gtk_text_buffer_insert (info->buffer, iter, span->text, -1);
1698 gtk_text_buffer_insert_pixbuf (info->buffer, iter, span->pixbuf);
1699 g_object_unref (span->pixbuf);
1701 gtk_text_buffer_get_iter_at_mark (info->buffer, &start_iter, mark);
1707 GtkTextTag *tag = tags->data;
1709 gtk_text_buffer_apply_tag (info->buffer, tag,
1715 gtk_text_buffer_move_mark (info->buffer, mark, iter);
1720 gtk_text_buffer_delete_mark (info->buffer, mark);
1726 read_int (const guchar *start)
1740 header_is (Header *header,
1743 return (strncmp (header->id, id, strlen (id)) == 0);
1747 read_headers (const gchar *start,
1754 GList *headers = NULL;
1761 if (strncmp (start + i, "GTKTEXTBUFFERCONTENTS-0001", 26) == 0 ||
1762 strncmp (start + i, "GTKTEXTBUFFERPIXBDATA-0001", 26) == 0)
1764 section_len = read_int ((const guchar *) start + i + 26);
1766 if (i + 30 + section_len > len)
1769 header = g_new0 (Header, 1);
1770 header->id = start + i;
1771 header->length = section_len;
1772 header->start = start + i + 30;
1774 i += 30 + section_len;
1776 headers = g_list_prepend (headers, header);
1782 return g_list_reverse (headers);
1785 g_list_free_full (headers, g_free);
1787 g_set_error_literal (error,
1789 G_MARKUP_ERROR_PARSE,
1790 _("Serialized data is malformed"));
1796 deserialize_text (GtkTextBuffer *buffer,
1800 gboolean create_tags,
1804 GMarkupParseContext *context;
1806 gboolean retval = FALSE;
1808 static const GMarkupParser rich_text_parser = {
1809 start_element_handler,
1810 end_element_handler,
1816 parse_info_init (&info, buffer, create_tags, headers);
1818 context = g_markup_parse_context_new (&rich_text_parser,
1821 if (!g_markup_parse_context_parse (context,
1827 if (!g_markup_parse_context_end_parse (context, error))
1832 /* Now insert the text */
1833 insert_text (&info, iter);
1836 parse_info_free (&info);
1838 g_markup_parse_context_free (context);
1844 _gtk_text_buffer_deserialize_rich_text (GtkTextBuffer *register_buffer,
1845 GtkTextBuffer *content_buffer,
1849 gboolean create_tags,
1857 headers = read_headers ((gchar *) text, length, error);
1862 header = headers->data;
1863 if (!header_is (header, "GTKTEXTBUFFERCONTENTS-0001"))
1865 g_set_error_literal (error,
1867 G_MARKUP_ERROR_PARSE,
1868 _("Serialized data is malformed. First section isn't GTKTEXTBUFFERCONTENTS-0001"));
1874 retval = deserialize_text (content_buffer, iter,
1875 header->start, header->length,
1876 create_tags, error, headers->next);
1879 g_list_free_full (headers, g_free);