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