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