forked from AuroraMiddleware/gtk
9527205291
GSlice is better for allocating structs. https://bugzilla.gnome.org/show_bug.cgi?id=733407
822 lines
28 KiB
C
822 lines
28 KiB
C
/* gtkrichtext.c
|
||
*
|
||
* Copyright (C) 2006 Imendio AB
|
||
* Contact: Michael Natterer <mitch@imendio.com>
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Library General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2 of the License, or (at your option) any later version.
|
||
*
|
||
* This library is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
* Library General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Library General Public
|
||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include <string.h>
|
||
|
||
#include "gtktextbufferrichtext.h"
|
||
#include "gtktextbufferserialize.h"
|
||
#include "gtkintl.h"
|
||
|
||
|
||
typedef struct
|
||
{
|
||
gchar *mime_type;
|
||
gboolean can_create_tags;
|
||
GdkAtom atom;
|
||
gpointer function;
|
||
gpointer user_data;
|
||
GDestroyNotify user_data_destroy;
|
||
} GtkRichTextFormat;
|
||
|
||
|
||
static GList * register_format (GList *formats,
|
||
const gchar *mime_type,
|
||
gpointer function,
|
||
gpointer user_data,
|
||
GDestroyNotify user_data_destroy,
|
||
GdkAtom *atom);
|
||
static GList * unregister_format (GList *formats,
|
||
GdkAtom atom);
|
||
static GdkAtom * get_formats (GList *formats,
|
||
gint *n_formats);
|
||
static void free_format (GtkRichTextFormat *format);
|
||
static void free_format_list (GList *formats);
|
||
static GQuark serialize_quark (void);
|
||
static GQuark deserialize_quark (void);
|
||
|
||
|
||
/**
|
||
* gtk_text_buffer_register_serialize_format:
|
||
* @buffer: a #GtkTextBuffer
|
||
* @mime_type: the format’s mime-type
|
||
* @function: the serialize function to register
|
||
* @user_data: @function’s user_data
|
||
* @user_data_destroy: a function to call when @user_data is no longer needed
|
||
*
|
||
* This function registers a rich text serialization @function along with
|
||
* its @mime_type with the passed @buffer.
|
||
*
|
||
* Returns: (transfer none): the #GdkAtom that corresponds to the
|
||
* newly registered format’s mime-type.
|
||
*
|
||
* Since: 2.10
|
||
**/
|
||
GdkAtom
|
||
gtk_text_buffer_register_serialize_format (GtkTextBuffer *buffer,
|
||
const gchar *mime_type,
|
||
GtkTextBufferSerializeFunc function,
|
||
gpointer user_data,
|
||
GDestroyNotify user_data_destroy)
|
||
{
|
||
GList *formats;
|
||
GdkAtom atom;
|
||
|
||
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), GDK_NONE);
|
||
g_return_val_if_fail (mime_type != NULL && *mime_type != '\0', GDK_NONE);
|
||
g_return_val_if_fail (function != NULL, GDK_NONE);
|
||
|
||
formats = g_object_steal_qdata (G_OBJECT (buffer), serialize_quark ());
|
||
|
||
formats = register_format (formats, mime_type,
|
||
(gpointer) function,
|
||
user_data, user_data_destroy,
|
||
&atom);
|
||
|
||
g_object_set_qdata_full (G_OBJECT (buffer), serialize_quark (),
|
||
formats, (GDestroyNotify) free_format_list);
|
||
|
||
g_object_notify (G_OBJECT (buffer), "copy-target-list");
|
||
|
||
return atom;
|
||
}
|
||
|
||
/**
|
||
* gtk_text_buffer_register_serialize_tagset:
|
||
* @buffer: a #GtkTextBuffer
|
||
* @tagset_name: (allow-none): an optional tagset name, on %NULL
|
||
*
|
||
* This function registers GTK+’s internal rich text serialization
|
||
* format with the passed @buffer. The internal format does not comply
|
||
* to any standard rich text format and only works between #GtkTextBuffer
|
||
* instances. It is capable of serializing all of a text buffer’s tags
|
||
* and embedded pixbufs.
|
||
*
|
||
* This function is just a wrapper around
|
||
* gtk_text_buffer_register_serialize_format(). The mime type used
|
||
* for registering is “application/x-gtk-text-buffer-rich-text”, or
|
||
* “application/x-gtk-text-buffer-rich-text;format=@tagset_name” if a
|
||
* @tagset_name was passed.
|
||
*
|
||
* The @tagset_name can be used to restrict the transfer of rich text
|
||
* to buffers with compatible sets of tags, in order to avoid unknown
|
||
* tags from being pasted. It is probably the common case to pass an
|
||
* identifier != %NULL here, since the %NULL tagset requires the
|
||
* receiving buffer to deal with with pasting of arbitrary tags.
|
||
*
|
||
* Returns: (transfer none): the #GdkAtom that corresponds to the
|
||
* newly registered format’s mime-type.
|
||
*
|
||
* Since: 2.10
|
||
**/
|
||
GdkAtom
|
||
gtk_text_buffer_register_serialize_tagset (GtkTextBuffer *buffer,
|
||
const gchar *tagset_name)
|
||
{
|
||
gchar *mime_type = "application/x-gtk-text-buffer-rich-text";
|
||
GdkAtom format;
|
||
|
||
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), GDK_NONE);
|
||
g_return_val_if_fail (tagset_name == NULL || *tagset_name != '\0', GDK_NONE);
|
||
|
||
if (tagset_name)
|
||
mime_type =
|
||
g_strdup_printf ("application/x-gtk-text-buffer-rich-text;format=%s",
|
||
tagset_name);
|
||
|
||
format = gtk_text_buffer_register_serialize_format (buffer, mime_type,
|
||
_gtk_text_buffer_serialize_rich_text,
|
||
NULL, NULL);
|
||
|
||
if (tagset_name)
|
||
g_free (mime_type);
|
||
|
||
return format;
|
||
}
|
||
|
||
/**
|
||
* gtk_text_buffer_register_deserialize_format:
|
||
* @buffer: a #GtkTextBuffer
|
||
* @mime_type: the format’s mime-type
|
||
* @function: the deserialize function to register
|
||
* @user_data: @function’s user_data
|
||
* @user_data_destroy: a function to call when @user_data is no longer needed
|
||
*
|
||
* This function registers a rich text deserialization @function along with
|
||
* its @mime_type with the passed @buffer.
|
||
*
|
||
* Returns: (transfer none): the #GdkAtom that corresponds to the
|
||
* newly registered format’s mime-type.
|
||
*
|
||
* Since: 2.10
|
||
**/
|
||
GdkAtom
|
||
gtk_text_buffer_register_deserialize_format (GtkTextBuffer *buffer,
|
||
const gchar *mime_type,
|
||
GtkTextBufferDeserializeFunc function,
|
||
gpointer user_data,
|
||
GDestroyNotify user_data_destroy)
|
||
{
|
||
GList *formats;
|
||
GdkAtom atom;
|
||
|
||
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), GDK_NONE);
|
||
g_return_val_if_fail (mime_type != NULL && *mime_type != '\0', GDK_NONE);
|
||
g_return_val_if_fail (function != NULL, GDK_NONE);
|
||
|
||
formats = g_object_steal_qdata (G_OBJECT (buffer), deserialize_quark ());
|
||
|
||
formats = register_format (formats, mime_type,
|
||
(gpointer) function,
|
||
user_data, user_data_destroy,
|
||
&atom);
|
||
|
||
g_object_set_qdata_full (G_OBJECT (buffer), deserialize_quark (),
|
||
formats, (GDestroyNotify) free_format_list);
|
||
|
||
g_object_notify (G_OBJECT (buffer), "paste-target-list");
|
||
|
||
return atom;
|
||
}
|
||
|
||
/**
|
||
* gtk_text_buffer_register_deserialize_tagset:
|
||
* @buffer: a #GtkTextBuffer
|
||
* @tagset_name: (allow-none): an optional tagset name, on %NULL
|
||
*
|
||
* This function registers GTK+’s internal rich text serialization
|
||
* format with the passed @buffer. See
|
||
* gtk_text_buffer_register_serialize_tagset() for details.
|
||
*
|
||
* Returns: (transfer none): the #GdkAtom that corresponds to the
|
||
* newly registered format’s mime-type.
|
||
*
|
||
* Since: 2.10
|
||
**/
|
||
GdkAtom
|
||
gtk_text_buffer_register_deserialize_tagset (GtkTextBuffer *buffer,
|
||
const gchar *tagset_name)
|
||
{
|
||
gchar *mime_type = "application/x-gtk-text-buffer-rich-text";
|
||
GdkAtom format;
|
||
|
||
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), GDK_NONE);
|
||
g_return_val_if_fail (tagset_name == NULL || *tagset_name != '\0', GDK_NONE);
|
||
|
||
if (tagset_name)
|
||
mime_type =
|
||
g_strdup_printf ("application/x-gtk-text-buffer-rich-text;format=%s",
|
||
tagset_name);
|
||
|
||
format = gtk_text_buffer_register_deserialize_format (buffer, mime_type,
|
||
_gtk_text_buffer_deserialize_rich_text,
|
||
NULL, NULL);
|
||
|
||
if (tagset_name)
|
||
g_free (mime_type);
|
||
|
||
return format;
|
||
}
|
||
|
||
/**
|
||
* gtk_text_buffer_unregister_serialize_format:
|
||
* @buffer: a #GtkTextBuffer
|
||
* @format: a #GdkAtom representing a registered rich text format.
|
||
*
|
||
* This function unregisters a rich text format that was previously
|
||
* registered using gtk_text_buffer_register_serialize_format() or
|
||
* gtk_text_buffer_register_serialize_tagset()
|
||
*
|
||
* Since: 2.10
|
||
**/
|
||
void
|
||
gtk_text_buffer_unregister_serialize_format (GtkTextBuffer *buffer,
|
||
GdkAtom format)
|
||
{
|
||
GList *formats;
|
||
|
||
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
|
||
g_return_if_fail (format != GDK_NONE);
|
||
|
||
formats = g_object_steal_qdata (G_OBJECT (buffer), serialize_quark ());
|
||
|
||
formats = unregister_format (formats, format);
|
||
|
||
g_object_set_qdata_full (G_OBJECT (buffer), serialize_quark (),
|
||
formats, (GDestroyNotify) free_format_list);
|
||
|
||
g_object_notify (G_OBJECT (buffer), "copy-target-list");
|
||
}
|
||
|
||
/**
|
||
* gtk_text_buffer_unregister_deserialize_format:
|
||
* @buffer: a #GtkTextBuffer
|
||
* @format: a #GdkAtom representing a registered rich text format.
|
||
*
|
||
* This function unregisters a rich text format that was previously
|
||
* registered using gtk_text_buffer_register_deserialize_format() or
|
||
* gtk_text_buffer_register_deserialize_tagset().
|
||
*
|
||
* Since: 2.10
|
||
**/
|
||
void
|
||
gtk_text_buffer_unregister_deserialize_format (GtkTextBuffer *buffer,
|
||
GdkAtom format)
|
||
{
|
||
GList *formats;
|
||
|
||
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
|
||
g_return_if_fail (format != GDK_NONE);
|
||
|
||
formats = g_object_steal_qdata (G_OBJECT (buffer), deserialize_quark ());
|
||
|
||
formats = unregister_format (formats, format);
|
||
|
||
g_object_set_qdata_full (G_OBJECT (buffer), deserialize_quark (),
|
||
formats, (GDestroyNotify) free_format_list);
|
||
|
||
g_object_notify (G_OBJECT (buffer), "paste-target-list");
|
||
}
|
||
|
||
/**
|
||
* gtk_text_buffer_deserialize_set_can_create_tags:
|
||
* @buffer: a #GtkTextBuffer
|
||
* @format: a #GdkAtom representing a registered rich text format
|
||
* @can_create_tags: whether deserializing this format may create tags
|
||
*
|
||
* Use this function to allow a rich text deserialization function to
|
||
* create new tags in the receiving buffer. Note that using this
|
||
* function is almost always a bad idea, because the rich text
|
||
* functions you register should know how to map the rich text format
|
||
* they handler to your text buffers set of tags.
|
||
*
|
||
* The ability of creating new (arbitrary!) tags in the receiving buffer
|
||
* is meant for special rich text formats like the internal one that
|
||
* is registered using gtk_text_buffer_register_deserialize_tagset(),
|
||
* because that format is essentially a dump of the internal structure
|
||
* of the source buffer, including its tag names.
|
||
*
|
||
* You should allow creation of tags only if you know what you are
|
||
* doing, e.g. if you defined a tagset name for your application
|
||
* suite’s text buffers and you know that it’s fine to receive new
|
||
* tags from these buffers, because you know that your application can
|
||
* handle the newly created tags.
|
||
*
|
||
* Since: 2.10
|
||
**/
|
||
void
|
||
gtk_text_buffer_deserialize_set_can_create_tags (GtkTextBuffer *buffer,
|
||
GdkAtom format,
|
||
gboolean can_create_tags)
|
||
{
|
||
GList *formats;
|
||
GList *list;
|
||
gchar *format_name;
|
||
|
||
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
|
||
g_return_if_fail (format != GDK_NONE);
|
||
|
||
formats = g_object_get_qdata (G_OBJECT (buffer), deserialize_quark ());
|
||
|
||
for (list = formats; list; list = g_list_next (list))
|
||
{
|
||
GtkRichTextFormat *fmt = list->data;
|
||
|
||
if (fmt->atom == format)
|
||
{
|
||
fmt->can_create_tags = can_create_tags ? TRUE : FALSE;
|
||
return;
|
||
}
|
||
}
|
||
|
||
format_name = gdk_atom_name (format);
|
||
g_warning ("%s: \"%s\" is not registered as deserializable format "
|
||
"with text buffer %p",
|
||
G_STRFUNC, format_name ? format_name : "not a GdkAtom", buffer);
|
||
g_free (format_name);
|
||
}
|
||
|
||
/**
|
||
* gtk_text_buffer_deserialize_get_can_create_tags:
|
||
* @buffer: a #GtkTextBuffer
|
||
* @format: a #GdkAtom representing a registered rich text format
|
||
*
|
||
* This functions returns the value set with
|
||
* gtk_text_buffer_deserialize_set_can_create_tags()
|
||
*
|
||
* Returns: whether deserializing this format may create tags
|
||
*
|
||
* Since: 2.10
|
||
**/
|
||
gboolean
|
||
gtk_text_buffer_deserialize_get_can_create_tags (GtkTextBuffer *buffer,
|
||
GdkAtom format)
|
||
{
|
||
GList *formats;
|
||
GList *list;
|
||
gchar *format_name;
|
||
|
||
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
|
||
g_return_val_if_fail (format != GDK_NONE, FALSE);
|
||
|
||
formats = g_object_get_qdata (G_OBJECT (buffer), deserialize_quark ());
|
||
|
||
for (list = formats; list; list = g_list_next (list))
|
||
{
|
||
GtkRichTextFormat *fmt = list->data;
|
||
|
||
if (fmt->atom == format)
|
||
{
|
||
return fmt->can_create_tags;
|
||
}
|
||
}
|
||
|
||
format_name = gdk_atom_name (format);
|
||
g_warning ("%s: \"%s\" is not registered as deserializable format "
|
||
"with text buffer %p",
|
||
G_STRFUNC, format_name ? format_name : "not a GdkAtom", buffer);
|
||
g_free (format_name);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/**
|
||
* gtk_text_buffer_get_serialize_formats:
|
||
* @buffer: a #GtkTextBuffer
|
||
* @n_formats: (out): return location for the number of formats
|
||
*
|
||
* This function returns the rich text serialize formats registered
|
||
* with @buffer using gtk_text_buffer_register_serialize_format() or
|
||
* gtk_text_buffer_register_serialize_tagset()
|
||
*
|
||
* Returns: (array length=n_formats) (transfer container): an array of
|
||
* #GdkAtoms representing the registered formats.
|
||
*
|
||
* Since: 2.10
|
||
**/
|
||
GdkAtom *
|
||
gtk_text_buffer_get_serialize_formats (GtkTextBuffer *buffer,
|
||
gint *n_formats)
|
||
{
|
||
GList *formats;
|
||
|
||
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
|
||
g_return_val_if_fail (n_formats != NULL, NULL);
|
||
|
||
formats = g_object_get_qdata (G_OBJECT (buffer), serialize_quark ());
|
||
|
||
return get_formats (formats, n_formats);
|
||
}
|
||
|
||
/**
|
||
* gtk_text_buffer_get_deserialize_formats:
|
||
* @buffer: a #GtkTextBuffer
|
||
* @n_formats: (out): return location for the number of formats
|
||
*
|
||
* This function returns the rich text deserialize formats registered
|
||
* with @buffer using gtk_text_buffer_register_deserialize_format() or
|
||
* gtk_text_buffer_register_deserialize_tagset()
|
||
*
|
||
* Returns: (array length=n_formats) (transfer container): an array of
|
||
* #GdkAtoms representing the registered formats.
|
||
*
|
||
* Since: 2.10
|
||
**/
|
||
GdkAtom *
|
||
gtk_text_buffer_get_deserialize_formats (GtkTextBuffer *buffer,
|
||
gint *n_formats)
|
||
{
|
||
GList *formats;
|
||
|
||
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
|
||
g_return_val_if_fail (n_formats != NULL, NULL);
|
||
|
||
formats = g_object_get_qdata (G_OBJECT (buffer), deserialize_quark ());
|
||
|
||
return get_formats (formats, n_formats);
|
||
}
|
||
|
||
/**
|
||
* gtk_text_buffer_serialize:
|
||
* @register_buffer: the #GtkTextBuffer @format is registered with
|
||
* @content_buffer: the #GtkTextBuffer to serialize
|
||
* @format: the rich text format to use for serializing
|
||
* @start: start of block of text to serialize
|
||
* @end: end of block of test to serialize
|
||
* @length: (out): return location for the length of the serialized data
|
||
*
|
||
* This function serializes the portion of text between @start
|
||
* and @end in the rich text format represented by @format.
|
||
*
|
||
* @formats to be used must be registered using
|
||
* gtk_text_buffer_register_serialize_format() or
|
||
* gtk_text_buffer_register_serialize_tagset() beforehand.
|
||
*
|
||
* Returns: (array length=length) (transfer full): the serialized
|
||
* data, encoded as @format
|
||
*
|
||
* Since: 2.10
|
||
**/
|
||
guint8 *
|
||
gtk_text_buffer_serialize (GtkTextBuffer *register_buffer,
|
||
GtkTextBuffer *content_buffer,
|
||
GdkAtom format,
|
||
const GtkTextIter *start,
|
||
const GtkTextIter *end,
|
||
gsize *length)
|
||
{
|
||
GList *formats;
|
||
GList *list;
|
||
|
||
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (register_buffer), NULL);
|
||
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (content_buffer), NULL);
|
||
g_return_val_if_fail (format != GDK_NONE, NULL);
|
||
g_return_val_if_fail (start != NULL, NULL);
|
||
g_return_val_if_fail (end != NULL, NULL);
|
||
g_return_val_if_fail (length != NULL, NULL);
|
||
|
||
*length = 0;
|
||
|
||
formats = g_object_get_qdata (G_OBJECT (register_buffer),
|
||
serialize_quark ());
|
||
|
||
for (list = formats; list; list = g_list_next (list))
|
||
{
|
||
GtkRichTextFormat *fmt = list->data;
|
||
|
||
if (fmt->atom == format)
|
||
{
|
||
GtkTextBufferSerializeFunc function = fmt->function;
|
||
|
||
return function (register_buffer, content_buffer,
|
||
start, end, length, fmt->user_data);
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/**
|
||
* gtk_text_buffer_deserialize:
|
||
* @register_buffer: the #GtkTextBuffer @format is registered with
|
||
* @content_buffer: the #GtkTextBuffer to deserialize into
|
||
* @format: the rich text format to use for deserializing
|
||
* @iter: insertion point for the deserialized text
|
||
* @data: (array length=length): data to deserialize
|
||
* @length: length of @data
|
||
* @error: return location for a #GError
|
||
*
|
||
* This function deserializes rich text in format @format and inserts
|
||
* it at @iter.
|
||
*
|
||
* @formats to be used must be registered using
|
||
* gtk_text_buffer_register_deserialize_format() or
|
||
* gtk_text_buffer_register_deserialize_tagset() beforehand.
|
||
*
|
||
* Returns: %TRUE on success, %FALSE otherwise.
|
||
*
|
||
* Since: 2.10
|
||
**/
|
||
gboolean
|
||
gtk_text_buffer_deserialize (GtkTextBuffer *register_buffer,
|
||
GtkTextBuffer *content_buffer,
|
||
GdkAtom format,
|
||
GtkTextIter *iter,
|
||
const guint8 *data,
|
||
gsize length,
|
||
GError **error)
|
||
{
|
||
GList *formats;
|
||
GList *list;
|
||
|
||
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (register_buffer), FALSE);
|
||
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (content_buffer), FALSE);
|
||
g_return_val_if_fail (format != GDK_NONE, FALSE);
|
||
g_return_val_if_fail (iter != NULL, FALSE);
|
||
g_return_val_if_fail (data != NULL, FALSE);
|
||
g_return_val_if_fail (length > 0, FALSE);
|
||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||
|
||
formats = g_object_get_qdata (G_OBJECT (register_buffer),
|
||
deserialize_quark ());
|
||
|
||
for (list = formats; list; list = g_list_next (list))
|
||
{
|
||
GtkRichTextFormat *fmt = list->data;
|
||
|
||
if (fmt->atom == format)
|
||
{
|
||
GtkTextBufferDeserializeFunc function = fmt->function;
|
||
gboolean success;
|
||
GSList *split_tags;
|
||
GSList *list;
|
||
GtkTextMark *left_end = NULL;
|
||
GtkTextMark *right_start = NULL;
|
||
GSList *left_start_list = NULL;
|
||
GSList *right_end_list = NULL;
|
||
|
||
/* We don't want the tags that are effective at the insertion
|
||
* point to affect the pasted text, therefore we remove and
|
||
* remember them, so they can be re-applied left and right of
|
||
* the inserted text after pasting
|
||
*/
|
||
split_tags = gtk_text_iter_get_tags (iter);
|
||
|
||
list = split_tags;
|
||
while (list)
|
||
{
|
||
GtkTextTag *tag = list->data;
|
||
|
||
list = g_slist_next (list);
|
||
|
||
/* If a tag begins at the insertion point, ignore it
|
||
* because it doesn't affect the pasted text
|
||
*/
|
||
if (gtk_text_iter_begins_tag (iter, tag))
|
||
split_tags = g_slist_remove (split_tags, tag);
|
||
}
|
||
|
||
if (split_tags)
|
||
{
|
||
/* Need to remember text marks, because text iters
|
||
* don't survive pasting
|
||
*/
|
||
left_end = gtk_text_buffer_create_mark (content_buffer,
|
||
NULL, iter, TRUE);
|
||
right_start = gtk_text_buffer_create_mark (content_buffer,
|
||
NULL, iter, FALSE);
|
||
|
||
for (list = split_tags; list; list = g_slist_next (list))
|
||
{
|
||
GtkTextTag *tag = list->data;
|
||
GtkTextIter *backward_toggle = gtk_text_iter_copy (iter);
|
||
GtkTextIter *forward_toggle = gtk_text_iter_copy (iter);
|
||
GtkTextMark *left_start = NULL;
|
||
GtkTextMark *right_end = NULL;
|
||
|
||
gtk_text_iter_backward_to_tag_toggle (backward_toggle, tag);
|
||
left_start = gtk_text_buffer_create_mark (content_buffer,
|
||
NULL,
|
||
backward_toggle,
|
||
FALSE);
|
||
|
||
gtk_text_iter_forward_to_tag_toggle (forward_toggle, tag);
|
||
right_end = gtk_text_buffer_create_mark (content_buffer,
|
||
NULL,
|
||
forward_toggle,
|
||
TRUE);
|
||
|
||
left_start_list = g_slist_prepend (left_start_list, left_start);
|
||
right_end_list = g_slist_prepend (right_end_list, right_end);
|
||
|
||
gtk_text_buffer_remove_tag (content_buffer, tag,
|
||
backward_toggle,
|
||
forward_toggle);
|
||
|
||
gtk_text_iter_free (forward_toggle);
|
||
gtk_text_iter_free (backward_toggle);
|
||
}
|
||
|
||
left_start_list = g_slist_reverse (left_start_list);
|
||
right_end_list = g_slist_reverse (right_end_list);
|
||
}
|
||
|
||
success = function (register_buffer, content_buffer,
|
||
iter, data, length,
|
||
fmt->can_create_tags,
|
||
fmt->user_data,
|
||
error);
|
||
|
||
if (!success && error != NULL && *error == NULL)
|
||
g_set_error (error, 0, 0,
|
||
_("Unknown error when trying to deserialize %s"),
|
||
gdk_atom_name (format));
|
||
|
||
if (split_tags)
|
||
{
|
||
GSList *left_list;
|
||
GSList *right_list;
|
||
GtkTextIter left_e;
|
||
GtkTextIter right_s;
|
||
|
||
/* Turn the remembered marks back into iters so they
|
||
* can by used to re-apply the remembered tags
|
||
*/
|
||
gtk_text_buffer_get_iter_at_mark (content_buffer,
|
||
&left_e, left_end);
|
||
gtk_text_buffer_get_iter_at_mark (content_buffer,
|
||
&right_s, right_start);
|
||
|
||
for (list = split_tags,
|
||
left_list = left_start_list,
|
||
right_list = right_end_list;
|
||
list && left_list && right_list;
|
||
list = g_slist_next (list),
|
||
left_list = g_slist_next (left_list),
|
||
right_list = g_slist_next (right_list))
|
||
{
|
||
GtkTextTag *tag = list->data;
|
||
GtkTextMark *left_start = left_list->data;
|
||
GtkTextMark *right_end = right_list->data;
|
||
GtkTextIter left_s;
|
||
GtkTextIter right_e;
|
||
|
||
gtk_text_buffer_get_iter_at_mark (content_buffer,
|
||
&left_s, left_start);
|
||
gtk_text_buffer_get_iter_at_mark (content_buffer,
|
||
&right_e, right_end);
|
||
|
||
gtk_text_buffer_apply_tag (content_buffer, tag,
|
||
&left_s, &left_e);
|
||
gtk_text_buffer_apply_tag (content_buffer, tag,
|
||
&right_s, &right_e);
|
||
|
||
gtk_text_buffer_delete_mark (content_buffer, left_start);
|
||
gtk_text_buffer_delete_mark (content_buffer, right_end);
|
||
}
|
||
|
||
gtk_text_buffer_delete_mark (content_buffer, left_end);
|
||
gtk_text_buffer_delete_mark (content_buffer, right_start);
|
||
|
||
g_slist_free (split_tags);
|
||
g_slist_free (left_start_list);
|
||
g_slist_free (right_end_list);
|
||
}
|
||
|
||
return success;
|
||
}
|
||
}
|
||
|
||
g_set_error (error, 0, 0,
|
||
_("No deserialize function found for format %s"),
|
||
gdk_atom_name (format));
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
/* private functions */
|
||
|
||
static GList *
|
||
register_format (GList *formats,
|
||
const gchar *mime_type,
|
||
gpointer function,
|
||
gpointer user_data,
|
||
GDestroyNotify user_data_destroy,
|
||
GdkAtom *atom)
|
||
{
|
||
GtkRichTextFormat *format;
|
||
|
||
*atom = gdk_atom_intern (mime_type, FALSE);
|
||
|
||
formats = unregister_format (formats, *atom);
|
||
|
||
format = g_slice_new0 (GtkRichTextFormat);
|
||
|
||
format->mime_type = g_strdup (mime_type);
|
||
format->can_create_tags = FALSE;
|
||
format->atom = *atom;
|
||
format->function = function;
|
||
format->user_data = user_data;
|
||
format->user_data_destroy = user_data_destroy;
|
||
|
||
return g_list_append (formats, format);
|
||
}
|
||
|
||
static GList *
|
||
unregister_format (GList *formats,
|
||
GdkAtom atom)
|
||
{
|
||
GList *list;
|
||
|
||
for (list = formats; list; list = g_list_next (list))
|
||
{
|
||
GtkRichTextFormat *format = list->data;
|
||
|
||
if (format->atom == atom)
|
||
{
|
||
free_format (format);
|
||
|
||
return g_list_delete_link (formats, list);
|
||
}
|
||
}
|
||
|
||
return formats;
|
||
}
|
||
|
||
static GdkAtom *
|
||
get_formats (GList *formats,
|
||
gint *n_formats)
|
||
{
|
||
GdkAtom *array;
|
||
GList *list;
|
||
gint i;
|
||
|
||
*n_formats = g_list_length (formats);
|
||
array = g_new0 (GdkAtom, *n_formats);
|
||
|
||
for (list = formats, i = 0; list; list = g_list_next (list), i++)
|
||
{
|
||
GtkRichTextFormat *format = list->data;
|
||
|
||
array[i] = format->atom;
|
||
}
|
||
|
||
return array;
|
||
}
|
||
|
||
static void
|
||
free_format (GtkRichTextFormat *format)
|
||
{
|
||
if (format->user_data_destroy)
|
||
format->user_data_destroy (format->user_data);
|
||
|
||
g_free (format->mime_type);
|
||
g_slice_free (GtkRichTextFormat, format);
|
||
}
|
||
|
||
static void
|
||
free_format_list (GList *formats)
|
||
{
|
||
g_list_free_full (formats, (GDestroyNotify) free_format);
|
||
}
|
||
|
||
static GQuark
|
||
serialize_quark (void)
|
||
{
|
||
static GQuark quark = 0;
|
||
|
||
if (! quark)
|
||
quark = g_quark_from_static_string ("gtk-text-buffer-serialize-formats");
|
||
|
||
return quark;
|
||
}
|
||
|
||
static GQuark
|
||
deserialize_quark (void)
|
||
{
|
||
static GQuark quark = 0;
|
||
|
||
if (! quark)
|
||
quark = g_quark_from_static_string ("gtk-text-buffer-deserialize-formats");
|
||
|
||
return quark;
|
||
}
|