]> Pileus Git - ~andy/gtk/blob - gtk/gtktextbufferserialize.c
gtktexttag: Move public members to private header
[~andy/gtk] / gtk / gtktextbufferserialize.c
1 /* gtktextbufferserialize.c
2  *
3  * Copyright (C) 2001 Havoc Pennington
4  * Copyright (C) 2004 Nokia Corporation
5  *
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.
10  *
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.
15  *
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.
20  */
21
22 /* FIXME: We should use other error codes for the
23  * parts that deal with the format errors
24  */
25
26 #include "config.h"
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <errno.h>
32
33 #include "gdk-pixbuf/gdk-pixdata.h"
34 #include "gtktextbufferserialize.h"
35 #include "gtktexttagprivate.h"
36 #include "gtkintl.h"
37
38
39 typedef struct
40 {
41   GString *tag_table_str;
42   GString *text_str;
43   GHashTable *tags;
44   GtkTextIter start, end;
45
46   gint n_pixbufs;
47   GList *pixbufs;
48   gint tag_id;
49   GHashTable *tag_id_tags;
50 } SerializationContext;
51
52 static gchar *
53 serialize_value (GValue *value)
54 {
55   if (g_value_type_transformable (value->g_type, G_TYPE_STRING))
56     {
57       GValue text_value = { 0 };
58       gchar *tmp;
59
60       g_value_init (&text_value, G_TYPE_STRING);
61       g_value_transform (value, &text_value);
62
63       tmp = g_markup_escape_text (g_value_get_string (&text_value), -1);
64       g_value_unset (&text_value);
65
66       return tmp;
67     }
68   else if (value->g_type == GDK_TYPE_COLOR)
69     {
70       GdkColor *color = g_value_get_boxed (value);
71
72       return g_strdup_printf ("%x:%x:%x", color->red, color->green, color->blue);
73     }
74   else
75     {
76       g_warning ("Type %s is not serializable\n", g_type_name (value->g_type));
77     }
78
79   return NULL;
80 }
81
82 static gboolean
83 deserialize_value (const gchar *str,
84                    GValue      *value)
85 {
86   if (g_value_type_transformable (G_TYPE_STRING, value->g_type))
87     {
88       GValue text_value = { 0 };
89       gboolean retval;
90
91       g_value_init (&text_value, G_TYPE_STRING);
92       g_value_set_static_string (&text_value, str);
93
94       retval = g_value_transform (&text_value, value);
95       g_value_unset (&text_value);
96
97       return retval;
98     }
99   else if (value->g_type == G_TYPE_BOOLEAN)
100     {
101       gboolean v;
102
103       v = strcmp (str, "TRUE") == 0;
104
105       g_value_set_boolean (value, v);
106
107       return TRUE;
108     }
109   else if (value->g_type == G_TYPE_INT)
110     {
111       gchar *tmp;
112       int v;
113
114       errno = 0;
115       v = g_ascii_strtoll (str, &tmp, 10);
116
117       if (errno || tmp == NULL || tmp == str)
118         return FALSE;
119
120       g_value_set_int (value, v);
121
122       return TRUE;
123     }
124   else if (value->g_type == G_TYPE_DOUBLE)
125     {
126       gchar *tmp;
127       gdouble v;
128
129       v = g_ascii_strtod (str, &tmp);
130
131       if (tmp == NULL || tmp == str)
132         return FALSE;
133
134       g_value_set_double (value, v);
135
136       return TRUE;
137     }
138   else if (value->g_type == GDK_TYPE_COLOR)
139     {
140       GdkColor color;
141       const gchar *old;
142       gchar *tmp;
143
144       old = str;
145       tmp = NULL;
146       errno = 0;
147       color.red = g_ascii_strtoll (old, &tmp, 16);
148
149       if (errno || tmp == old)
150         return FALSE;
151
152       old = tmp;
153       if (*old++ != ':')
154         return FALSE;
155
156       tmp = NULL;
157       errno = 0;
158       color.green = g_ascii_strtoll (old, &tmp, 16);
159       if (errno || tmp == old)
160         return FALSE;
161
162       old = tmp;
163       if (*old++ != ':')
164         return FALSE;
165
166       tmp = NULL;
167       errno = 0;
168       color.blue = g_ascii_strtoll (old, &tmp, 16);
169
170       if (errno || tmp == old || *tmp != '\0')
171         return FALSE;
172
173       g_value_set_boxed (value, &color);
174
175       return TRUE;
176     }
177   else if (G_VALUE_HOLDS_ENUM (value))
178     {
179       GEnumClass *class = G_ENUM_CLASS (g_type_class_peek (value->g_type));
180       GEnumValue *enum_value;
181
182       enum_value = g_enum_get_value_by_name (class, str);
183
184       if (enum_value)
185         {
186           g_value_set_enum (value, enum_value->value);
187           return TRUE;
188         }
189
190       return FALSE;
191     }
192   else
193     {
194       g_warning ("Type %s can not be deserialized\n", g_type_name (value->g_type));
195     }
196
197   return FALSE;
198 }
199
200 /* Checks if a param is set, or if it's the default value */
201 static gboolean
202 is_param_set (GObject    *object,
203               GParamSpec *pspec,
204               GValue     *value)
205 {
206   /* We need to special case some attributes here */
207   if (strcmp (pspec->name, "background-gdk") == 0)
208     {
209       gboolean is_set;
210
211       g_object_get (object, "background-set", &is_set, NULL);
212
213       if (is_set)
214         {
215           g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
216
217           g_object_get_property (object, pspec->name, value);
218
219           return TRUE;
220         }
221
222       return FALSE;
223     }
224   else if (strcmp (pspec->name, "foreground-gdk") == 0)
225     {
226       gboolean is_set;
227
228       g_object_get (object, "foreground-set", &is_set, NULL);
229
230       if (is_set)
231         {
232           g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
233
234           g_object_get_property (object, pspec->name, value);
235
236           return TRUE;
237         }
238
239       return FALSE;
240     }
241   else
242     {
243       gboolean is_set;
244       gchar *is_set_name;
245
246       is_set_name = g_strdup_printf ("%s-set", pspec->name);
247
248       if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), is_set_name) == NULL)
249         {
250           g_free (is_set_name);
251           return FALSE;
252         }
253       else
254         {
255           g_object_get (object, is_set_name, &is_set, NULL);
256
257           if (!is_set)
258             {
259               g_free (is_set_name);
260               return FALSE;
261             }
262
263           g_free (is_set_name);
264
265           g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
266
267           g_object_get_property (object, pspec->name, value);
268
269           if (g_param_value_defaults (pspec, value))
270             {
271               g_value_unset (value);
272
273               return FALSE;
274             }
275         }
276       return TRUE;
277     }
278 }
279
280 static void
281 serialize_tag (gpointer key,
282                gpointer data,
283                gpointer user_data)
284 {
285   SerializationContext *context = user_data;
286   GtkTextTag *tag = data;
287   gchar *tag_name;
288   gint tag_id;
289   GParamSpec **pspecs;
290   guint n_pspecs;
291   int i;
292
293   g_string_append (context->tag_table_str, "  <tag ");
294
295   /* Handle anonymous tags */
296   if (tag->priv->name)
297     {
298       tag_name = g_markup_escape_text (tag->priv->name, -1);
299       g_string_append_printf (context->tag_table_str, "name=\"%s\"", tag_name);
300       g_free (tag_name);
301     }
302   else
303     {
304       tag_id = GPOINTER_TO_INT (g_hash_table_lookup (context->tag_id_tags, tag));
305
306       g_string_append_printf (context->tag_table_str, "id=\"%d\"", tag_id);
307     }
308
309   g_string_append_printf (context->tag_table_str, " priority=\"%d\">\n", tag->priv->priority);
310
311   /* Serialize properties */
312   pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (tag), &n_pspecs);
313
314   for (i = 0; i < n_pspecs; i++)
315     {
316       GValue value = { 0 };
317       gchar *tmp, *tmp2;
318
319       if (!(pspecs[i]->flags & G_PARAM_READABLE) ||
320           !(pspecs[i]->flags & G_PARAM_WRITABLE))
321         continue;
322
323       if (!is_param_set (G_OBJECT (tag), pspecs[i], &value))
324         continue;
325
326       /* Now serialize the attr */
327       tmp2 = serialize_value (&value);
328
329       if (tmp2)
330         {
331           tmp = g_markup_escape_text (pspecs[i]->name, -1);
332           g_string_append_printf (context->tag_table_str, "   <attr name=\"%s\" ", tmp);
333           g_free (tmp);
334
335           tmp = g_markup_escape_text (g_type_name (pspecs[i]->value_type), -1);
336           g_string_append_printf (context->tag_table_str, "type=\"%s\" value=\"%s\" />\n", tmp, tmp2);
337
338           g_free (tmp);
339           g_free (tmp2);
340         }
341
342       g_value_unset (&value);
343     }
344
345   g_free (pspecs);
346
347   g_string_append (context->tag_table_str, "  </tag>\n");
348 }
349
350 static void
351 serialize_tags (SerializationContext *context)
352 {
353   g_string_append (context->tag_table_str, " <text_view_markup>\n");
354   g_string_append (context->tag_table_str, " <tags>\n");
355   g_hash_table_foreach (context->tags, serialize_tag, context);
356   g_string_append (context->tag_table_str, " </tags>\n");
357 }
358
359 #if 0
360 static void
361 dump_tag_list (const gchar *str,
362                GList       *list)
363 {
364   g_print ("%s: ", str);
365
366   if (!list)
367     g_print ("(empty)");
368   else
369     {
370       while (list)
371         {
372           g_print ("%s ", ((GtkTextTag *)list->data)->name);
373           list = list->next;
374         }
375     }
376
377   g_print ("\n");
378 }
379 #endif
380
381 static void
382 find_list_delta (GSList  *old_list,
383                  GSList  *new_list,
384                  GList  **added,
385                  GList  **removed)
386 {
387   GSList *tmp;
388   GList *tmp_added, *tmp_removed;
389
390   tmp_added = NULL;
391   tmp_removed = NULL;
392
393   /* Find added tags */
394   tmp = new_list;
395   while (tmp)
396     {
397       if (!g_slist_find (old_list, tmp->data))
398         tmp_added = g_list_prepend (tmp_added, tmp->data);
399
400       tmp = tmp->next;
401     }
402
403   *added = tmp_added;
404
405   /* Find removed tags */
406   tmp = old_list;
407   while (tmp)
408     {
409       if (!g_slist_find (new_list, tmp->data))
410         tmp_removed = g_list_prepend (tmp_removed, tmp->data);
411
412       tmp = tmp->next;
413     }
414
415   /* We reverse the list here to match the xml semantics */
416   *removed = g_list_reverse (tmp_removed);
417 }
418
419 static void
420 serialize_section_header (GString     *str,
421                           const gchar *name,
422                           gint         length)
423 {
424   g_return_if_fail (strlen (name) == 26);
425
426   g_string_append (str, name);
427
428   g_string_append_c (str, length >> 24);
429
430   g_string_append_c (str, (length >> 16) & 0xff);
431   g_string_append_c (str, (length >> 8) & 0xff);
432   g_string_append_c (str, length & 0xff);
433 }
434
435 static void
436 serialize_text (GtkTextBuffer        *buffer,
437                 SerializationContext *context)
438 {
439   GtkTextIter iter, old_iter;
440   GSList *tag_list, *new_tag_list;
441   GSList *active_tags;
442
443   g_string_append (context->text_str, "<text>");
444
445   iter = context->start;
446   tag_list = NULL;
447   active_tags = NULL;
448
449   do
450     {
451       GList *added, *removed;
452       GList *tmp;
453       gchar *tmp_text, *escaped_text;
454
455       new_tag_list = gtk_text_iter_get_tags (&iter);
456       find_list_delta (tag_list, new_tag_list, &added, &removed);
457
458       /* Handle removed tags */
459       for (tmp = removed; tmp; tmp = tmp->next)
460         {
461           GtkTextTag *tag = tmp->data;
462
463           /* Only close the tag if we didn't close it before (by using
464            * the stack logic in the while() loop below)
465            */
466           if (g_slist_find (active_tags, tag))
467             {
468               g_string_append (context->text_str, "</apply_tag>");
469
470               /* Drop all tags that were opened after this one (which are
471                * above this on in the stack)
472                */
473               while (active_tags->data != tag)
474                 {
475                   added = g_list_prepend (added, active_tags->data);
476                   active_tags = g_slist_remove (active_tags, active_tags->data);
477                   g_string_append_printf (context->text_str, "</apply_tag>");
478                 }
479
480               active_tags = g_slist_remove (active_tags, active_tags->data);
481             }
482         }
483
484       /* Handle added tags */
485       for (tmp = added; tmp; tmp = tmp->next)
486         {
487           GtkTextTag *tag = tmp->data;
488           gchar *tag_name;
489
490           /* Add it to the tag hash table */
491           g_hash_table_insert (context->tags, tag, tag);
492
493           if (tag->priv->name)
494             {
495               tag_name = g_markup_escape_text (tag->priv->name, -1);
496
497               g_string_append_printf (context->text_str, "<apply_tag name=\"%s\">", tag_name);
498               g_free (tag_name);
499             }
500           else
501             {
502               gpointer tag_id;
503
504               /* We've got an anonymous tag, find out if it's been
505                  used before */
506               if (!g_hash_table_lookup_extended (context->tag_id_tags, tag, NULL, &tag_id))
507                 {
508                   tag_id = GINT_TO_POINTER (context->tag_id++);
509
510                   g_hash_table_insert (context->tag_id_tags, tag, tag_id);
511                 }
512
513               g_string_append_printf (context->text_str, "<apply_tag id=\"%d\">", GPOINTER_TO_INT (tag_id));
514             }
515
516           active_tags = g_slist_prepend (active_tags, tag);
517         }
518
519       g_slist_free (tag_list);
520       tag_list = new_tag_list;
521
522       g_list_free (added);
523       g_list_free (removed);
524
525       old_iter = iter;
526
527       /* Now try to go to either the next tag toggle, or if a pixbuf appears */
528       while (TRUE)
529         {
530           gunichar ch = gtk_text_iter_get_char (&iter);
531
532           if (ch == 0xFFFC)
533             {
534               GdkPixbuf *pixbuf = gtk_text_iter_get_pixbuf (&iter);
535
536               if (pixbuf)
537                 {
538                   /* Append the text before the pixbuf */
539                   tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
540                   escaped_text = g_markup_escape_text (tmp_text, -1);
541                   g_free (tmp_text);
542
543                   /* Forward so we don't get the 0xfffc char */
544                   gtk_text_iter_forward_char (&iter);
545                   old_iter = iter;
546
547                   g_string_append (context->text_str, escaped_text);
548                   g_free (escaped_text);
549
550                   g_string_append_printf (context->text_str, "<pixbuf index=\"%d\" />", context->n_pixbufs);
551
552                   context->n_pixbufs++;
553                   context->pixbufs = g_list_prepend (context->pixbufs, pixbuf);
554                 }
555             }
556           else if (ch == 0)
557             {
558                 break;
559             }
560           else
561             gtk_text_iter_forward_char (&iter);
562
563           if (gtk_text_iter_toggles_tag (&iter, NULL))
564             break;
565         }
566
567       /* We might have moved too far */
568       if (gtk_text_iter_compare (&iter, &context->end) > 0)
569         iter = context->end;
570
571       /* Append the text */
572       tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
573       escaped_text = g_markup_escape_text (tmp_text, -1);
574       g_free (tmp_text);
575
576       g_string_append (context->text_str, escaped_text);
577       g_free (escaped_text);
578     }
579   while (!gtk_text_iter_equal (&iter, &context->end));
580
581   /* Close any open tags */
582   for (tag_list = active_tags; tag_list; tag_list = tag_list->next)
583     g_string_append (context->text_str, "</apply_tag>");
584
585   g_slist_free (active_tags);
586   g_string_append (context->text_str, "</text>\n</text_view_markup>\n");
587 }
588
589 static void
590 serialize_pixbufs (SerializationContext *context,
591                    GString              *text)
592 {
593   GList *list;
594
595   for (list = context->pixbufs; list != NULL; list = list->next)
596     {
597       GdkPixbuf *pixbuf = list->data;
598       GdkPixdata pixdata;
599       guint8 *tmp;
600       guint len;
601
602       gdk_pixdata_from_pixbuf (&pixdata, pixbuf, FALSE);
603       tmp = gdk_pixdata_serialize (&pixdata, &len);
604
605       serialize_section_header (text, "GTKTEXTBUFFERPIXBDATA-0001", len);
606       g_string_append_len (text, (gchar *) tmp, len);
607       g_free (tmp);
608     }
609 }
610
611 guint8 *
612 _gtk_text_buffer_serialize_rich_text (GtkTextBuffer     *register_buffer,
613                                       GtkTextBuffer     *content_buffer,
614                                       const GtkTextIter *start,
615                                       const GtkTextIter *end,
616                                       gsize             *length,
617                                       gpointer           user_data)
618 {
619   SerializationContext context;
620   GString *text;
621
622   context.tags = g_hash_table_new (NULL, NULL);
623   context.text_str = g_string_new (NULL);
624   context.tag_table_str = g_string_new (NULL);
625   context.start = *start;
626   context.end = *end;
627   context.n_pixbufs = 0;
628   context.pixbufs = NULL;
629   context.tag_id = 0;
630   context.tag_id_tags = g_hash_table_new (NULL, NULL);
631
632   /* We need to serialize the text before the tag table so we know
633      what tags are used */
634   serialize_text (content_buffer, &context);
635   serialize_tags (&context);
636
637   text = g_string_new (NULL);
638   serialize_section_header (text, "GTKTEXTBUFFERCONTENTS-0001",
639                             context.tag_table_str->len + context.text_str->len);
640
641   g_string_append_len (text, context.tag_table_str->str, context.tag_table_str->len);
642   g_string_append_len (text, context.text_str->str, context.text_str->len);
643
644   context.pixbufs = g_list_reverse (context.pixbufs);
645   serialize_pixbufs (&context, text);
646
647   g_hash_table_destroy (context.tags);
648   g_list_free (context.pixbufs);
649   g_string_free (context.text_str, TRUE);
650   g_string_free (context.tag_table_str, TRUE);
651   g_hash_table_destroy (context.tag_id_tags);
652
653   *length = text->len;
654
655   return (guint8 *) g_string_free (text, FALSE);
656 }
657
658 typedef enum
659 {
660   STATE_START,
661   STATE_TEXT_VIEW_MARKUP,
662   STATE_TAGS,
663   STATE_TAG,
664   STATE_ATTR,
665   STATE_TEXT,
666   STATE_APPLY_TAG,
667   STATE_PIXBUF
668 } ParseState;
669
670 typedef struct
671 {
672   gchar *text;
673   GdkPixbuf *pixbuf;
674   GSList *tags;
675 } TextSpan;
676
677 typedef struct
678 {
679   GtkTextTag *tag;
680   gint prio;
681 } TextTagPrio;
682
683 typedef struct
684 {
685   GSList *states;
686
687   GList *headers;
688
689   GtkTextBuffer *buffer;
690
691   /* Tags that are defined in <tag> elements */
692   GHashTable *defined_tags;
693
694   /* Tags that are anonymous */
695   GHashTable *anonymous_tags;
696
697   /* Tag name substitutions */
698   GHashTable *substitutions;
699
700   /* Current tag */
701   GtkTextTag *current_tag;
702
703   /* Priority of current tag */
704   gint current_tag_prio;
705
706   /* Id of current tag */
707   gint current_tag_id;
708
709   /* Tags and their priorities */
710   GList *tag_priorities;
711
712   GSList *tag_stack;
713
714   GList *spans;
715
716   gboolean create_tags;
717
718   gboolean parsed_text;
719   gboolean parsed_tags;
720 } ParseInfo;
721
722 static void
723 set_error (GError              **err,
724            GMarkupParseContext  *context,
725            int                   error_domain,
726            int                   error_code,
727            const char           *format,
728            ...)
729 {
730   int line, ch;
731   va_list args;
732   char *str;
733
734   g_markup_parse_context_get_position (context, &line, &ch);
735
736   va_start (args, format);
737   str = g_strdup_vprintf (format, args);
738   va_end (args);
739
740   g_set_error (err, error_domain, error_code,
741                ("Line %d character %d: %s"),
742                line, ch, str);
743
744   g_free (str);
745 }
746
747 static void
748 push_state (ParseInfo  *info,
749             ParseState  state)
750 {
751   info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
752 }
753
754 static void
755 pop_state (ParseInfo *info)
756 {
757   g_return_if_fail (info->states != NULL);
758
759   info->states = g_slist_remove (info->states, info->states->data);
760 }
761
762 static ParseState
763 peek_state (ParseInfo *info)
764 {
765   g_return_val_if_fail (info->states != NULL, STATE_START);
766
767   return GPOINTER_TO_INT (info->states->data);
768 }
769
770 #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
771
772
773 static gboolean
774 check_id_or_name (GMarkupParseContext  *context,
775                   const gchar          *element_name,
776                   const gchar         **attribute_names,
777                   const gchar         **attribute_values,
778                   gint                 *id,
779                   const gchar         **name,
780                   GError              **error)
781 {
782   gboolean has_id = FALSE;
783   gboolean has_name = FALSE;
784   int i;
785
786   *id = 0;
787   *name = NULL;
788
789   for (i = 0; attribute_names[i] != NULL; i++)
790     {
791       if (strcmp (attribute_names[i], "name") == 0)
792         {
793           *name = attribute_values[i];
794
795           if (has_id)
796             {
797               set_error (error, context,
798                          G_MARKUP_ERROR,
799                          G_MARKUP_ERROR_PARSE,
800                          _("Both \"id\" and \"name\" were found on the <%s> element"),
801                          element_name);
802               return FALSE;
803             }
804
805           if (has_name)
806             {
807               set_error (error, context,
808                          G_MARKUP_ERROR,
809                          G_MARKUP_ERROR_PARSE,
810                          _("The attribute \"%s\" was found twice on the <%s> element"),
811                          "name", element_name);
812               return FALSE;
813             }
814
815           has_name = TRUE;
816         }
817       else if (strcmp (attribute_names[i], "id") == 0)
818         {
819           gchar *tmp;
820
821           if (has_name)
822             {
823               set_error (error, context,
824                          G_MARKUP_ERROR,
825                          G_MARKUP_ERROR_PARSE,
826                          _("Both \"id\" and \"name\" were found on the <%s> element"),
827                          element_name);
828               return FALSE;
829             }
830
831           if (has_id)
832             {
833               set_error (error, context,
834                          G_MARKUP_ERROR,
835                          G_MARKUP_ERROR_PARSE,
836                          _("The attribute \"%s\" was found twice on the <%s> element"),
837                          "id", element_name);
838               return FALSE;
839             }
840
841           has_id = TRUE;
842
843           /* Try parsing the integer */
844           tmp = NULL;
845           errno = 0;
846           *id = g_ascii_strtoll (attribute_values[i], &tmp, 10);
847
848           if (errno || tmp == attribute_values[i])
849             {
850               set_error (error, context,
851                          G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
852                          _("<%s> element has invalid ID \"%s\""), attribute_values[i]);
853               return FALSE;
854             }
855         }
856     }
857
858   if (!has_id && !has_name)
859     {
860       set_error (error, context,
861                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
862                  _("<%s> element has neither a \"name\" nor an \"id\" attribute"), element_name);
863       return FALSE;
864     }
865
866   return TRUE;
867 }
868
869 typedef struct
870 {
871   const char  *name;
872   const char **retloc;
873 } LocateAttr;
874
875 static gboolean
876 locate_attributes (GMarkupParseContext  *context,
877                    const char           *element_name,
878                    const char          **attribute_names,
879                    const char          **attribute_values,
880                    gboolean              allow_unknown_attrs,
881                    GError              **error,
882                    const char           *first_attribute_name,
883                    const char          **first_attribute_retloc,
884                    ...)
885 {
886   va_list args;
887   const char *name;
888   const char **retloc;
889   int n_attrs;
890 #define MAX_ATTRS 24
891   LocateAttr attrs[MAX_ATTRS];
892   gboolean retval;
893   int i;
894
895   g_return_val_if_fail (first_attribute_name != NULL, FALSE);
896   g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
897
898   retval = TRUE;
899
900   n_attrs = 1;
901   attrs[0].name = first_attribute_name;
902   attrs[0].retloc = first_attribute_retloc;
903   *first_attribute_retloc = NULL;
904
905   va_start (args, first_attribute_retloc);
906
907   name = va_arg (args, const char*);
908   retloc = va_arg (args, const char**);
909
910   while (name != NULL)
911     {
912       g_return_val_if_fail (retloc != NULL, FALSE);
913
914       g_assert (n_attrs < MAX_ATTRS);
915
916       attrs[n_attrs].name = name;
917       attrs[n_attrs].retloc = retloc;
918       n_attrs += 1;
919       *retloc = NULL;
920
921       name = va_arg (args, const char*);
922       retloc = va_arg (args, const char**);
923     }
924
925   va_end (args);
926
927   if (!retval)
928     return retval;
929
930   i = 0;
931   while (attribute_names[i])
932     {
933       int j;
934       gboolean found;
935
936       found = FALSE;
937       j = 0;
938       while (j < n_attrs)
939         {
940           if (strcmp (attrs[j].name, attribute_names[i]) == 0)
941             {
942               retloc = attrs[j].retloc;
943
944               if (*retloc != NULL)
945                 {
946                   set_error (error, context,
947                              G_MARKUP_ERROR,
948                              G_MARKUP_ERROR_PARSE,
949                              _("Attribute \"%s\" repeated twice on the same <%s> element"),
950                              attrs[j].name, element_name);
951                   retval = FALSE;
952                   goto out;
953                 }
954
955               *retloc = attribute_values[i];
956               found = TRUE;
957             }
958
959           ++j;
960         }
961
962       if (!found && !allow_unknown_attrs)
963         {
964           set_error (error, context,
965                      G_MARKUP_ERROR,
966                      G_MARKUP_ERROR_PARSE,
967                      _("Attribute \"%s\" is invalid on <%s> element in this context"),
968                      attribute_names[i], element_name);
969           retval = FALSE;
970           goto out;
971         }
972
973       ++i;
974     }
975
976  out:
977   return retval;
978 }
979
980 static gboolean
981 check_no_attributes (GMarkupParseContext  *context,
982                      const char           *element_name,
983                      const char          **attribute_names,
984                      const char          **attribute_values,
985                      GError              **error)
986 {
987   if (attribute_names[0] != NULL)
988     {
989       set_error (error, context,
990                  G_MARKUP_ERROR,
991                  G_MARKUP_ERROR_PARSE,
992                  _("Attribute \"%s\" is invalid on <%s> element in this context"),
993                  attribute_names[0], element_name);
994       return FALSE;
995     }
996
997   return TRUE;
998 }
999
1000 static GtkTextTag *
1001 tag_exists (GMarkupParseContext *context,
1002             const gchar         *name,
1003             gint                 id,
1004             ParseInfo           *info,
1005             GError             **error)
1006 {
1007   GtkTextTagTable *tag_table;
1008   const gchar *real_name;
1009
1010   tag_table = gtk_text_buffer_get_tag_table (info->buffer);
1011
1012   if (info->create_tags)
1013     {
1014       /* If we have an anonymous tag, just return it directly */
1015       if (!name)
1016         return g_hash_table_lookup (info->anonymous_tags,
1017                                     GINT_TO_POINTER (id));
1018
1019       /* First, try the substitutions */
1020       real_name = g_hash_table_lookup (info->substitutions, name);
1021
1022       if (real_name)
1023         return gtk_text_tag_table_lookup (tag_table, real_name);
1024
1025       /* Next, try the list of defined tags */
1026       if (g_hash_table_lookup (info->defined_tags, name) != NULL)
1027         return gtk_text_tag_table_lookup (tag_table, name);
1028
1029       set_error (error, context,
1030                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1031                  _("Tag \"%s\" has not been defined."), name);
1032
1033       return NULL;
1034     }
1035   else
1036     {
1037       GtkTextTag *tag;
1038
1039       if (!name)
1040         {
1041           set_error (error, context,
1042                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1043                      _("Anonymous tag found and tags can not be created."));
1044           return NULL;
1045         }
1046
1047       tag = gtk_text_tag_table_lookup (tag_table, name);
1048
1049       if (tag)
1050         return tag;
1051
1052       set_error (error, context,
1053                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1054                  _("Tag \"%s\" does not exist in buffer and tags can not be created."), name);
1055
1056       return NULL;
1057     }
1058 }
1059
1060 typedef struct
1061 {
1062   const gchar *id;
1063   gint length;
1064   const gchar *start;
1065 } Header;
1066
1067 static GdkPixbuf *
1068 get_pixbuf_from_headers (GList   *headers,
1069                          int      id,
1070                          GError **error)
1071 {
1072   Header *header;
1073   GdkPixdata pixdata;
1074   GdkPixbuf *pixbuf;
1075
1076   header = g_list_nth_data (headers, id);
1077
1078   if (!header)
1079     return NULL;
1080
1081   if (!gdk_pixdata_deserialize (&pixdata, header->length,
1082                                 (const guint8 *) header->start, error))
1083     return NULL;
1084
1085   pixbuf = gdk_pixbuf_from_pixdata (&pixdata, TRUE, error);
1086
1087   return pixbuf;
1088 }
1089
1090 static void
1091 parse_apply_tag_element (GMarkupParseContext  *context,
1092                          const gchar          *element_name,
1093                          const gchar         **attribute_names,
1094                          const gchar         **attribute_values,
1095                          ParseInfo            *info,
1096                          GError              **error)
1097 {
1098   const gchar *name, *priority;
1099   gint id;
1100   GtkTextTag *tag;
1101
1102   g_assert (peek_state (info) == STATE_TEXT ||
1103             peek_state (info) == STATE_APPLY_TAG);
1104
1105   if (ELEMENT_IS ("apply_tag"))
1106     {
1107       if (!locate_attributes (context, element_name, attribute_names, attribute_values, TRUE, error,
1108                               "priority", &priority, NULL))
1109         return;
1110
1111       if (!check_id_or_name (context, element_name, attribute_names, attribute_values,
1112                              &id, &name, error))
1113         return;
1114
1115
1116       tag = tag_exists (context, name, id, info, error);
1117
1118       if (!tag)
1119         return;
1120
1121       info->tag_stack = g_slist_prepend (info->tag_stack, tag);
1122
1123       push_state (info, STATE_APPLY_TAG);
1124     }
1125   else if (ELEMENT_IS ("pixbuf"))
1126     {
1127       int int_id;
1128       GdkPixbuf *pixbuf;
1129       TextSpan *span;
1130       const gchar *pixbuf_id;
1131
1132       if (!locate_attributes (context, element_name, attribute_names, attribute_values, FALSE, error,
1133                               "index", &pixbuf_id, NULL))
1134         return;
1135
1136       int_id = atoi (pixbuf_id);
1137       pixbuf = get_pixbuf_from_headers (info->headers, int_id, error);
1138
1139       span = g_new0 (TextSpan, 1);
1140       span->pixbuf = pixbuf;
1141       span->tags = NULL;
1142
1143       info->spans = g_list_prepend (info->spans, span);
1144
1145       if (!pixbuf)
1146         return;
1147
1148       push_state (info, STATE_PIXBUF);
1149     }
1150   else
1151     set_error (error, context,
1152                G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1153                _("Element <%s> is not allowed below <%s>"),
1154                element_name, peek_state(info) == STATE_TEXT ? "text" : "apply_tag");
1155 }
1156
1157 static void
1158 parse_attr_element (GMarkupParseContext  *context,
1159                     const gchar          *element_name,
1160                     const gchar         **attribute_names,
1161                     const gchar         **attribute_values,
1162                     ParseInfo            *info,
1163                     GError              **error)
1164 {
1165   const gchar *name, *type, *value;
1166   GType gtype;
1167   GValue gvalue = { 0 };
1168   GParamSpec *pspec;
1169
1170   g_assert (peek_state (info) == STATE_TAG);
1171
1172   if (ELEMENT_IS ("attr"))
1173     {
1174       if (!locate_attributes (context, element_name, attribute_names, attribute_values, FALSE, error,
1175                               "name", &name, "type", &type, "value", &value, NULL))
1176         return;
1177
1178       gtype = g_type_from_name (type);
1179
1180       if (gtype == G_TYPE_INVALID)
1181         {
1182           set_error (error, context,
1183                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1184                      _("\"%s\" is not a valid attribute type"), type);
1185           return;
1186         }
1187
1188       if (!(pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info->current_tag), name)))
1189         {
1190           set_error (error, context,
1191                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1192                      _("\"%s\" is not a valid attribute name"), name);
1193           return;
1194         }
1195
1196       g_value_init (&gvalue, gtype);
1197
1198       if (!deserialize_value (value, &gvalue))
1199         {
1200           set_error (error, context,
1201                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1202                      _("\"%s\" could not be converted to a value of type \"%s\" for attribute \"%s\""),
1203                      value, type, name);
1204           return;
1205         }
1206
1207       if (g_param_value_validate (pspec, &gvalue))
1208         {
1209           set_error (error, context,
1210                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1211                      _("\"%s\" is not a valid value for attribute \"%s\""),
1212                      value, name);
1213           g_value_unset (&gvalue);
1214           return;
1215         }
1216
1217       g_object_set_property (G_OBJECT (info->current_tag),
1218                              name, &gvalue);
1219
1220       g_value_unset (&gvalue);
1221
1222       push_state (info, STATE_ATTR);
1223     }
1224   else
1225     {
1226       set_error (error, context,
1227                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1228                  _("Element <%s> is not allowed below <%s>"),
1229                  element_name, "tag");
1230     }
1231 }
1232
1233
1234 static gchar *
1235 get_tag_name (ParseInfo   *info,
1236               const gchar *tag_name)
1237 {
1238   GtkTextTagTable *tag_table;
1239   gchar *name;
1240   gint i;
1241
1242   name = g_strdup (tag_name);
1243
1244   if (!info->create_tags)
1245     return name;
1246
1247   i = 0;
1248   tag_table = gtk_text_buffer_get_tag_table (info->buffer);
1249
1250   while (gtk_text_tag_table_lookup (tag_table, name) != NULL)
1251     {
1252       g_free (name);
1253       name = g_strdup_printf ("%s-%d", tag_name, ++i);
1254     }
1255
1256   if (i != 0)
1257     {
1258       g_hash_table_insert (info->substitutions, g_strdup (tag_name), g_strdup (name));
1259     }
1260
1261   return name;
1262 }
1263
1264 static void
1265 parse_tag_element (GMarkupParseContext  *context,
1266                    const gchar          *element_name,
1267                    const gchar         **attribute_names,
1268                    const gchar         **attribute_values,
1269                    ParseInfo            *info,
1270                    GError              **error)
1271 {
1272   const gchar *name, *priority;
1273   gchar *tag_name;
1274   gint id;
1275   gint prio;
1276   gchar *tmp;
1277
1278   g_assert (peek_state (info) == STATE_TAGS);
1279
1280   if (ELEMENT_IS ("tag"))
1281     {
1282       if (!locate_attributes (context, element_name, attribute_names, attribute_values, TRUE, error,
1283                               "priority", &priority, NULL))
1284         return;
1285
1286       if (!check_id_or_name (context, element_name, attribute_names, attribute_values,
1287                              &id, &name, error))
1288         return;
1289
1290       if (name)
1291         {
1292           if (g_hash_table_lookup (info->defined_tags, name) != NULL)
1293             {
1294               set_error (error, context,
1295                          G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1296                          _("Tag \"%s\" already defined"), name);
1297               return;
1298             }
1299         }
1300
1301       tmp = NULL;
1302       errno = 0;
1303       prio = g_ascii_strtoll (priority, &tmp, 10);
1304
1305       if (errno || tmp == priority)
1306         {
1307           set_error (error, context,
1308                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1309                      _("Tag \"%s\" has invalid priority \"%s\""), name, priority);
1310           return;
1311         }
1312
1313       if (name)
1314         {
1315           tag_name = get_tag_name (info, name);
1316           info->current_tag = gtk_text_tag_new (tag_name);
1317           g_free (tag_name);
1318         }
1319       else
1320         {
1321           info->current_tag = gtk_text_tag_new (NULL);
1322           info->current_tag_id = id;
1323         }
1324
1325       info->current_tag_prio = prio;
1326
1327       push_state (info, STATE_TAG);
1328     }
1329   else
1330     {
1331       set_error (error, context,
1332                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1333                  _("Element <%s> is not allowed below <%s>"),
1334                  element_name, "tags");
1335     }
1336 }
1337
1338 static void
1339 start_element_handler (GMarkupParseContext  *context,
1340                        const gchar          *element_name,
1341                        const gchar         **attribute_names,
1342                        const gchar         **attribute_values,
1343                        gpointer              user_data,
1344                        GError              **error)
1345 {
1346   ParseInfo *info = user_data;
1347
1348   switch (peek_state (info))
1349     {
1350     case STATE_START:
1351       if (ELEMENT_IS ("text_view_markup"))
1352         {
1353           if (!check_no_attributes (context, element_name,
1354                                     attribute_names, attribute_values, error))
1355             return;
1356
1357           push_state (info, STATE_TEXT_VIEW_MARKUP);
1358           break;
1359         }
1360       else
1361         set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1362                    _("Outermost element in text must be <text_view_markup> not <%s>"),
1363                    element_name);
1364       break;
1365     case STATE_TEXT_VIEW_MARKUP:
1366       if (ELEMENT_IS ("tags"))
1367         {
1368           if (info->parsed_tags)
1369             {
1370               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1371                          _("A <%s> element has already been specified"), "tags");
1372               return;
1373             }
1374
1375           if (!check_no_attributes (context, element_name,
1376                                     attribute_names, attribute_values, error))
1377             return;
1378
1379           push_state (info, STATE_TAGS);
1380           break;
1381         }
1382       else if (ELEMENT_IS ("text"))
1383         {
1384           if (info->parsed_text)
1385             {
1386               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1387                          _("A <%s> element has already been specified"), "text");
1388               return;
1389             }
1390           else if (!info->parsed_tags)
1391             {
1392               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1393                          _("A <text> element can't occur before a <tags> element"));
1394               return;
1395             }
1396
1397           if (!check_no_attributes (context, element_name,
1398                                     attribute_names, attribute_values, error))
1399             return;
1400
1401           push_state (info, STATE_TEXT);
1402           break;
1403         }
1404       else
1405         set_error (error, context,
1406                    G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1407                  _("Element <%s> is not allowed below <%s>"),
1408                    element_name, "text_view_markup");
1409       break;
1410     case STATE_TAGS:
1411       parse_tag_element (context, element_name,
1412                          attribute_names, attribute_values,
1413                          info, error);
1414       break;
1415     case STATE_TAG:
1416       parse_attr_element (context, element_name,
1417                           attribute_names, attribute_values,
1418                           info, error);
1419       break;
1420     case STATE_TEXT:
1421     case STATE_APPLY_TAG:
1422       parse_apply_tag_element (context, element_name,
1423                                attribute_names, attribute_values,
1424                                info, error);
1425       break;
1426     default:
1427       g_assert_not_reached ();
1428       break;
1429     }
1430 }
1431
1432 static gint
1433 sort_tag_prio (TextTagPrio *a,
1434                TextTagPrio *b)
1435 {
1436   if (a->prio < b->prio)
1437     return -1;
1438   else if (a->prio > b->prio)
1439     return 1;
1440   else
1441     return 0;
1442 }
1443
1444 static void
1445 end_element_handler (GMarkupParseContext  *context,
1446                      const gchar          *element_name,
1447                      gpointer              user_data,
1448                      GError              **error)
1449 {
1450   ParseInfo *info = user_data;
1451   gchar *tmp;
1452   GList *list;
1453
1454   switch (peek_state (info))
1455     {
1456     case STATE_TAGS:
1457       pop_state (info);
1458       g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
1459
1460       info->parsed_tags = TRUE;
1461
1462       /* Sort list and add the tags */
1463       info->tag_priorities = g_list_sort (info->tag_priorities,
1464                                           (GCompareFunc)sort_tag_prio);
1465       list = info->tag_priorities;
1466       while (list)
1467         {
1468           TextTagPrio *prio = list->data;
1469
1470           if (info->create_tags)
1471             gtk_text_tag_table_add (gtk_text_buffer_get_tag_table (info->buffer),
1472                                     prio->tag);
1473
1474           g_object_unref (prio->tag);
1475           prio->tag = NULL;
1476
1477           list = list->next;
1478         }
1479
1480       break;
1481     case STATE_TAG:
1482       pop_state (info);
1483       g_assert (peek_state (info) == STATE_TAGS);
1484
1485       if (info->current_tag->priv->name)
1486         {
1487           /* Add tag to defined tags hash */
1488           tmp = g_strdup (info->current_tag->priv->name);
1489           g_hash_table_insert (info->defined_tags,
1490                                tmp, tmp);
1491         }
1492       else
1493         {
1494           g_hash_table_insert (info->anonymous_tags,
1495                                GINT_TO_POINTER (info->current_tag_id),
1496                                info->current_tag);
1497         }
1498
1499       if (info->create_tags)
1500         {
1501           TextTagPrio *prio;
1502
1503           /* add the tag to the list */
1504           prio = g_new0 (TextTagPrio, 1);
1505           prio->prio = info->current_tag_prio;
1506           prio->tag = info->current_tag;
1507
1508           info->tag_priorities = g_list_prepend (info->tag_priorities, prio);
1509         }
1510
1511       info->current_tag = NULL;
1512       break;
1513     case STATE_ATTR:
1514       pop_state (info);
1515       g_assert (peek_state (info) == STATE_TAG);
1516       break;
1517     case STATE_APPLY_TAG:
1518       pop_state (info);
1519       g_assert (peek_state (info) == STATE_APPLY_TAG ||
1520                 peek_state (info) == STATE_TEXT);
1521
1522       /* Pop tag */
1523       info->tag_stack = g_slist_delete_link (info->tag_stack,
1524                                              info->tag_stack);
1525
1526       break;
1527     case STATE_TEXT:
1528       pop_state (info);
1529       g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
1530
1531       info->spans = g_list_reverse (info->spans);
1532       info->parsed_text = TRUE;
1533       break;
1534     case STATE_TEXT_VIEW_MARKUP:
1535       pop_state (info);
1536       g_assert (peek_state (info) == STATE_START);
1537       break;
1538     case STATE_PIXBUF:
1539       pop_state (info);
1540       g_assert (peek_state (info) == STATE_APPLY_TAG ||
1541                 peek_state (info) == STATE_TEXT);
1542       break;
1543     default:
1544       g_assert_not_reached ();
1545       break;
1546     }
1547 }
1548
1549 static gboolean
1550 all_whitespace (const char *text,
1551                 int         text_len)
1552 {
1553   const char *p;
1554   const char *end;
1555
1556   p = text;
1557   end = text + text_len;
1558
1559   while (p != end)
1560     {
1561       if (!g_ascii_isspace (*p))
1562         return FALSE;
1563
1564       p = g_utf8_next_char (p);
1565     }
1566
1567   return TRUE;
1568 }
1569
1570 static void
1571 text_handler (GMarkupParseContext  *context,
1572               const gchar          *text,
1573               gsize                 text_len,
1574               gpointer              user_data,
1575               GError              **error)
1576 {
1577   ParseInfo *info = user_data;
1578   TextSpan *span;
1579
1580   if (all_whitespace (text, text_len) &&
1581       peek_state (info) != STATE_TEXT &&
1582       peek_state (info) != STATE_APPLY_TAG)
1583     return;
1584
1585   switch (peek_state (info))
1586     {
1587     case STATE_START:
1588       g_assert_not_reached (); /* gmarkup shouldn't do this */
1589       break;
1590     case STATE_TEXT:
1591     case STATE_APPLY_TAG:
1592       if (text_len == 0)
1593         return;
1594
1595       span = g_new0 (TextSpan, 1);
1596       span->text = g_strndup (text, text_len);
1597       span->tags = g_slist_copy (info->tag_stack);
1598
1599       info->spans = g_list_prepend (info->spans, span);
1600       break;
1601     default:
1602       g_assert_not_reached ();
1603       break;
1604     }
1605 }
1606
1607 static void
1608 parse_info_init (ParseInfo     *info,
1609                  GtkTextBuffer *buffer,
1610                  gboolean       create_tags,
1611                  GList         *headers)
1612 {
1613   info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
1614
1615   info->create_tags = create_tags;
1616   info->headers = headers;
1617   info->defined_tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1618   info->substitutions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1619   info->anonymous_tags = g_hash_table_new_full (NULL, NULL, NULL, NULL);
1620   info->tag_stack = NULL;
1621   info->spans = NULL;
1622   info->parsed_text = FALSE;
1623   info->parsed_tags = FALSE;
1624   info->current_tag = NULL;
1625   info->current_tag_prio = -1;
1626   info->tag_priorities = NULL;
1627
1628   info->buffer = buffer;
1629 }
1630
1631 static void
1632 text_span_free (TextSpan *span)
1633 {
1634   g_free (span->text);
1635   g_slist_free (span->tags);
1636   g_free (span);
1637 }
1638
1639 static void
1640 parse_info_free (ParseInfo *info)
1641 {
1642   GList *list;
1643
1644   g_slist_free (info->tag_stack);
1645   g_slist_free (info->states);
1646
1647   g_hash_table_destroy (info->substitutions);
1648   g_hash_table_destroy (info->defined_tags);
1649
1650   if (info->current_tag)
1651     g_object_unref (info->current_tag);
1652
1653   list = info->spans;
1654   while (list)
1655     {
1656       text_span_free (list->data);
1657
1658       list = list->next;
1659     }
1660   g_list_free (info->spans);
1661
1662   list = info->tag_priorities;
1663   while (list)
1664     {
1665       TextTagPrio *prio = list->data;
1666
1667       if (prio->tag)
1668         g_object_unref (prio->tag);
1669       g_free (prio);
1670
1671       list = list->next;
1672     }
1673   g_list_free (info->tag_priorities);
1674
1675 }
1676
1677 static void
1678 insert_text (ParseInfo   *info,
1679              GtkTextIter *iter)
1680 {
1681   GtkTextIter start_iter;
1682   GtkTextMark *mark;
1683   GList *tmp;
1684   GSList *tags;
1685
1686   start_iter = *iter;
1687
1688   mark = gtk_text_buffer_create_mark (info->buffer, "deserialize_insert_point",
1689                                       &start_iter, TRUE);
1690
1691   tmp = info->spans;
1692   while (tmp)
1693     {
1694       TextSpan *span = tmp->data;
1695
1696       if (span->text)
1697         gtk_text_buffer_insert (info->buffer, iter, span->text, -1);
1698       else
1699         {
1700           gtk_text_buffer_insert_pixbuf (info->buffer, iter, span->pixbuf);
1701           g_object_unref (span->pixbuf);
1702         }
1703       gtk_text_buffer_get_iter_at_mark (info->buffer, &start_iter, mark);
1704
1705       /* Apply tags */
1706       tags = span->tags;
1707       while (tags)
1708         {
1709           GtkTextTag *tag = tags->data;
1710
1711           gtk_text_buffer_apply_tag (info->buffer, tag,
1712                                      &start_iter, iter);
1713
1714           tags = tags->next;
1715         }
1716
1717       gtk_text_buffer_move_mark (info->buffer, mark, iter);
1718
1719       tmp = tmp->next;
1720     }
1721
1722   gtk_text_buffer_delete_mark (info->buffer, mark);
1723 }
1724
1725
1726
1727 static int
1728 read_int (const guchar *start)
1729 {
1730   int result;
1731
1732   result =
1733     start[0] << 24 |
1734     start[1] << 16 |
1735     start[2] << 8 |
1736     start[3];
1737
1738   return result;
1739 }
1740
1741 static gboolean
1742 header_is (Header      *header,
1743            const gchar *id)
1744 {
1745   return (strncmp (header->id, id, strlen (id)) == 0);
1746 }
1747
1748 static GList *
1749 read_headers (const gchar *start,
1750               gint         len,
1751               GError     **error)
1752 {
1753   int i = 0;
1754   int section_len;
1755   Header *header;
1756   GList *headers = NULL;
1757
1758   while (i < len)
1759     {
1760       if (i + 30 >= len)
1761         goto error;
1762
1763       if (strncmp (start + i, "GTKTEXTBUFFERCONTENTS-0001", 26) == 0 ||
1764           strncmp (start + i, "GTKTEXTBUFFERPIXBDATA-0001", 26) == 0)
1765         {
1766           section_len = read_int ((const guchar *) start + i + 26);
1767
1768           if (i + 30 + section_len > len)
1769             goto error;
1770
1771           header = g_new0 (Header, 1);
1772           header->id = start + i;
1773           header->length = section_len;
1774           header->start = start + i + 30;
1775
1776           i += 30 + section_len;
1777
1778           headers = g_list_prepend (headers, header);
1779         }
1780       else
1781         break;
1782     }
1783
1784   return g_list_reverse (headers);
1785
1786  error:
1787   g_list_foreach (headers, (GFunc) g_free, NULL);
1788   g_list_free (headers);
1789
1790   g_set_error_literal (error,
1791                        G_MARKUP_ERROR,
1792                        G_MARKUP_ERROR_PARSE,
1793                        _("Serialized data is malformed"));
1794
1795   return NULL;
1796 }
1797
1798 static gboolean
1799 deserialize_text (GtkTextBuffer *buffer,
1800                   GtkTextIter   *iter,
1801                   const gchar   *text,
1802                   gint           len,
1803                   gboolean       create_tags,
1804                   GError       **error,
1805                   GList         *headers)
1806 {
1807   GMarkupParseContext *context;
1808   ParseInfo info;
1809   gboolean retval = FALSE;
1810
1811   static const GMarkupParser rich_text_parser = {
1812     start_element_handler,
1813     end_element_handler,
1814     text_handler,
1815     NULL,
1816     NULL
1817   };
1818
1819   parse_info_init (&info, buffer, create_tags, headers);
1820
1821   context = g_markup_parse_context_new (&rich_text_parser,
1822                                         0, &info, NULL);
1823
1824   if (!g_markup_parse_context_parse (context,
1825                                      text,
1826                                      len,
1827                                      error))
1828     goto out;
1829
1830   if (!g_markup_parse_context_end_parse (context, error))
1831     goto out;
1832
1833   retval = TRUE;
1834
1835   /* Now insert the text */
1836   insert_text (&info, iter);
1837
1838  out:
1839   parse_info_free (&info);
1840
1841   g_markup_parse_context_free (context);
1842
1843   return retval;
1844 }
1845
1846 gboolean
1847 _gtk_text_buffer_deserialize_rich_text (GtkTextBuffer *register_buffer,
1848                                         GtkTextBuffer *content_buffer,
1849                                         GtkTextIter   *iter,
1850                                         const guint8  *text,
1851                                         gsize          length,
1852                                         gboolean       create_tags,
1853                                         gpointer       user_data,
1854                                         GError       **error)
1855 {
1856   GList *headers;
1857   Header *header;
1858   gboolean retval;
1859
1860   headers = read_headers ((gchar *) text, length, error);
1861
1862   if (!headers)
1863     return FALSE;
1864
1865   header = headers->data;
1866   if (!header_is (header, "GTKTEXTBUFFERCONTENTS-0001"))
1867     {
1868       g_set_error_literal (error,
1869                            G_MARKUP_ERROR,
1870                            G_MARKUP_ERROR_PARSE,
1871                            _("Serialized data is malformed. First section isn't GTKTEXTBUFFERCONTENTS-0001"));
1872
1873       retval = FALSE;
1874       goto out;
1875     }
1876
1877   retval = deserialize_text (content_buffer, iter,
1878                              header->start, header->length,
1879                              create_tags, error, headers->next);
1880
1881  out:
1882   g_list_foreach (headers, (GFunc)g_free, NULL);
1883   g_list_free (headers);
1884
1885   return retval;
1886 }