]> pilppa.org Git - familiar-h63xx-build.git/blob - org.handhelds.familiar/packages/gtk+/gtk+-2.6.4-1.osso7/gtktextbufferserialize.c.diff
OE tree imported from monotone branch org.openembedded.oz354fam083 at revision 8b12e3...
[familiar-h63xx-build.git] / org.handhelds.familiar / packages / gtk+ / gtk+-2.6.4-1.osso7 / gtktextbufferserialize.c.diff
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 +}