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