]> Pileus Git - ~andy/gtk/blob - gtk/gtktextbufferrichtext.c
Merge branch 'windows_list'
[~andy/gtk] / gtk / gtktextbufferrichtext.c
1 /* gtkrichtext.c
2  *
3  * Copyright (C) 2006 Imendio AB
4  * Contact: Michael Natterer <mitch@imendio.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23
24 #include <string.h>
25
26 #include "gtktextbufferrichtext.h"
27 #include "gtktextbufferserialize.h"
28 #include "gtkintl.h"
29
30
31 typedef struct
32 {
33   gchar          *mime_type;
34   gboolean        can_create_tags;
35   GdkAtom         atom;
36   gpointer        function;
37   gpointer        user_data;
38   GDestroyNotify  user_data_destroy;
39 } GtkRichTextFormat;
40
41
42 static GList   * register_format   (GList             *formats,
43                                     const gchar       *mime_type,
44                                     gpointer           function,
45                                     gpointer           user_data,
46                                     GDestroyNotify     user_data_destroy,
47                                     GdkAtom           *atom);
48 static GList   * unregister_format (GList             *formats,
49                                     GdkAtom            atom);
50 static GdkAtom * get_formats       (GList             *formats,
51                                     gint              *n_formats);
52 static void      free_format       (GtkRichTextFormat *format);
53 static void      free_format_list  (GList             *formats);
54 static GQuark    serialize_quark   (void);
55 static GQuark    deserialize_quark (void);
56
57
58 /**
59  * gtk_text_buffer_register_serialize_format:
60  * @buffer: a #GtkTextBuffer
61  * @mime_type: the format's mime-type
62  * @function: the serialize function to register
63  * @user_data: %function's user_data
64  * @user_data_destroy: a function to call when @user_data is no longer needed
65  *
66  * This function registers a rich text serialization @function along with
67  * its @mime_type with the passed @buffer.
68  *
69  * Return value: the #GdkAtom that corresponds to the newly registered
70  *               format's mime-type.
71  *
72  * Since: 2.10
73  **/
74 GdkAtom
75 gtk_text_buffer_register_serialize_format (GtkTextBuffer              *buffer,
76                                            const gchar                *mime_type,
77                                            GtkTextBufferSerializeFunc  function,
78                                            gpointer                    user_data,
79                                            GDestroyNotify              user_data_destroy)
80 {
81   GList   *formats;
82   GdkAtom  atom;
83
84   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), GDK_NONE);
85   g_return_val_if_fail (mime_type != NULL && *mime_type != '\0', GDK_NONE);
86   g_return_val_if_fail (function != NULL, GDK_NONE);
87
88   formats = g_object_steal_qdata (G_OBJECT (buffer), serialize_quark ());
89
90   formats = register_format (formats, mime_type,
91                              (gpointer) function,
92                              user_data, user_data_destroy,
93                              &atom);
94
95   g_object_set_qdata_full (G_OBJECT (buffer), serialize_quark (),
96                            formats, (GDestroyNotify) free_format_list);
97
98   g_object_notify (G_OBJECT (buffer), "copy-target-list");
99
100   return atom;
101 }
102
103 /**
104  * gtk_text_buffer_register_serialize_tagset:
105  * @buffer: a #GtkTextBuffer
106  * @tagset_name: (allow-none): an optional tagset name, on %NULL
107  *
108  * This function registers GTK+'s internal rich text serialization
109  * format with the passed @buffer. The internal format does not comply
110  * to any standard rich text format and only works between #GtkTextBuffer
111  * instances. It is capable of serializing all of a text buffer's tags
112  * and embedded pixbufs.
113  *
114  * This function is just a wrapper around
115  * gtk_text_buffer_register_serialize_format(). The mime type used
116  * for registering is "application/x-gtk-text-buffer-rich-text", or
117  * "application/x-gtk-text-buffer-rich-text;format=@tagset_name" if a
118  * @tagset_name was passed.
119  *
120  * The @tagset_name can be used to restrict the transfer of rich text
121  * to buffers with compatible sets of tags, in order to avoid unknown
122  * tags from being pasted. It is probably the common case to pass an
123  * identifier != %NULL here, since the %NULL tagset requires the
124  * receiving buffer to deal with with pasting of arbitrary tags.
125  *
126  * Return value: the #GdkAtom that corresponds to the newly registered
127  *               format's mime-type.
128  *
129  * Since: 2.10
130  **/
131 GdkAtom
132 gtk_text_buffer_register_serialize_tagset (GtkTextBuffer *buffer,
133                                            const gchar   *tagset_name)
134 {
135   gchar   *mime_type = "application/x-gtk-text-buffer-rich-text";
136   GdkAtom  format;
137
138   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), GDK_NONE);
139   g_return_val_if_fail (tagset_name == NULL || *tagset_name != '\0', GDK_NONE);
140
141   if (tagset_name)
142     mime_type =
143       g_strdup_printf ("application/x-gtk-text-buffer-rich-text;format=%s",
144                        tagset_name);
145
146   format = gtk_text_buffer_register_serialize_format (buffer, mime_type,
147                                                       _gtk_text_buffer_serialize_rich_text,
148                                                       NULL, NULL);
149
150   if (tagset_name)
151     g_free (mime_type);
152
153   return format;
154 }
155
156 /**
157  * gtk_text_buffer_register_deserialize_format:
158  * @buffer: a #GtkTextBuffer
159  * @mime_type: the format's mime-type
160  * @function: the deserialize function to register
161  * @user_data: @function's user_data
162  * @user_data_destroy: a function to call when @user_data is no longer needed
163  *
164  * This function registers a rich text deserialization @function along with
165  * its @mime_type with the passed @buffer.
166  *
167  * Return value: the #GdkAtom that corresponds to the newly registered
168  *               format's mime-type.
169  *
170  * Since: 2.10
171  **/
172 GdkAtom
173 gtk_text_buffer_register_deserialize_format (GtkTextBuffer                *buffer,
174                                              const gchar                  *mime_type,
175                                              GtkTextBufferDeserializeFunc  function,
176                                              gpointer                      user_data,
177                                              GDestroyNotify                user_data_destroy)
178 {
179   GList   *formats;
180   GdkAtom  atom;
181
182   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), GDK_NONE);
183   g_return_val_if_fail (mime_type != NULL && *mime_type != '\0', GDK_NONE);
184   g_return_val_if_fail (function != NULL, GDK_NONE);
185
186   formats = g_object_steal_qdata (G_OBJECT (buffer), deserialize_quark ());
187
188   formats = register_format (formats, mime_type,
189                              (gpointer) function,
190                              user_data, user_data_destroy,
191                              &atom);
192
193   g_object_set_qdata_full (G_OBJECT (buffer), deserialize_quark (),
194                            formats, (GDestroyNotify) free_format_list);
195
196   g_object_notify (G_OBJECT (buffer), "paste-target-list");
197
198   return atom;
199 }
200
201 /**
202  * gtk_text_buffer_register_deserialize_tagset:
203  * @buffer: a #GtkTextBuffer
204  * @tagset_name: (allow-none): an optional tagset name, on %NULL
205  *
206  * This function registers GTK+'s internal rich text serialization
207  * format with the passed @buffer. See
208  * gtk_text_buffer_register_serialize_tagset() for details.
209  *
210  * Return value: the #GdkAtom that corresponds to the newly registered
211  *               format's mime-type.
212  *
213  * Since: 2.10
214  **/
215 GdkAtom
216 gtk_text_buffer_register_deserialize_tagset (GtkTextBuffer *buffer,
217                                              const gchar   *tagset_name)
218 {
219   gchar   *mime_type = "application/x-gtk-text-buffer-rich-text";
220   GdkAtom  format;
221
222   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), GDK_NONE);
223   g_return_val_if_fail (tagset_name == NULL || *tagset_name != '\0', GDK_NONE);
224
225   if (tagset_name)
226     mime_type =
227       g_strdup_printf ("application/x-gtk-text-buffer-rich-text;format=%s",
228                        tagset_name);
229
230   format = gtk_text_buffer_register_deserialize_format (buffer, mime_type,
231                                                         _gtk_text_buffer_deserialize_rich_text,
232                                                         NULL, NULL);
233
234   if (tagset_name)
235     g_free (mime_type);
236
237   return format;
238 }
239
240 /**
241  * gtk_text_buffer_unregister_serialize_format:
242  * @buffer: a #GtkTextBuffer
243  * @format: a #GdkAtom representing a registered rich text format.
244  *
245  * This function unregisters a rich text format that was previously
246  * registered using gtk_text_buffer_register_serialize_format() or
247  * gtk_text_buffer_register_serialize_tagset()
248  *
249  * Since: 2.10
250  **/
251 void
252 gtk_text_buffer_unregister_serialize_format (GtkTextBuffer *buffer,
253                                              GdkAtom        format)
254 {
255   GList *formats;
256
257   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
258   g_return_if_fail (format != GDK_NONE);
259
260   formats = g_object_steal_qdata (G_OBJECT (buffer), serialize_quark ());
261
262   formats = unregister_format (formats, format);
263
264   g_object_set_qdata_full (G_OBJECT (buffer), serialize_quark (),
265                            formats, (GDestroyNotify) free_format_list);
266
267   g_object_notify (G_OBJECT (buffer), "copy-target-list");
268 }
269
270 /**
271  * gtk_text_buffer_unregister_deserialize_format:
272  * @buffer: a #GtkTextBuffer
273  * @format: a #GdkAtom representing a registered rich text format.
274  *
275  * This function unregisters a rich text format that was previously
276  * registered using gtk_text_buffer_register_deserialize_format() or
277  * gtk_text_buffer_register_deserialize_tagset().
278  *
279  * Since: 2.10
280  **/
281 void
282 gtk_text_buffer_unregister_deserialize_format (GtkTextBuffer *buffer,
283                                                GdkAtom        format)
284 {
285   GList *formats;
286
287   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
288   g_return_if_fail (format != GDK_NONE);
289
290   formats = g_object_steal_qdata (G_OBJECT (buffer), deserialize_quark ());
291
292   formats = unregister_format (formats, format);
293
294   g_object_set_qdata_full (G_OBJECT (buffer), deserialize_quark (),
295                            formats, (GDestroyNotify) free_format_list);
296
297   g_object_notify (G_OBJECT (buffer), "paste-target-list");
298 }
299
300 /**
301  * gtk_text_buffer_deserialize_set_can_create_tags:
302  * @buffer: a #GtkTextBuffer
303  * @format: a #GdkAtom representing a registered rich text format
304  * @can_create_tags: whether deserializing this format may create tags
305  *
306  * Use this function to allow a rich text deserialization function to
307  * create new tags in the receiving buffer. Note that using this
308  * function is almost always a bad idea, because the rich text
309  * functions you register should know how to map the rich text format
310  * they handler to your text buffers set of tags.
311  *
312  * The ability of creating new (arbitrary!) tags in the receiving buffer
313  * is meant for special rich text formats like the internal one that
314  * is registered using gtk_text_buffer_register_deserialize_tagset(),
315  * because that format is essentially a dump of the internal structure
316  * of the source buffer, including its tag names.
317  *
318  * You should allow creation of tags only if you know what you are
319  * doing, e.g. if you defined a tagset name for your application
320  * suite's text buffers and you know that it's fine to receive new
321  * tags from these buffers, because you know that your application can
322  * handle the newly created tags.
323  *
324  * Since: 2.10
325  **/
326 void
327 gtk_text_buffer_deserialize_set_can_create_tags (GtkTextBuffer *buffer,
328                                                  GdkAtom        format,
329                                                  gboolean       can_create_tags)
330 {
331   GList *formats;
332   GList *list;
333   gchar *format_name;
334
335   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
336   g_return_if_fail (format != GDK_NONE);
337
338   formats = g_object_get_qdata (G_OBJECT (buffer), deserialize_quark ());
339
340   for (list = formats; list; list = g_list_next (list))
341     {
342       GtkRichTextFormat *fmt = list->data;
343
344       if (fmt->atom == format)
345         {
346           fmt->can_create_tags = can_create_tags ? TRUE : FALSE;
347           return;
348         }
349     }
350
351   format_name = gdk_atom_name (format);
352   g_warning ("%s: \"%s\" is not registered as deserializable format "
353              "with text buffer %p",
354              G_STRFUNC, format_name ? format_name : "not a GdkAtom", buffer);
355   g_free (format_name);
356 }
357
358 /**
359  * gtk_text_buffer_deserialize_get_can_create_tags:
360  * @buffer: a #GtkTextBuffer
361  * @format: a #GdkAtom representing a registered rich text format
362  *
363  * This functions returns the value set with
364  * gtk_text_buffer_deserialize_set_can_create_tags()
365  *
366  * Return value: whether deserializing this format may create tags
367  *
368  * Since: 2.10
369  **/
370 gboolean
371 gtk_text_buffer_deserialize_get_can_create_tags (GtkTextBuffer *buffer,
372                                                  GdkAtom        format)
373 {
374   GList *formats;
375   GList *list;
376   gchar *format_name;
377
378   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
379   g_return_val_if_fail (format != GDK_NONE, FALSE);
380
381   formats = g_object_get_qdata (G_OBJECT (buffer), deserialize_quark ());
382
383   for (list = formats; list; list = g_list_next (list))
384     {
385       GtkRichTextFormat *fmt = list->data;
386
387       if (fmt->atom == format)
388         {
389           return fmt->can_create_tags;
390         }
391     }
392
393   format_name = gdk_atom_name (format);
394   g_warning ("%s: \"%s\" is not registered as deserializable format "
395              "with text buffer %p",
396              G_STRFUNC, format_name ? format_name : "not a GdkAtom", buffer);
397   g_free (format_name);
398
399   return FALSE;
400 }
401
402 /**
403  * gtk_text_buffer_get_serialize_formats:
404  * @buffer: a #GtkTextBuffer
405  * @n_formats: return location for the number of formats
406  *
407  * This function returns the rich text serialize formats registered
408  * with @buffer using gtk_text_buffer_register_serialize_format() or
409  * gtk_text_buffer_register_serialize_tagset()
410  *
411  * Return value: an array of #GdkAtom<!-- -->s representing the registered
412  *               formats.
413  *
414  * Since: 2.10
415  **/
416 GdkAtom *
417 gtk_text_buffer_get_serialize_formats (GtkTextBuffer *buffer,
418                                        gint          *n_formats)
419 {
420   GList *formats;
421
422   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
423   g_return_val_if_fail (n_formats != NULL, NULL);
424
425   formats = g_object_get_qdata (G_OBJECT (buffer), serialize_quark ());
426
427   return get_formats (formats, n_formats);
428 }
429
430 /**
431  * gtk_text_buffer_get_deserialize_formats:
432  * @buffer: a #GtkTextBuffer
433  * @n_formats: return location for the number of formats
434  *
435  * This function returns the rich text deserialize formats registered
436  * with @buffer using gtk_text_buffer_register_deserialize_format() or
437  * gtk_text_buffer_register_deserialize_tagset()
438  *
439  * Return value: an array of #GdkAtom<!-- -->s representing the registered
440  *               formats.
441  *
442  * Since: 2.10
443  **/
444 GdkAtom *
445 gtk_text_buffer_get_deserialize_formats (GtkTextBuffer *buffer,
446                                          gint          *n_formats)
447 {
448   GList *formats;
449
450   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
451   g_return_val_if_fail (n_formats != NULL, NULL);
452
453   formats = g_object_get_qdata (G_OBJECT (buffer), deserialize_quark ());
454
455   return get_formats (formats, n_formats);
456 }
457
458 /**
459  * gtk_text_buffer_serialize:
460  * @register_buffer: the #GtkTextBuffer @format is registered with
461  * @content_buffer: the #GtkTextBuffer to serialize
462  * @format: the rich text format to use for serializing
463  * @start: start of block of text to serialize
464  * @end: end of block of test to serialize
465  * @length: return location for the length of the serialized data
466  *
467  * This function serializes the portion of text between @start
468  * and @end in the rich text format represented by @format.
469  *
470  * @format<!-- -->s to be used must be registered using
471  * gtk_text_buffer_register_serialize_format() or
472  * gtk_text_buffer_register_serialize_tagset() beforehand.
473  *
474  * Return value: the serialized data, encoded as @format
475  *
476  * Since: 2.10
477  **/
478 guint8 *
479 gtk_text_buffer_serialize (GtkTextBuffer     *register_buffer,
480                            GtkTextBuffer     *content_buffer,
481                            GdkAtom            format,
482                            const GtkTextIter *start,
483                            const GtkTextIter *end,
484                            gsize             *length)
485 {
486   GList *formats;
487   GList *list;
488
489   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (register_buffer), NULL);
490   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (content_buffer), NULL);
491   g_return_val_if_fail (format != GDK_NONE, NULL);
492   g_return_val_if_fail (start != NULL, NULL);
493   g_return_val_if_fail (end != NULL, NULL);
494   g_return_val_if_fail (length != NULL, NULL);
495
496   *length = 0;
497
498   formats = g_object_get_qdata (G_OBJECT (register_buffer),
499                                 serialize_quark ());
500
501   for (list = formats; list; list = g_list_next (list))
502     {
503       GtkRichTextFormat *fmt = list->data;
504
505       if (fmt->atom == format)
506         {
507           GtkTextBufferSerializeFunc function = fmt->function;
508
509           return function (register_buffer, content_buffer,
510                            start, end, length, fmt->user_data);
511         }
512     }
513
514   return NULL;
515 }
516
517 /**
518  * gtk_text_buffer_deserialize:
519  * @register_buffer: the #GtkTextBuffer @format is registered with
520  * @content_buffer: the #GtkTextBuffer to deserialize into
521  * @format: the rich text format to use for deserializing
522  * @iter: insertion point for the deserialized text
523  * @data: data to deserialize
524  * @length: length of @data
525  * @error: return location for a #GError
526  *
527  * This function deserializes rich text in format @format and inserts
528  * it at @iter.
529  *
530  * @format<!-- -->s to be used must be registered using
531  * gtk_text_buffer_register_deserialize_format() or
532  * gtk_text_buffer_register_deserialize_tagset() beforehand.
533  *
534  * Return value: %TRUE on success, %FALSE otherwise.
535  *
536  * Since: 2.10
537  **/
538 gboolean
539 gtk_text_buffer_deserialize (GtkTextBuffer  *register_buffer,
540                              GtkTextBuffer  *content_buffer,
541                              GdkAtom         format,
542                              GtkTextIter    *iter,
543                              const guint8   *data,
544                              gsize           length,
545                              GError        **error)
546 {
547   GList    *formats;
548   GList    *list;
549
550   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (register_buffer), FALSE);
551   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (content_buffer), FALSE);
552   g_return_val_if_fail (format != GDK_NONE, FALSE);
553   g_return_val_if_fail (iter != NULL, FALSE);
554   g_return_val_if_fail (data != NULL, FALSE);
555   g_return_val_if_fail (length > 0, FALSE);
556   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
557
558   formats = g_object_get_qdata (G_OBJECT (register_buffer),
559                                 deserialize_quark ());
560
561   for (list = formats; list; list = g_list_next (list))
562     {
563       GtkRichTextFormat *fmt = list->data;
564
565       if (fmt->atom == format)
566         {
567           GtkTextBufferDeserializeFunc function = fmt->function;
568           gboolean                     success;
569           GSList                      *split_tags;
570           GSList                      *list;
571           GtkTextMark                 *left_end        = NULL;
572           GtkTextMark                 *right_start     = NULL;
573           GSList                      *left_start_list = NULL;
574           GSList                      *right_end_list  = NULL;
575
576           /*  We don't want the tags that are effective at the insertion
577            *  point to affect the pasted text, therefore we remove and
578            *  remember them, so they can be re-applied left and right of
579            *  the inserted text after pasting
580            */
581           split_tags = gtk_text_iter_get_tags (iter);
582
583           list = split_tags;
584           while (list)
585             {
586               GtkTextTag *tag = list->data;
587
588               list = g_slist_next (list);
589
590               /*  If a tag begins at the insertion point, ignore it
591                *  because it doesn't affect the pasted text
592                */
593               if (gtk_text_iter_begins_tag (iter, tag))
594                 split_tags = g_slist_remove (split_tags, tag);
595             }
596
597           if (split_tags)
598             {
599               /*  Need to remember text marks, because text iters
600                *  don't survive pasting
601                */
602               left_end = gtk_text_buffer_create_mark (content_buffer,
603                                                       NULL, iter, TRUE);
604               right_start = gtk_text_buffer_create_mark (content_buffer,
605                                                          NULL, iter, FALSE);
606
607               for (list = split_tags; list; list = g_slist_next (list))
608                 {
609                   GtkTextTag  *tag             = list->data;
610                   GtkTextIter *backward_toggle = gtk_text_iter_copy (iter);
611                   GtkTextIter *forward_toggle  = gtk_text_iter_copy (iter);
612                   GtkTextMark *left_start      = NULL;
613                   GtkTextMark *right_end       = NULL;
614
615                   gtk_text_iter_backward_to_tag_toggle (backward_toggle, tag);
616                   left_start = gtk_text_buffer_create_mark (content_buffer,
617                                                             NULL,
618                                                             backward_toggle,
619                                                             FALSE);
620
621                   gtk_text_iter_forward_to_tag_toggle (forward_toggle, tag);
622                   right_end = gtk_text_buffer_create_mark (content_buffer,
623                                                            NULL,
624                                                            forward_toggle,
625                                                            TRUE);
626
627                   left_start_list = g_slist_prepend (left_start_list, left_start);
628                   right_end_list = g_slist_prepend (right_end_list, right_end);
629
630                   gtk_text_buffer_remove_tag (content_buffer, tag,
631                                               backward_toggle,
632                                               forward_toggle);
633
634                   gtk_text_iter_free (forward_toggle);
635                   gtk_text_iter_free (backward_toggle);
636                 }
637
638               left_start_list = g_slist_reverse (left_start_list);
639               right_end_list = g_slist_reverse (right_end_list);
640             }
641
642           success = function (register_buffer, content_buffer,
643                               iter, data, length,
644                               fmt->can_create_tags,
645                               fmt->user_data,
646                               error);
647
648           if (!success && error != NULL && *error == NULL)
649             g_set_error (error, 0, 0,
650                          _("Unknown error when trying to deserialize %s"),
651                          gdk_atom_name (format));
652
653           if (split_tags)
654             {
655               GSList      *left_list;
656               GSList      *right_list;
657               GtkTextIter  left_e;
658               GtkTextIter  right_s;
659
660               /*  Turn the remembered marks back into iters so they
661                *  can by used to re-apply the remembered tags
662                */
663               gtk_text_buffer_get_iter_at_mark (content_buffer,
664                                                 &left_e, left_end);
665               gtk_text_buffer_get_iter_at_mark (content_buffer,
666                                                 &right_s, right_start);
667
668               for (list = split_tags,
669                      left_list = left_start_list,
670                      right_list = right_end_list;
671                    list && left_list && right_list;
672                    list = g_slist_next (list),
673                      left_list = g_slist_next (left_list),
674                      right_list = g_slist_next (right_list))
675                 {
676                   GtkTextTag  *tag        = list->data;
677                   GtkTextMark *left_start = left_list->data;
678                   GtkTextMark *right_end  = right_list->data;
679                   GtkTextIter  left_s;
680                   GtkTextIter  right_e;
681
682                   gtk_text_buffer_get_iter_at_mark (content_buffer,
683                                                     &left_s, left_start);
684                   gtk_text_buffer_get_iter_at_mark (content_buffer,
685                                                     &right_e, right_end);
686
687                   gtk_text_buffer_apply_tag (content_buffer, tag,
688                                              &left_s, &left_e);
689                   gtk_text_buffer_apply_tag (content_buffer, tag,
690                                              &right_s, &right_e);
691
692                   gtk_text_buffer_delete_mark (content_buffer, left_start);
693                   gtk_text_buffer_delete_mark (content_buffer, right_end);
694                 }
695
696               gtk_text_buffer_delete_mark (content_buffer, left_end);
697               gtk_text_buffer_delete_mark (content_buffer, right_start);
698
699               g_slist_free (split_tags);
700               g_slist_free (left_start_list);
701               g_slist_free (right_end_list);
702             }
703
704           return success;
705         }
706     }
707
708   g_set_error (error, 0, 0,
709                _("No deserialize function found for format %s"),
710                gdk_atom_name (format));
711
712   return FALSE;
713 }
714
715
716 /*  private functions  */
717
718 static GList *
719 register_format (GList          *formats,
720                  const gchar    *mime_type,
721                  gpointer        function,
722                  gpointer        user_data,
723                  GDestroyNotify  user_data_destroy,
724                  GdkAtom        *atom)
725 {
726   GtkRichTextFormat *format;
727
728   *atom = gdk_atom_intern (mime_type, FALSE);
729
730   formats = unregister_format (formats, *atom);
731
732   format = g_new0 (GtkRichTextFormat, 1);
733
734   format->mime_type         = g_strdup (mime_type);
735   format->can_create_tags   = FALSE;
736   format->atom              = *atom;
737   format->function          = function;
738   format->user_data         = user_data;
739   format->user_data_destroy = user_data_destroy;
740
741   return g_list_append (formats, format);
742 }
743
744 static GList *
745 unregister_format (GList   *formats,
746                    GdkAtom  atom)
747 {
748   GList *list;
749
750   for (list = formats; list; list = g_list_next (list))
751     {
752       GtkRichTextFormat *format = list->data;
753
754       if (format->atom == atom)
755         {
756           free_format (format);
757
758           return g_list_delete_link (formats, list);
759         }
760     }
761
762   return formats;
763 }
764
765 static GdkAtom *
766 get_formats (GList *formats,
767              gint  *n_formats)
768 {
769   GdkAtom *array;
770   GList   *list;
771   gint     i;
772
773   *n_formats = g_list_length (formats);
774   array = g_new0 (GdkAtom, *n_formats);
775
776   for (list = formats, i = 0; list; list = g_list_next (list), i++)
777     {
778       GtkRichTextFormat *format = list->data;
779
780       array[i] = format->atom;
781     }
782
783   return array;
784 }
785
786 static void
787 free_format (GtkRichTextFormat *format)
788 {
789   if (format->user_data_destroy)
790     format->user_data_destroy (format->user_data);
791
792   g_free (format->mime_type);
793   g_free (format);
794 }
795
796 static void
797 free_format_list (GList *formats)
798 {
799   g_list_foreach (formats, (GFunc) free_format, NULL);
800   g_list_free (formats);
801 }
802
803 static GQuark
804 serialize_quark (void)
805 {
806   static GQuark quark = 0;
807
808   if (! quark)
809     quark = g_quark_from_static_string ("gtk-text-buffer-serialize-formats");
810
811   return quark;
812 }
813
814 static GQuark
815 deserialize_quark (void)
816 {
817   static GQuark quark = 0;
818
819   if (! quark)
820     quark = g_quark_from_static_string ("gtk-text-buffer-deserialize-formats");
821
822   return quark;
823 }