Add infrastructure for copy/paste and DND of rich text for GtkTextBuffer.

2006-03-07  Michael Natterer  <mitch@imendio.com>

	Add infrastructure for copy/paste and DND of rich text for
	GtkTextBuffer. Fixes bug #324177.

	* gtk/gtktextbufferrichtext.[ch]: new files implementing a
	per-buffer registry of rich text formats.

	* gtk/gtk.h: #include gtktextbufferrichtext.h

	* gtk/gtktextbufferserialize.[ch]: new files implementing an
	internal serialization format that can handle all of a text
	buffer's tags and pixbufs. It's not useful for anything except
	tranfer between instances of GtkTextBuffer (Anders Carlsson).

	* gtk/Makefile.am: build the new files.

	* gtk/gtkclipboard.[ch]: added convenience APIs for rich text,
	just as they exist for plain text and pixbufs.

	* gtk/gtkselection.[ch]: added rich text convenience APIs here
	too.  Return the target list from gtk_target_list_ref(). Register
	GtkTargetList as boxed type. Added
	gtk_target_table_new_from_list() and gtk_target_table_free(),
	which make converting between GtkTargetList and arrays of
	GtkTargetEntry considerably easier.

	* gtk/gtktextutil.[ch]: added _gtk_text_util_create_rich_drag_icon()
	which creates a fancy rich text icon (Matthias Clasen).

	* gtk/gtktextbuffer.[ch]: use all the new stuff above and
	implement copy and paste of rich text. Added APIs for getting the
	target lists used for copy and paste. Added public enum
	GtkTextBufferTargetInfo which contains the "info" IDs associated
	with the entries of the target lists.

	* gtk/gtktextview.c: use the new rich text APIs and
	GtkTextBuffer's new target list API to enable DND of rich text
	chunks.

	* gtk/gtk.symbols: export all the new symbols added.

	* tests/testtext.c: added rich text testing stuff.
This commit is contained in:
Michael Natterer 2006-03-07 13:46:11 +00:00 committed by Michael Natterer
parent 1f5c294851
commit 6c1d990adc
19 changed files with 4252 additions and 194 deletions

View File

@ -1,3 +1,47 @@
2006-03-07 Michael Natterer <mitch@imendio.com>
Add infrastructure for copy/paste and DND of rich text for
GtkTextBuffer. Fixes bug #324177.
* gtk/gtktextbufferrichtext.[ch]: new files implementing a
per-buffer registry of rich text formats.
* gtk/gtk.h: #include gtktextbufferrichtext.h
* gtk/gtktextbufferserialize.[ch]: new files implementing an
internal serialization format that can handle all of a text
buffer's tags and pixbufs. It's not useful for anything except
tranfer between instances of GtkTextBuffer (Anders Carlsson).
* gtk/Makefile.am: build the new files.
* gtk/gtkclipboard.[ch]: added convenience APIs for rich text,
just as they exist for plain text and pixbufs.
* gtk/gtkselection.[ch]: added rich text convenience APIs here
too. Return the target list from gtk_target_list_ref(). Register
GtkTargetList as boxed type. Added
gtk_target_table_new_from_list() and gtk_target_table_free(),
which make converting between GtkTargetList and arrays of
GtkTargetEntry considerably easier.
* gtk/gtktextutil.[ch]: added _gtk_text_util_create_rich_drag_icon()
which creates a fancy rich text icon (Matthias Clasen).
* gtk/gtktextbuffer.[ch]: use all the new stuff above and
implement copy and paste of rich text. Added APIs for getting the
target lists used for copy and paste. Added public enum
GtkTextBufferTargetInfo which contains the "info" IDs associated
with the entries of the target lists.
* gtk/gtktextview.c: use the new rich text APIs and
GtkTextBuffer's new target list API to enable DND of rich text
chunks.
* gtk/gtk.symbols: export all the new symbols added.
* tests/testtext.c: added rich text testing stuff.
2006-03-06 Matthias Clasen <mclasen@redhat.com>
* gtk/gtktextview.c (text_window_invalidate_cursors): Take

View File

@ -1,3 +1,47 @@
2006-03-07 Michael Natterer <mitch@imendio.com>
Add infrastructure for copy/paste and DND of rich text for
GtkTextBuffer. Fixes bug #324177.
* gtk/gtktextbufferrichtext.[ch]: new files implementing a
per-buffer registry of rich text formats.
* gtk/gtk.h: #include gtktextbufferrichtext.h
* gtk/gtktextbufferserialize.[ch]: new files implementing an
internal serialization format that can handle all of a text
buffer's tags and pixbufs. It's not useful for anything except
tranfer between instances of GtkTextBuffer (Anders Carlsson).
* gtk/Makefile.am: build the new files.
* gtk/gtkclipboard.[ch]: added convenience APIs for rich text,
just as they exist for plain text and pixbufs.
* gtk/gtkselection.[ch]: added rich text convenience APIs here
too. Return the target list from gtk_target_list_ref(). Register
GtkTargetList as boxed type. Added
gtk_target_table_new_from_list() and gtk_target_table_free(),
which make converting between GtkTargetList and arrays of
GtkTargetEntry considerably easier.
* gtk/gtktextutil.[ch]: added _gtk_text_util_create_rich_drag_icon()
which creates a fancy rich text icon (Matthias Clasen).
* gtk/gtktextbuffer.[ch]: use all the new stuff above and
implement copy and paste of rich text. Added APIs for getting the
target lists used for copy and paste. Added public enum
GtkTextBufferTargetInfo which contains the "info" IDs associated
with the entries of the target lists.
* gtk/gtktextview.c: use the new rich text APIs and
GtkTextBuffer's new target list API to enable DND of rich text
chunks.
* gtk/gtk.symbols: export all the new symbols added.
* tests/testtext.c: added rich text testing stuff.
2006-03-06 Matthias Clasen <mclasen@redhat.com>
* gtk/gtktextview.c (text_window_invalidate_cursors): Take

View File

@ -248,6 +248,7 @@ gtk_public_h_sources = \
gtktearoffmenuitem.h \
gtktext.h \
gtktextbuffer.h \
gtktextbufferrichtext.h \
gtktextchild.h \
gtktextdisplay.h \
gtktextiter.h \
@ -481,6 +482,9 @@ gtk_c_sources = \
gtktext.c \
gtktextbtree.c \
gtktextbuffer.c \
gtktextbufferrichtext.c \
gtktextbufferserialize.c\
gtktextbufferserialize.h\
gtktextchild.c \
gtktextdisplay.c \
gtktextiter.c \

View File

@ -160,6 +160,7 @@
#include <gtk/gtktearoffmenuitem.h>
#include <gtk/gtktext.h>
#include <gtk/gtktextbuffer.h>
#include <gtk/gtktextbufferrichtext.h>
#include <gtk/gtktextview.h>
#include <gtk/gtktipsquery.h>
#include <gtk/gtktoggleaction.h>

View File

@ -600,6 +600,7 @@ gtk_clipboard_get_owner
gtk_clipboard_get_type G_GNUC_CONST
gtk_clipboard_request_contents
gtk_clipboard_request_image
gtk_clipboard_request_rich_text
gtk_clipboard_request_targets
gtk_clipboard_request_text
gtk_clipboard_set_can_store
@ -610,9 +611,11 @@ gtk_clipboard_set_with_owner
gtk_clipboard_store
gtk_clipboard_wait_for_contents
gtk_clipboard_wait_for_image
gtk_clipboard_wait_for_rich_text
gtk_clipboard_wait_for_targets
gtk_clipboard_wait_for_text
gtk_clipboard_wait_is_image_available
gtk_clipboard_wait_is_rich_text_available
gtk_clipboard_wait_is_text_available
gtk_clipboard_wait_is_target_available
#endif
@ -2607,6 +2610,23 @@ gtk_rc_style_unref
#endif
#endif
#if IN_HEADER(__GTK_TEXT_BUFFER_RICH_TEXT_H__)
#if IN_FILE(__GTK_TEXT_BUFFER_RICH_TEXT_C__)
gtk_text_buffer_deserialize
gtk_text_buffer_deserialize_get_can_create_tags
gtk_text_buffer_deserialize_set_can_create_tags
gtk_text_buffer_get_deserialize_formats
gtk_text_buffer_get_serialize_formats
gtk_text_buffer_register_deserialize_format
gtk_text_buffer_register_deserialize_tagset
gtk_text_buffer_register_serialize_format
gtk_text_buffer_register_serialize_tagset
gtk_text_buffer_serialize
gtk_text_buffer_unregister_deserialize_format
gtk_text_buffer_unregister_serialize_format
#endif
#endif
#if IN_HEADER(__GTK_RULER_H__)
#if IN_FILE(__GTK_RULER_C__)
gtk_ruler_draw_pos
@ -2678,9 +2698,11 @@ gtk_selection_data_set_pixbuf
gtk_selection_data_set_text
gtk_selection_data_set_uris
gtk_selection_data_targets_include_image
gtk_selection_data_targets_include_rich_text
gtk_selection_data_targets_include_text
gtk_selection_data_targets_include_uri
gtk_targets_include_image
gtk_targets_include_rich_text
gtk_targets_include_text
gtk_targets_include_uri
gtk_selection_owner_set
@ -2688,14 +2710,18 @@ gtk_selection_owner_set_for_display
gtk_selection_remove_all
gtk_target_list_add
gtk_target_list_add_image_targets
gtk_target_list_add_rich_text_targets
gtk_target_list_add_table
gtk_target_list_add_text_targets
gtk_target_list_add_uri_targets
gtk_target_list_find
gtk_target_list_get_type G_GNUC_CONST
gtk_target_list_new
gtk_target_list_ref
gtk_target_list_remove
gtk_target_list_unref
gtk_target_table_new_from_list
gtk_target_table_free
#endif
#endif
@ -2934,6 +2960,7 @@ gtk_text_buffer_delete_selection
gtk_text_buffer_end_user_action
gtk_text_buffer_get_bounds
gtk_text_buffer_get_char_count
gtk_text_buffer_get_copy_target_list
gtk_text_buffer_get_end_iter
gtk_text_buffer_get_has_selection
gtk_text_buffer_get_insert
@ -2946,6 +2973,7 @@ gtk_text_buffer_get_iter_at_offset
gtk_text_buffer_get_line_count
gtk_text_buffer_get_mark
gtk_text_buffer_get_modified
gtk_text_buffer_get_paste_target_list
gtk_text_buffer_get_selection_bound
gtk_text_buffer_get_selection_bounds
gtk_text_buffer_get_slice

View File

@ -27,6 +27,7 @@
#include "gtkinvisible.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtktextbufferrichtext.h"
#include "gtkintl.h"
#include "gtkalias.h"
@ -47,6 +48,7 @@ typedef struct _GtkClipboardClass GtkClipboardClass;
typedef struct _RequestContentsInfo RequestContentsInfo;
typedef struct _RequestTextInfo RequestTextInfo;
typedef struct _RequestRichTextInfo RequestRichTextInfo;
typedef struct _RequestImageInfo RequestImageInfo;
typedef struct _RequestTargetsInfo RequestTargetsInfo;
@ -97,6 +99,15 @@ struct _RequestTextInfo
gpointer user_data;
};
struct _RequestRichTextInfo
{
GtkClipboardRichTextReceivedFunc callback;
GdkAtom *atoms;
gint n_atoms;
gint current_atom;
gpointer user_data;
};
struct _RequestImageInfo
{
GtkClipboardImageReceivedFunc callback;
@ -927,7 +938,7 @@ request_text_received_func (GtkClipboard *clipboard,
RequestTextInfo *info = data;
gchar *result = NULL;
result = gtk_selection_data_get_text (selection_data);
result = (gchar *) gtk_selection_data_get_text (selection_data);
if (!result)
{
@ -992,6 +1003,80 @@ gtk_clipboard_request_text (GtkClipboard *clipboard,
info);
}
static void
request_rich_text_received_func (GtkClipboard *clipboard,
GtkSelectionData *selection_data,
gpointer data)
{
RequestRichTextInfo *info = data;
guint8 *result = NULL;
gsize length = 0;
result = selection_data->data;
length = selection_data->length;
info->current_atom++;
if ((!result || length < 1) && (info->current_atom < info->n_atoms))
{
gtk_clipboard_request_contents (clipboard, info->atoms[info->current_atom],
request_rich_text_received_func,
info);
return;
}
info->callback (clipboard, selection_data->target, result, length,
info->user_data);
g_free (info->atoms);
g_free (info);
}
/**
* gtk_clipboard_request_rich_text:
* @clipboard: a #GtkClipboard
* @buffer: a #GtkTextBuffer
* @callback: a function to call when the text is received,
* or the retrieval fails. (It will always be called
* one way or the other.)
* @user_data: user data to pass to @callback.
*
* Requests the contents of the clipboard as rich text. When the rich
* text is later received, @callback will be called.
*
* The @text parameter to @callback will contain the resulting rich
* text if the request succeeded, or %NULL if it failed. The @length
* parameter will contain @text's length. This function can fail for
* various reasons, in particular if the clipboard was empty or if the
* contents of the clipboard could not be converted into rich text form.
*
* Since: 2.10
**/
void
gtk_clipboard_request_rich_text (GtkClipboard *clipboard,
GtkTextBuffer *buffer,
GtkClipboardRichTextReceivedFunc callback,
gpointer user_data)
{
RequestRichTextInfo *info;
g_return_if_fail (clipboard != NULL);
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
g_return_if_fail (callback != NULL);
info = g_new (RequestRichTextInfo, 1);
info->callback = callback;
info->atoms = NULL;
info->n_atoms = 0;
info->current_atom = 0;
info->user_data = user_data;
info->atoms = gtk_text_buffer_get_deserialize_formats (buffer, &info->n_atoms);
gtk_clipboard_request_contents (clipboard, info->atoms[info->current_atom],
request_rich_text_received_func,
info);
}
static void
request_image_received_func (GtkClipboard *clipboard,
GtkSelectionData *selection_data,
@ -1143,6 +1228,8 @@ typedef struct
{
GMainLoop *loop;
gpointer data;
GdkAtom format; /* used by rich text */
gsize length; /* used by rich text */
} WaitResults;
static void
@ -1254,6 +1341,75 @@ gtk_clipboard_wait_for_text (GtkClipboard *clipboard)
return results.data;
}
static void
clipboard_rich_text_received_func (GtkClipboard *clipboard,
GdkAtom format,
const guint8 *text,
gsize length,
gpointer data)
{
WaitResults *results = data;
results->data = g_memdup (text, length);
results->format = format;
results->length = length;
g_main_loop_quit (results->loop);
}
/**
* gtk_clipboard_wait_for_rich_text:
* @clipboard: a #GtkClipboard
* @buffer: a #GtkTextBuffer
* @length: return location for the length of the returned data
*
* Requests the contents of the clipboard as rich text. This function
* waits for the data to be received using the main loop, so events,
* timeouts, etc, may be dispatched during the wait.
*
* Return value: a newly-allocated binary block of data which must
* be freed with g_free(), or %NULL if retrieving
* the selection data failed. (This could happen
* for various reasons, in particular if the
* clipboard was empty or if the contents of the
* clipboard could not be converted into text form.)
*
* Since: 2.10
**/
guint8 *
gtk_clipboard_wait_for_rich_text (GtkClipboard *clipboard,
GtkTextBuffer *buffer,
GdkAtom *format,
gsize *length)
{
WaitResults results;
g_return_val_if_fail (clipboard != NULL, NULL);
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
g_return_val_if_fail (format != NULL, NULL);
g_return_val_if_fail (length != NULL, NULL);
results.data = NULL;
results.loop = g_main_loop_new (NULL, TRUE);
gtk_clipboard_request_rich_text (clipboard, buffer,
clipboard_rich_text_received_func,
&results);
if (g_main_loop_is_running (results.loop))
{
GDK_THREADS_LEAVE ();
g_main_loop_run (results.loop);
GDK_THREADS_ENTER ();
}
g_main_loop_unref (results.loop);
*format = results.format;
*length = results.length;
return results.data;
}
static void
clipboard_image_received_func (GtkClipboard *clipboard,
GdkPixbuf *pixbuf,
@ -1361,6 +1517,45 @@ gtk_clipboard_wait_is_text_available (GtkClipboard *clipboard)
return result;
}
/**
* gtk_clipboard_wait_is_rich_text_available:
* @clipboard: a #GtkClipboard
* @buffer: a #GtkTextBuffer
*
* Test to see if there is rich text available to be pasted
* This is done by requesting the TARGETS atom and checking
* if it contains any of the supported rich text targets. This function
* waits for the data to be received using the main loop, so events,
* timeouts, etc, may be dispatched during the wait.
*
* This function is a little faster than calling
* gtk_clipboard_wait_for_rich_text() since it doesn't need to retrieve
* the actual text.
*
* Return value: %TRUE is there is rich text available, %FALSE otherwise.
*
* Since: 2.10
**/
gboolean
gtk_clipboard_wait_is_rich_text_available (GtkClipboard *clipboard,
GtkTextBuffer *buffer)
{
GtkSelectionData *data;
gboolean result = FALSE;
g_return_val_if_fail (GTK_IS_CLIPBOARD (clipboard), FALSE);
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern_static_string ("TARGETS"));
if (data)
{
result = gtk_selection_data_targets_include_rich_text (data, buffer);
gtk_selection_data_free (data);
}
return result;
}
/**
* gtk_clipboard_wait_is_image_available:
* @clipboard: a #GtkClipboard

View File

@ -30,19 +30,24 @@ G_BEGIN_DECLS
#define GTK_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CLIPBOARD, GtkClipboard))
#define GTK_IS_CLIPBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CLIPBOARD))
typedef void (* GtkClipboardReceivedFunc) (GtkClipboard *clipboard,
GtkSelectionData *selection_data,
gpointer data);
typedef void (* GtkClipboardTextReceivedFunc) (GtkClipboard *clipboard,
const gchar *text,
gpointer data);
typedef void (* GtkClipboardImageReceivedFunc) (GtkClipboard *clipboard,
GdkPixbuf *pixbuf,
gpointer data);
typedef void (* GtkClipboardTargetsReceivedFunc) (GtkClipboard *clipboard,
GdkAtom *atoms,
gint n_atoms,
gpointer data);
typedef void (* GtkClipboardReceivedFunc) (GtkClipboard *clipboard,
GtkSelectionData *selection_data,
gpointer data);
typedef void (* GtkClipboardTextReceivedFunc) (GtkClipboard *clipboard,
const gchar *text,
gpointer data);
typedef void (* GtkClipboardRichTextReceivedFunc) (GtkClipboard *clipboard,
GdkAtom format,
const guint8 *text,
gsize length,
gpointer data);
typedef void (* GtkClipboardImageReceivedFunc) (GtkClipboard *clipboard,
GdkPixbuf *pixbuf,
gpointer data);
typedef void (* GtkClipboardTargetsReceivedFunc) (GtkClipboard *clipboard,
GdkAtom *atoms,
gint n_atoms,
gpointer data);
/* Should these functions have GtkClipboard *clipboard as the first argument?
* right now for ClearFunc, you may have trouble determining _which_ clipboard
@ -86,32 +91,42 @@ void gtk_clipboard_set_text (GtkClipboard *clipboard,
void gtk_clipboard_set_image (GtkClipboard *clipboard,
GdkPixbuf *pixbuf);
void gtk_clipboard_request_contents (GtkClipboard *clipboard,
GdkAtom target,
GtkClipboardReceivedFunc callback,
gpointer user_data);
void gtk_clipboard_request_text (GtkClipboard *clipboard,
GtkClipboardTextReceivedFunc callback,
gpointer user_data);
void gtk_clipboard_request_image (GtkClipboard *clipboard,
GtkClipboardImageReceivedFunc callback,
gpointer user_data);
void gtk_clipboard_request_targets (GtkClipboard *clipboard,
GtkClipboardTargetsReceivedFunc callback,
gpointer user_data);
void gtk_clipboard_request_contents (GtkClipboard *clipboard,
GdkAtom target,
GtkClipboardReceivedFunc callback,
gpointer user_data);
void gtk_clipboard_request_text (GtkClipboard *clipboard,
GtkClipboardTextReceivedFunc callback,
gpointer user_data);
void gtk_clipboard_request_rich_text (GtkClipboard *clipboard,
GtkTextBuffer *buffer,
GtkClipboardRichTextReceivedFunc callback,
gpointer user_data);
void gtk_clipboard_request_image (GtkClipboard *clipboard,
GtkClipboardImageReceivedFunc callback,
gpointer user_data);
void gtk_clipboard_request_targets (GtkClipboard *clipboard,
GtkClipboardTargetsReceivedFunc callback,
gpointer user_data);
GtkSelectionData *gtk_clipboard_wait_for_contents (GtkClipboard *clipboard,
GdkAtom target);
gchar * gtk_clipboard_wait_for_text (GtkClipboard *clipboard);
GdkPixbuf * gtk_clipboard_wait_for_image (GtkClipboard *clipboard);
gboolean gtk_clipboard_wait_for_targets (GtkClipboard *clipboard,
GdkAtom **targets,
gint *n_targets);
GtkSelectionData *gtk_clipboard_wait_for_contents (GtkClipboard *clipboard,
GdkAtom target);
gchar * gtk_clipboard_wait_for_text (GtkClipboard *clipboard);
guint8 * gtk_clipboard_wait_for_rich_text (GtkClipboard *clipboard,
GtkTextBuffer *buffer,
GdkAtom *format,
gsize *size);
GdkPixbuf * gtk_clipboard_wait_for_image (GtkClipboard *clipboard);
gboolean gtk_clipboard_wait_for_targets (GtkClipboard *clipboard,
GdkAtom **targets,
gint *n_targets);
gboolean gtk_clipboard_wait_is_text_available (GtkClipboard *clipboard);
gboolean gtk_clipboard_wait_is_image_available (GtkClipboard *clipboard);
gboolean gtk_clipboard_wait_is_target_available (GtkClipboard *clipboard,
GdkAtom target);
gboolean gtk_clipboard_wait_is_text_available (GtkClipboard *clipboard);
gboolean gtk_clipboard_wait_is_rich_text_available (GtkClipboard *clipboard,
GtkTextBuffer *buffer);
gboolean gtk_clipboard_wait_is_image_available (GtkClipboard *clipboard);
gboolean gtk_clipboard_wait_is_target_available (GtkClipboard *clipboard,
GdkAtom target);
void gtk_clipboard_set_can_store (GtkClipboard *clipboard,

View File

@ -58,6 +58,7 @@
#include "gtkmain.h"
#include "gtkselection.h"
#include "gtktextbufferrichtext.h"
#include "gtkintl.h"
#include "gdk-pixbuf/gdk-pixbuf.h"
@ -213,13 +214,16 @@ gtk_target_list_new (const GtkTargetEntry *targets,
*
* Increases the reference count of a #GtkTargetList by one.
*
* Return value: the passed in #GtkTargetList.
**/
void
GtkTargetList *
gtk_target_list_ref (GtkTargetList *list)
{
g_return_if_fail (list != NULL);
g_return_val_if_fail (list != NULL, NULL);
list->ref_count++;
return list;
}
/**
@ -338,6 +342,45 @@ gtk_target_list_add_text_targets (GtkTargetList *list,
gtk_target_list_add (list, text_plain_atom, 0, info);
}
/**
* gtk_target_list_add_rich_text_targets:
* @list: a #GtkTargetList
* @info: an ID that will be passed back to the application
* @deserializable: if %TRUE, then deserializable rich text formats
* will be added, serializable formats otherwise.
* @buffer: a #GtkTextBuffer.
*
* Appends the rich text targets registered with
* gtk_text_buffer_register_serialize_format() or
* gtk_text_buffer_register_deserialize_format() to the target list. All
* targets are added with the same @info.
*
* Since: 2.10
**/
void
gtk_target_list_add_rich_text_targets (GtkTargetList *list,
guint info,
gboolean deserializable,
GtkTextBuffer *buffer)
{
GdkAtom *atoms;
gint n_atoms;
gint i;
g_return_if_fail (list != NULL);
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
if (deserializable)
atoms = gtk_text_buffer_get_deserialize_formats (buffer, &n_atoms);
else
atoms = gtk_text_buffer_get_serialize_formats (buffer, &n_atoms);
for (i = 0; i < n_atoms; i++)
gtk_target_list_add (list, atoms[i], 0, info);
g_free (atoms);
}
/**
* gtk_target_list_add_image_targets:
* @list: a #GtkTargetList
@ -514,6 +557,72 @@ gtk_target_list_find (GtkTargetList *list,
return FALSE;
}
/**
* gtk_target_table_new_from_list:
* @list: a #GtkTargetList
* @n_targets: return location for the number ot targets in the table
*
* This function creates an #GtkTargetEntry array that contains the
* same targets as the passed %list. The returned table is newly
* allocated and should be freed using gtk_target_table_free() when no
* longer needed.
*
* Return value: the new table.
*
* Since: 2.10
**/
GtkTargetEntry *
gtk_target_table_new_from_list (GtkTargetList *list,
gint *n_targets)
{
GtkTargetEntry *targets;
GList *tmp_list;
gint i;
g_return_val_if_fail (list != NULL, NULL);
g_return_val_if_fail (n_targets != NULL, NULL);
*n_targets = g_list_length (list->list);
targets = g_new0 (GtkTargetEntry, *n_targets);
for (i = 0, tmp_list = list->list;
i < *n_targets;
i++, tmp_list = g_list_next (tmp_list))
{
GtkTargetPair *pair = tmp_list->data;
targets[i].target = gdk_atom_name (pair->target);
targets[i].flags = pair->flags;
targets[i].info = pair->info;
}
return targets;
}
/**
* gtk_target_table_free:
* @targets: a #GtkTargetEntry array
* @n_targets: the number of entries in the array
*
* This function frees a target table as returned by
* gtk_target_table_new_from_list()
*
* Since: 2.10
**/
void
gtk_target_table_free (GtkTargetEntry *targets,
gint n_targets)
{
gint i;
g_return_if_fail (targets == NULL || n_targets > 0);
for (i = 0; i < n_targets; i++)
g_free (targets[i].target);
g_free (targets);
}
/**
* gtk_selection_owner_set_for_display:
* @display: the #Gdkdisplay where the selection is set
@ -1608,7 +1717,54 @@ gtk_targets_include_text (GdkAtom *targets,
return result;
}
/**
* gtk_targets_include_rich_text:
* @targets: an array of #GdkAtom<!-- -->s
* @n_targets: the length of @targets
* @buffer: a #GtkTextBuffer
*
* Determines if any of the targets in @targets can be used to
* provide rich text.
*
* Return value: %TRUE if @targets include a suitable target for rich text,
* otherwise %FALSE.
*
* Since: 2.10
**/
gboolean
gtk_targets_include_rich_text (GdkAtom *targets,
gint n_targets,
GtkTextBuffer *buffer)
{
GdkAtom *rich_targets;
gint n_rich_targets;
gint i, j;
gboolean result = FALSE;
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
rich_targets = gtk_text_buffer_get_deserialize_formats (buffer,
&n_rich_targets);
for (i = 0; i < n_targets; i++)
{
for (j = 0; j < n_rich_targets; j++)
{
if (targets[i] == rich_targets[j])
{
result = TRUE;
goto done;
}
}
}
done:
g_free (rich_targets);
return result;
}
/**
* gtk_selection_data_targets_include_text:
* @selection_data: a #GtkSelectionData object
@ -1638,6 +1794,42 @@ gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
return result;
}
/**
* gtk_selection_data_targets_include_rich_text:
* @selection_data: a #GtkSelectionData object
* @buffer: a #GtkTextBuffer
*
* Given a #GtkSelectionData object holding a list of targets,
* determines if any of the targets in @targets can be used to
* provide rich text.
*
* Return value: %TRUE if @selection_data holds a list of targets,
* and a suitable target for rich text is included,
* otherwise %FALSE.
*
* Since: 2.10
**/
gboolean
gtk_selection_data_targets_include_rich_text (GtkSelectionData *selection_data,
GtkTextBuffer *buffer)
{
GdkAtom *targets;
gint n_targets;
gboolean result = FALSE;
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
init_atoms ();
if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
{
result = gtk_targets_include_rich_text (targets, n_targets, buffer);
g_free (targets);
}
return result;
}
/**
* gtk_targets_include_image:
* @targets: an array of #GdkAtom<!-- -->s
@ -2756,6 +2948,19 @@ gtk_selection_data_get_type (void)
return our_type;
}
GType
gtk_target_list_get_type (void)
{
static GType our_type = 0;
if (our_type == 0)
our_type = g_boxed_type_register_static (I_("GtkTargetList"),
(GBoxedCopyFunc) gtk_target_list_ref,
(GBoxedFreeFunc) gtk_target_list_unref);
return our_type;
}
static int
gtk_selection_bytes_per_item (gint format)
{

View File

@ -31,6 +31,7 @@
#include <gdk/gdk.h>
#include <gtk/gtkenums.h>
#include <gtk/gtkwidget.h>
#include <gtk/gtktextiter.h>
G_BEGIN_DECLS
@ -38,6 +39,7 @@ typedef struct _GtkTargetList GtkTargetList;
typedef struct _GtkTargetEntry GtkTargetEntry;
#define GTK_TYPE_SELECTION_DATA (gtk_selection_data_get_type ())
#define GTK_TYPE_TARGET_LIST (gtk_target_list_get_type ())
/* The contents of a selection are returned in a GtkSelectionData
* structure. selection/target identify the request. type specifies
@ -87,19 +89,23 @@ struct _GtkTargetPair {
GtkTargetList *gtk_target_list_new (const GtkTargetEntry *targets,
guint ntargets);
void gtk_target_list_ref (GtkTargetList *list);
GtkTargetList *gtk_target_list_ref (GtkTargetList *list);
void gtk_target_list_unref (GtkTargetList *list);
void gtk_target_list_add (GtkTargetList *list,
GdkAtom target,
guint flags,
guint info);
void gtk_target_list_add_text_targets (GtkTargetList *list,
guint info);
void gtk_target_list_add_image_targets (GtkTargetList *list,
guint info,
gboolean writable);
void gtk_target_list_add_uri_targets (GtkTargetList *list,
guint info);
void gtk_target_list_add_text_targets (GtkTargetList *list,
guint info);
void gtk_target_list_add_rich_text_targets (GtkTargetList *list,
guint info,
gboolean deserializable,
GtkTextBuffer *buffer);
void gtk_target_list_add_image_targets (GtkTargetList *list,
guint info,
gboolean writable);
void gtk_target_list_add_uri_targets (GtkTargetList *list,
guint info);
void gtk_target_list_add_table (GtkTargetList *list,
const GtkTargetEntry *targets,
guint ntargets);
@ -109,6 +115,11 @@ gboolean gtk_target_list_find (GtkTargetList *list,
GdkAtom target,
guint *info);
GtkTargetEntry * gtk_target_table_new_from_list (GtkTargetList *list,
gint *n_targets);
void gtk_target_table_free (GtkTargetEntry *targets,
gint n_targets);
/* Public interface */
gboolean gtk_selection_owner_set (GtkWidget *widget,
@ -153,16 +164,21 @@ gboolean gtk_selection_data_get_targets (GtkSelectionData *selection_d
GdkAtom **targets,
gint *n_atoms);
gboolean gtk_selection_data_targets_include_text (GtkSelectionData *selection_data);
gboolean gtk_selection_data_targets_include_rich_text (GtkSelectionData *selection_data,
GtkTextBuffer *buffer);
gboolean gtk_selection_data_targets_include_image (GtkSelectionData *selection_data,
gboolean writable);
gboolean gtk_selection_data_targets_include_uri (GtkSelectionData *selection_data);
gboolean gtk_targets_include_text (GdkAtom *targets,
gint n_targets);
gboolean gtk_targets_include_image (GdkAtom *targets,
gint n_targets,
gboolean writable);
gboolean gtk_targets_include_uri (GdkAtom *targets,
gint n_targets);
gboolean gtk_targets_include_text (GdkAtom *targets,
gint n_targets);
gboolean gtk_targets_include_rich_text (GdkAtom *targets,
gint n_targets,
GtkTextBuffer *buffer);
gboolean gtk_targets_include_image (GdkAtom *targets,
gint n_targets,
gboolean writable);
gboolean gtk_targets_include_uri (GdkAtom *targets,
gint n_targets);
/* Called when a widget is destroyed */
@ -186,6 +202,7 @@ GType gtk_selection_data_get_type (void) G_GNUC_CONST;
GtkSelectionData *gtk_selection_data_copy (GtkSelectionData *data);
void gtk_selection_data_free (GtkSelectionData *data);
GType gtk_target_list_get_type (void) G_GNUC_CONST;
G_END_DECLS

View File

@ -1,5 +1,6 @@
/* GTK - The GIMP Toolkit
* gtktextbuffer.c Copyright (C) 2000 Red Hat, Inc.
* Copyright (C) 2004 Nokia Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -24,17 +25,17 @@
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include <config.h>
#include <string.h>
#include <stdarg.h>
#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
#include "gtkclipboard.h"
#include "gtkdnd.h"
#include "gtkinvisible.h"
#include "gtkmarshalers.h"
#include "gtktextbuffer.h"
#include "gtktextbufferrichtext.h"
#include "gtktextbtree.h"
#include "gtktextiterprivate.h"
#include "gtkprivate.h"
@ -42,6 +43,22 @@
#include "gtkalias.h"
#define GTK_TEXT_BUFFER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_TEXT_BUFFER, GtkTextBufferPrivate))
typedef struct _GtkTextBufferPrivate GtkTextBufferPrivate;
struct _GtkTextBufferPrivate
{
GtkTargetList *copy_target_list;
GtkTargetEntry *copy_target_entries;
gint n_copy_target_entries;
GtkTargetList *paste_target_list;
GtkTargetEntry *paste_target_entries;
gint n_paste_target_entries;
};
typedef struct _ClipboardRequest ClipboardRequest;
struct _ClipboardRequest
@ -74,25 +91,18 @@ enum {
/* Construct */
PROP_TAG_TABLE,
/* Normal */
PROP_TEXT,
PROP_HAS_SELECTION
};
enum {
TARGET_STRING,
TARGET_TEXT,
TARGET_COMPOUND_TEXT,
TARGET_UTF8_STRING,
TARGET_TEXT_BUFFER_CONTENTS
PROP_HAS_SELECTION,
PROP_COPY_TARGET_LIST,
PROP_PASTE_TARGET_LIST
};
static void gtk_text_buffer_init (GtkTextBuffer *tkxt_buffer);
static void gtk_text_buffer_class_init (GtkTextBufferClass *klass);
static void gtk_text_buffer_finalize (GObject *object);
static void gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer,
GtkTextIter *iter,
const gchar *text,
@ -127,6 +137,8 @@ static void update_selection_clipboards (GtkTextBuffer *buffer);
static GtkTextBuffer *create_clipboard_contents_buffer (GtkTextBuffer *buffer);
static void gtk_text_buffer_free_target_lists (GtkTextBuffer *buffer);
static GObjectClass *parent_class = NULL;
static guint signals[LAST_SIGNAL] = { 0 };
@ -138,6 +150,8 @@ static void gtk_text_buffer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void gtk_text_buffer_notify (GObject *object,
GParamSpec *pspec);
GType
@ -177,6 +191,7 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass)
object_class->finalize = gtk_text_buffer_finalize;
object_class->set_property = gtk_text_buffer_set_property;
object_class->get_property = gtk_text_buffer_get_property;
object_class->notify = gtk_text_buffer_notify;
klass->insert_text = gtk_text_buffer_real_insert_text;
klass->insert_pixbuf = gtk_text_buffer_real_insert_pixbuf;
@ -227,7 +242,39 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass)
P_("Has selection"),
P_("Whether the buffer has some text currently selected"),
FALSE,
G_PARAM_READABLE));
GTK_PARAM_READABLE));
/**
* GtkTextBuffer:copy-target-list:
*
* The list of targets this buffer supports for clipboard copying
* and as DND source.
*
* Since: 2.10
*/
g_object_class_install_property (object_class,
PROP_COPY_TARGET_LIST,
g_param_spec_boxed ("copy-target-list",
P_("Copy target list"),
P_("The list of targets this buffer supports for clipboard copying and DND source"),
GTK_TYPE_TARGET_LIST,
GTK_PARAM_READABLE));
/**
* GtkTextBuffer:paste-target-list:
*
* The list of targets this buffer supports for clipboard pasting
* and as DND destination.
*
* Since: 2.10
*/
g_object_class_install_property (object_class,
PROP_PASTE_TARGET_LIST,
g_param_spec_boxed ("paste-target-list",
P_("Paste target list"),
P_("The list of targets this buffer supports for clipboard pasting and DND destination"),
GTK_TYPE_TARGET_LIST,
GTK_PARAM_READABLE));
signals[INSERT_TEXT] =
g_signal_new (I_("insert_text"),
@ -378,7 +425,9 @@ gtk_text_buffer_class_init (GtkTextBufferClass *klass)
NULL, NULL,
_gtk_marshal_VOID__VOID,
G_TYPE_NONE,
0);
0);
g_type_class_add_private (object_class, sizeof (GtkTextBufferPrivate));
}
static void
@ -386,6 +435,9 @@ gtk_text_buffer_init (GtkTextBuffer *buffer)
{
buffer->clipboard_contents_buffers = NULL;
buffer->tag_table = NULL;
/* allow copying of arbiatray stuff in the internal rich text format */
gtk_text_buffer_register_serialize_tagset (buffer, NULL);
}
static void
@ -428,9 +480,9 @@ gtk_text_buffer_set_property (GObject *object,
case PROP_TAG_TABLE:
set_table (text_buffer, g_value_get_object (value));
break;
case PROP_TEXT:
gtk_text_buffer_set_text (text_buffer,
gtk_text_buffer_set_text (text_buffer,
g_value_get_string (value), -1);
break;
@ -454,28 +506,48 @@ gtk_text_buffer_get_property (GObject *object,
case PROP_TAG_TABLE:
g_value_set_object (value, get_table (text_buffer));
break;
case PROP_TEXT:
{
GtkTextIter start, end;
gtk_text_buffer_get_start_iter (text_buffer, &start);
gtk_text_buffer_get_end_iter (text_buffer, &end);
g_value_set_string (value,
gtk_text_buffer_get_text (text_buffer, &start, &end, FALSE));
break;
}
{
GtkTextIter start, end;
gtk_text_buffer_get_start_iter (text_buffer, &start);
gtk_text_buffer_get_end_iter (text_buffer, &end);
g_value_set_string (value,
gtk_text_buffer_get_text (text_buffer,
&start, &end, FALSE));
break;
}
case PROP_HAS_SELECTION:
g_value_set_boolean (value, text_buffer->has_selection);
break;
case PROP_COPY_TARGET_LIST:
g_value_set_boxed (value, gtk_text_buffer_get_copy_target_list (text_buffer));
break;
case PROP_PASTE_TARGET_LIST:
g_value_set_boxed (value, gtk_text_buffer_get_paste_target_list (text_buffer));
break;
default:
break;
}
}
static void
gtk_text_buffer_notify (GObject *object,
GParamSpec *pspec)
{
if (!strcmp (pspec->name, "copy-target-list") ||
!strcmp (pspec->name, "paste-target-list"))
{
gtk_text_buffer_free_target_lists (GTK_TEXT_BUFFER (object));
}
}
/**
* gtk_text_buffer_new:
* @table: a tag table, or NULL to create a new one
@ -498,11 +570,14 @@ static void
gtk_text_buffer_finalize (GObject *object)
{
GtkTextBuffer *buffer;
GtkTextBufferPrivate *priv;
buffer = GTK_TEXT_BUFFER (object);
remove_all_selection_clipboards (buffer);
priv = GTK_TEXT_BUFFER_GET_PRIVATE (buffer);
if (buffer->tag_table)
{
_gtk_text_tag_table_remove_buffer (buffer->tag_table, buffer);
@ -520,7 +595,9 @@ gtk_text_buffer_finalize (GObject *object)
free_log_attr_cache (buffer->log_attr_cache);
buffer->log_attr_cache = NULL;
gtk_text_buffer_free_target_lists (buffer);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@ -2840,22 +2917,36 @@ clipboard_get_selection_cb (GtkClipboard *clipboard,
if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
{
if (selection_data->target ==
gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"))
if (info == GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS)
{
/* Provide the address of the buffer; this will only be
* used within-process
*/
gtk_selection_data_set (selection_data,
gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"),
selection_data->target,
8, /* bytes */
(void*)&buffer,
sizeof (buffer));
}
else if (info == GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT)
{
guint8 *str;
gsize len;
str = gtk_text_buffer_serialize (buffer, buffer,
selection_data->target,
&start, &end, &len);
gtk_selection_data_set (selection_data,
selection_data->target,
8, /* bytes */
str, len);
g_free (str);
}
else
{
gchar *str;
str = gtk_text_iter_get_visible_text (&start, &end);
gtk_selection_data_set_text (selection_data, str, -1);
g_free (str);
@ -2870,8 +2961,11 @@ create_clipboard_contents_buffer (GtkTextBuffer *buffer)
contents = gtk_text_buffer_new (gtk_text_buffer_get_tag_table (buffer));
g_object_set_data (G_OBJECT (contents), I_("gtk-text-buffer-clipboard"), GINT_TO_POINTER (1));
g_object_set_data (G_OBJECT (contents), I_("gtk-text-buffer-clipboard-source"),
buffer);
g_object_set_data (G_OBJECT (contents), I_("gtk-text-buffer-clipboard"),
GINT_TO_POINTER (1));
return contents;
}
@ -2882,31 +2976,50 @@ clipboard_get_contents_cb (GtkClipboard *clipboard,
guint info,
gpointer data)
{
GtkTextBuffer *contents;
GtkTextBuffer *contents = GTK_TEXT_BUFFER (data);
contents = GTK_TEXT_BUFFER (data);
g_assert (contents); /* This should never be called unless we own the clipboard */
if (selection_data->target ==
gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"))
if (info == GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS)
{
/* Provide the address of the clipboard buffer; this will only
* be used within-process. OK to supply a NULL value for contents.
*/
gtk_selection_data_set (selection_data,
gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"),
selection_data->target,
8, /* bytes */
(void*)&contents,
sizeof (contents));
}
else if (info == GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT)
{
GtkTextBuffer *clipboard_source_buffer;
GtkTextIter start, end;
guint8 *str;
gsize len;
clipboard_source_buffer = g_object_get_data (G_OBJECT (contents),
"gtk-text-buffer-clipboard-source");
gtk_text_buffer_get_bounds (contents, &start, &end);
str = gtk_text_buffer_serialize (clipboard_source_buffer, contents,
selection_data->target,
&start, &end, &len);
gtk_selection_data_set (selection_data,
selection_data->target,
8, /* bytes */
str, len);
g_free (str);
}
else
{
gchar *str;
GtkTextIter start, end;
gtk_text_buffer_get_bounds (contents, &start, &end);
str = gtk_text_iter_get_visible_text (&start, &end);
gtk_selection_data_set_text (selection_data, str, -1);
g_free (str);
@ -3052,7 +3165,8 @@ selection_data_get_buffer (GtkSelectionData *selection_data,
if (gdk_window_get_window_type (owner) == GDK_WINDOW_FOREIGN)
return NULL;
if (selection_data->type != gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"))
if (selection_data->type !=
gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"))
return NULL;
if (selection_data->length != sizeof (src_buffer))
@ -3099,6 +3213,62 @@ restore_iter (const GtkTextIter *iter,
}
#endif
static void
clipboard_rich_text_received (GtkClipboard *clipboard,
GdkAtom format,
const guint8 *text,
gsize length,
gpointer data)
{
ClipboardRequest *request_data = data;
GtkTextIter insert_point;
gboolean retval = TRUE;
GError *error = NULL;
GtkTextBufferPrivate *priv;
priv = GTK_TEXT_BUFFER_GET_PRIVATE (request_data->buffer);
if (text != NULL && length > 0)
{
pre_paste_prep (request_data, &insert_point);
if (request_data->interactive)
gtk_text_buffer_begin_user_action (request_data->buffer);
if (!request_data->interactive ||
gtk_text_iter_can_insert (&insert_point,
request_data->default_editable))
{
retval = gtk_text_buffer_deserialize (request_data->buffer,
request_data->buffer,
format,
&insert_point,
text, length,
&error);
}
if (!retval)
{
g_warning ("error pasting: %s\n", error->message);
g_clear_error (&error);
}
if (request_data->interactive)
gtk_text_buffer_end_user_action (request_data->buffer);
if (retval)
{
post_paste_cleanup (request_data);
return;
}
}
/* Request the text selection instead */
gtk_clipboard_request_text (clipboard,
clipboard_text_received,
data);
}
static void
paste_from_buffer (ClipboardRequest *request_data,
GtkTextBuffer *src_buffer,
@ -3145,7 +3315,8 @@ clipboard_clipboard_buffer_received (GtkClipboard *clipboard,
{
ClipboardRequest *request_data = data;
GtkTextBuffer *src_buffer;
GtkTextBufferPrivate *priv;
src_buffer = selection_data_get_buffer (selection_data, request_data);
if (src_buffer)
@ -3155,7 +3326,7 @@ clipboard_clipboard_buffer_received (GtkClipboard *clipboard,
if (g_object_get_data (G_OBJECT (src_buffer), "gtk-text-buffer-clipboard"))
{
gtk_text_buffer_get_bounds (src_buffer, &start, &end);
paste_from_buffer (request_data, src_buffer,
&start, &end);
}
@ -3168,21 +3339,27 @@ clipboard_clipboard_buffer_received (GtkClipboard *clipboard,
}
else
{
/* Request the text selection instead */
gtk_clipboard_request_text (clipboard,
clipboard_text_received,
data);
priv = GTK_TEXT_BUFFER_GET_PRIVATE (request_data->buffer);
if (gtk_clipboard_wait_is_rich_text_available (clipboard,
request_data->buffer))
{
/* Request rich text */
gtk_clipboard_request_rich_text (clipboard,
request_data->buffer,
clipboard_rich_text_received,
data);
}
else
{
/* Request the text selection instead */
gtk_clipboard_request_text (clipboard,
clipboard_text_received,
data);
}
}
}
static const GtkTargetEntry targets[] = {
{ "STRING", 0, TARGET_STRING },
{ "TEXT", 0, TARGET_TEXT },
{ "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
{ "UTF8_STRING", 0, TARGET_UTF8_STRING },
{ "GTK_TEXT_BUFFER_CONTENTS", 0, TARGET_TEXT_BUFFER_CONTENTS }
};
typedef struct
{
GtkClipboard *clipboard;
@ -3192,7 +3369,13 @@ typedef struct
static void
update_selection_clipboards (GtkTextBuffer *buffer)
{
GSList *tmp_list = buffer->selection_clipboards;
GtkTextBufferPrivate *priv;
GSList *tmp_list = buffer->selection_clipboards;
priv = GTK_TEXT_BUFFER_GET_PRIVATE (buffer);
gtk_text_buffer_get_copy_target_list (buffer);
while (tmp_list)
{
GtkTextIter start;
@ -3214,7 +3397,9 @@ update_selection_clipboards (GtkTextBuffer *buffer)
/* Even if we already have the selection, we need to update our
* timestamp.
*/
if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
if (!gtk_clipboard_set_with_owner (clipboard,
priv->copy_target_entries,
priv->n_copy_target_entries,
clipboard_get_selection_cb,
clipboard_clear_selection_cb,
G_OBJECT (buffer)))
@ -3525,6 +3710,8 @@ cut_or_copy (GtkTextBuffer *buffer,
gboolean interactive,
gboolean default_editable)
{
GtkTextBufferPrivate *priv;
/* We prefer to cut the selected region between selection_bound and
* insertion point. If that region is empty, then we cut the region
* between the "anchor" and the insertion point (this is for
@ -3534,7 +3721,11 @@ cut_or_copy (GtkTextBuffer *buffer,
*/
GtkTextIter start;
GtkTextIter end;
priv = GTK_TEXT_BUFFER_GET_PRIVATE (buffer);
gtk_text_buffer_get_copy_target_list (buffer);
if (!gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
{
/* Let's try the anchor thing */
@ -3560,14 +3751,18 @@ cut_or_copy (GtkTextBuffer *buffer,
gtk_text_buffer_insert_range (contents, &ins, &start, &end);
if (!gtk_clipboard_set_with_data (clipboard, targets, G_N_ELEMENTS (targets),
if (!gtk_clipboard_set_with_data (clipboard,
priv->copy_target_entries,
priv->n_copy_target_entries,
clipboard_get_contents_cb,
clipboard_clear_contents_cb,
contents))
g_object_unref (contents);
else
gtk_clipboard_set_can_store (clipboard, (GtkTargetEntry *)targets, G_N_ELEMENTS (targets) -1);
gtk_clipboard_set_can_store (clipboard,
priv->copy_target_entries + 1,
priv->n_copy_target_entries - 1);
if (delete_region_after)
{
if (interactive)
@ -3699,6 +3894,126 @@ gtk_text_buffer_end_user_action (GtkTextBuffer *buffer)
}
}
static void
gtk_text_buffer_free_target_lists (GtkTextBuffer *buffer)
{
GtkTextBufferPrivate *priv = GTK_TEXT_BUFFER_GET_PRIVATE (buffer);
if (priv->copy_target_list)
{
gtk_target_list_unref (priv->copy_target_list);
priv->copy_target_list = NULL;
gtk_target_table_free (priv->copy_target_entries,
priv->n_copy_target_entries);
priv->copy_target_entries = NULL;
priv->n_copy_target_entries = 0;
}
if (priv->paste_target_list)
{
gtk_target_list_unref (priv->paste_target_list);
priv->paste_target_list = NULL;
gtk_target_table_free (priv->paste_target_entries,
priv->n_paste_target_entries);
priv->paste_target_entries = NULL;
priv->n_paste_target_entries = 0;
}
}
static GtkTargetList *
gtk_text_buffer_get_target_list (GtkTextBuffer *buffer,
gboolean deserializable,
GtkTargetEntry **entries,
gint *n_entries)
{
GtkTargetList *target_list;
target_list = gtk_target_list_new (NULL, 0);
gtk_target_list_add (target_list,
gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"),
GTK_TARGET_SAME_APP,
GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS);
gtk_target_list_add_rich_text_targets (target_list,
GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT,
deserializable,
buffer);
gtk_target_list_add_text_targets (target_list,
GTK_TEXT_BUFFER_TARGET_INFO_TEXT);
*entries = gtk_target_table_new_from_list (target_list, n_entries);
return target_list;
}
/**
* gtk_text_buffer_get_copy_target_list:
* @buffer: a #GtkTextBuffer
*
* This function returns the list of targets this text buffer can
* provide for copying and as DND source. The targets in the list are
* added with %info values from the #GtkTextBufferTargetInfo enum,
* using gtk_target_list_add_rich_text_targets() and
* gtk_target_list_add_text_targets()
*
* Return value: the #GtkTargetList
*
* Since: 2.10
**/
GtkTargetList *
gtk_text_buffer_get_copy_target_list (GtkTextBuffer *buffer)
{
GtkTextBufferPrivate *priv;
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
priv = GTK_TEXT_BUFFER_GET_PRIVATE (buffer);
if (! priv->copy_target_list)
priv->copy_target_list =
gtk_text_buffer_get_target_list (buffer, FALSE,
&priv->copy_target_entries,
&priv->n_copy_target_entries);
return priv->copy_target_list;
}
/**
* gtk_text_buffer_get_paste_target_list:
* @buffer: a #GtkTextBuffer
*
* This function returns the list of targets this text buffer supports
* for pasting and as DND destination. The targets in the list are
* added with %info values from the #GtkTextBufferTargetInfo enum,
* using gtk_target_list_add_rich_text_targets() and
* gtk_target_list_add_text_targets()
*
* Return value: the #GtkTargetList
*
* Since: 2.10
**/
GtkTargetList *
gtk_text_buffer_get_paste_target_list (GtkTextBuffer *buffer)
{
GtkTextBufferPrivate *priv;
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
priv = GTK_TEXT_BUFFER_GET_PRIVATE (buffer);
if (! priv->paste_target_list)
priv->paste_target_list =
gtk_text_buffer_get_target_list (buffer, TRUE,
&priv->paste_target_entries,
&priv->n_paste_target_entries);
return priv->paste_target_list;
}
/*
* Logical attribute cache
*/

View File

@ -41,6 +41,16 @@ G_BEGIN_DECLS
* GtkTextBTree is the PRIVATE internal representation of it.
*/
/* these values are used as "info" for the targets contained in the
* lists returned by gtk_text_buffer_get_copy,paste_target_list()
*/
typedef enum
{
GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS,
GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT,
GTK_TEXT_BUFFER_TARGET_INFO_TEXT
} GtkTextBufferTargetInfo;
typedef struct _GtkTextBTree GtkTextBTree;
typedef struct _GtkTextLogAttrCache GtkTextLogAttrCache;
@ -367,6 +377,9 @@ gboolean gtk_text_buffer_delete_selection (GtkTextBuffer *buffer,
void gtk_text_buffer_begin_user_action (GtkTextBuffer *buffer);
void gtk_text_buffer_end_user_action (GtkTextBuffer *buffer);
GtkTargetList * gtk_text_buffer_get_copy_target_list (GtkTextBuffer *buffer);
GtkTargetList * gtk_text_buffer_get_paste_target_list (GtkTextBuffer *buffer);
/* INTERNAL private stuff */
void _gtk_text_buffer_spew (GtkTextBuffer *buffer);

704
gtk/gtktextbufferrichtext.c Normal file
View File

@ -0,0 +1,704 @@
/* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include "gtktextbufferrichtext.h"
#include "gtktextbufferserialize.h"
#include "gtkalias.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.
*
* Return value: 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: 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.
*
* Return value: 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.
*
* Return value: 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: 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.
*
* Return value: 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()
*
* Return value: 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: 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()
*
* Return value: an array of #GdkAtom<!-- -->s 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: 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()
*
* Return value: an array of #GdkAtom<!-- -->s 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: 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.
*
* %format<!-- -->s to be used must be registered using
* gtk_text_buffer_register_serialize_format() or
* gtk_text_buffer_register_serialize_tagset() beforehand.
*
* Return value: 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_serialize:
* @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: data to deserialize
* @length: length of %data
* @error: return loaction for a #GError
*
* This function deserializes rich text in format %format and inserts
* it at %iter.
*
* %format<!-- -->s to be used must be registered using
* gtk_text_buffer_register_deserialize_format() or
* gtk_text_buffer_register_deserialize_tagset() beforehand.
*
* Return value: %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;
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));
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_new0 (GtkRichTextFormat, 1);
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_free (format);
}
static void
free_format_list (GList *formats)
{
g_list_foreach (formats, (GFunc) free_format, NULL);
g_list_free (formats);
}
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;
}
#define __GTK_TEXT_BUFFER_RICH_TEXT_C__
#include "gtkaliasdef.c"

View File

@ -0,0 +1,92 @@
/* gtkrichtext.h
*
* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GTK_TEXT_BUFFER_RICH_TEXT_H__
#define __GTK_TEXT_BUFFER_RICH_TEXT_H__
#include <gtk/gtktextbuffer.h>
G_BEGIN_DECLS
typedef guint8 * (* GtkTextBufferSerializeFunc) (GtkTextBuffer *register_buffer,
GtkTextBuffer *content_buffer,
const GtkTextIter *start,
const GtkTextIter *end,
gsize *length,
gpointer user_data);
typedef gboolean (* GtkTextBufferDeserializeFunc) (GtkTextBuffer *register_buffer,
GtkTextBuffer *content_buffer,
GtkTextIter *iter,
const guint8 *data,
gsize length,
gboolean create_tags,
gpointer user_data,
GError **error);
GdkAtom gtk_text_buffer_register_serialize_format (GtkTextBuffer *buffer,
const gchar *mime_type,
GtkTextBufferSerializeFunc function,
gpointer user_data,
GDestroyNotify user_data_destroy);
GdkAtom gtk_text_buffer_register_serialize_tagset (GtkTextBuffer *buffer,
const gchar *tagset_name);
GdkAtom gtk_text_buffer_register_deserialize_format (GtkTextBuffer *buffer,
const gchar *mime_type,
GtkTextBufferDeserializeFunc function,
gpointer user_data,
GDestroyNotify user_data_destroy);
GdkAtom gtk_text_buffer_register_deserialize_tagset (GtkTextBuffer *buffer,
const gchar *tagset_name);
void gtk_text_buffer_unregister_serialize_format (GtkTextBuffer *buffer,
GdkAtom format);
void gtk_text_buffer_unregister_deserialize_format (GtkTextBuffer *buffer,
GdkAtom format);
void gtk_text_buffer_deserialize_set_can_create_tags (GtkTextBuffer *buffer,
GdkAtom format,
gboolean can_create_tags);
gboolean gtk_text_buffer_deserialize_get_can_create_tags (GtkTextBuffer *buffer,
GdkAtom format);
GdkAtom * gtk_text_buffer_get_serialize_formats (GtkTextBuffer *buffer,
gint *n_formats);
GdkAtom * gtk_text_buffer_get_deserialize_formats (GtkTextBuffer *buffer,
gint *n_formats);
guint8 * gtk_text_buffer_serialize (GtkTextBuffer *register_buffer,
GtkTextBuffer *content_buffer,
GdkAtom format,
const GtkTextIter *start,
const GtkTextIter *end,
gsize *length);
gboolean gtk_text_buffer_deserialize (GtkTextBuffer *register_buffer,
GtkTextBuffer *content_buffer,
GdkAtom format,
GtkTextIter *iter,
const guint8 *data,
gsize length,
GError **error);
G_END_DECLS
#endif /* __GTK_TEXT_BUFFER_RICH_TEXT_H__ */

1877
gtk/gtktextbufferserialize.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
/* gtktextbufferserialize.h
*
* Copyright (C) 2004 Nokia Corporation.
*
* 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GTK_TEXT_BUFFER_SERIALIZE_H__
#define __GTK_TEXT_BUFFER_SERIALIZE_H__
#include <gtk/gtktextbuffer.h>
guint8 * _gtk_text_buffer_serialize_rich_text (GtkTextBuffer *register_buffer,
GtkTextBuffer *content_buffer,
const GtkTextIter *start,
const GtkTextIter *end,
gsize *length,
gpointer user_data);
gboolean _gtk_text_buffer_deserialize_rich_text (GtkTextBuffer *register_buffer,
GtkTextBuffer *content_buffer,
GtkTextIter *iter,
const guint8 *data,
gsize length,
gboolean create_tags,
gpointer user_data,
GError **error);
#endif /* __GTK_TEXT_BUFFER_SERIALIZE_H__ */

View File

@ -26,12 +26,19 @@
#include <config.h>
#include "gtktextutil.h"
#include "gtkintl.h"
#include "gtktextview.h"
#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
#include "gtktextdisplay.h"
#include "gtktextbuffer.h"
#include "gtkmenuitem.h"
#include "gtkintl.h"
#include "gtkalias.h"
#define DRAG_ICON_MAX_WIDTH 250
#define DRAG_ICON_LAYOUT_BORDER 2
#define DRAG_ICON_MAX_HEIGHT 250
#define DRAG_ICON_LAYOUT_BORDER 5
#define DRAG_ICON_MAX_LINES 7
#define ELLIPSIS_CHARACTER "\xe2\x80\xa6"
@ -177,7 +184,7 @@ limit_layout_lines (PangoLayout *layout)
*
* Creates a drag and drop icon from @text.
**/
GdkPixmap*
GdkPixmap *
_gtk_text_util_create_drag_icon (GtkWidget *widget,
gchar *text,
gsize len)
@ -238,3 +245,124 @@ _gtk_text_util_create_drag_icon (GtkWidget *widget,
return drawable;
}
static void
gtk_text_view_set_attributes_from_style (GtkTextView *text_view,
GtkTextAttributes *values,
GtkStyle *style)
{
values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
values->appearance.fg_color = style->text[GTK_STATE_NORMAL];
if (values->font)
pango_font_description_free (values->font);
values->font = pango_font_description_copy (style->font_desc);
}
GdkPixmap *
_gtk_text_util_create_rich_drag_icon (GtkWidget *widget,
GtkTextBuffer *buffer,
GtkTextIter *start,
GtkTextIter *end)
{
GdkDrawable *drawable = NULL;
gint pixmap_height, pixmap_width;
gint layout_width, layout_height;
GtkTextBuffer *new_buffer;
GtkTextLayout *layout;
GtkTextAttributes *style;
PangoContext *ltr_context, *rtl_context;
GtkTextIter iter;
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
g_return_val_if_fail (start != NULL, NULL);
g_return_val_if_fail (end != NULL, NULL);
new_buffer = gtk_text_buffer_new (gtk_text_buffer_get_tag_table (buffer));
gtk_text_buffer_get_start_iter (new_buffer, &iter);
gtk_text_buffer_insert_range (new_buffer, &iter, start, end);
gtk_text_buffer_get_start_iter (new_buffer, &iter);
layout = gtk_text_layout_new ();
ltr_context = gtk_widget_create_pango_context (widget);
pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
rtl_context = gtk_widget_create_pango_context (widget);
pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);
gtk_text_layout_set_contexts (layout, ltr_context, rtl_context);
g_object_unref (ltr_context);
g_object_unref (rtl_context);
style = gtk_text_attributes_new ();
layout_width = widget->allocation.width;
if (GTK_IS_TEXT_VIEW (widget))
{
gtk_widget_ensure_style (widget);
gtk_text_view_set_attributes_from_style (GTK_TEXT_VIEW (widget),
style, widget->style);
layout_width = layout_width
- gtk_text_view_get_border_window_size (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_LEFT)
- gtk_text_view_get_border_window_size (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_RIGHT);
}
style->direction = gtk_widget_get_direction (widget);
style->wrap_mode = PANGO_WRAP_WORD_CHAR;
gtk_text_layout_set_default_style (layout, style);
gtk_text_attributes_unref (style);
gtk_text_layout_set_buffer (layout, new_buffer);
gtk_text_layout_set_cursor_visible (layout, FALSE);
gtk_text_layout_set_screen_width (layout, layout_width);
gtk_text_layout_validate (layout, DRAG_ICON_MAX_HEIGHT);
gtk_text_layout_get_size (layout, &layout_width, &layout_height);
g_print ("%s: layout size %d %d\n", G_STRFUNC, layout_width, layout_height);
layout_width = MIN (layout_width, DRAG_ICON_MAX_WIDTH);
layout_height = MIN (layout_height, DRAG_ICON_MAX_HEIGHT);
pixmap_width = layout_width + DRAG_ICON_LAYOUT_BORDER * 2;
pixmap_height = layout_height + DRAG_ICON_LAYOUT_BORDER * 2;
g_print ("%s: pixmap size %d %d\n", G_STRFUNC, pixmap_width, pixmap_height);
drawable = gdk_pixmap_new (widget->window,
pixmap_width + 2, pixmap_height + 2, -1);
gdk_draw_rectangle (drawable,
widget->style->base_gc [GTK_WIDGET_STATE (widget)],
TRUE,
0, 0,
pixmap_width + 1,
pixmap_height + 1);
gtk_text_layout_draw (layout, widget, drawable,
widget->style->text_gc [GTK_WIDGET_STATE (widget)],
- (1 + DRAG_ICON_LAYOUT_BORDER),
- (1 + DRAG_ICON_LAYOUT_BORDER),
0, 0,
pixmap_width, pixmap_height, NULL);
gdk_draw_rectangle (drawable,
widget->style->black_gc,
FALSE,
0, 0,
pixmap_width + 1,
pixmap_height + 1);
g_object_unref (layout);
g_object_unref (new_buffer);
return drawable;
}

View File

@ -31,6 +31,7 @@
#include <gtk/gtkwidget.h>
#include <gtk/gtkmenushell.h>
#include <gtk/gtkeditable.h>
#include <gtk/gtktextbuffer.h>
G_BEGIN_DECLS
@ -47,8 +48,12 @@ void _gtk_text_util_append_special_char_menuitems (GtkMenuShell *me
G_END_DECLS
GdkPixmap* _gtk_text_util_create_drag_icon (GtkWidget *widget,
gchar *text,
gsize len);
GdkPixmap* _gtk_text_util_create_drag_icon (GtkWidget *widget,
gchar *text,
gsize len);
GdkPixmap* _gtk_text_util_create_rich_drag_icon (GtkWidget *widget,
GtkTextBuffer *buffer,
GtkTextIter *start,
GtkTextIter *end);
#endif /* __GTK_TEXT_UTIL_H__ */

View File

@ -39,6 +39,7 @@
#include "gtkseparatormenuitem.h"
#include "gtksettings.h"
#include "gtkstock.h"
#include "gtktextbufferrichtext.h"
#include "gtktextdisplay.h"
#include "gtktextview.h"
#include "gtkimmulticontext.h"
@ -314,6 +315,9 @@ static void gtk_text_view_mark_set_handler (GtkTextBuffer *buffer,
const GtkTextIter *location,
GtkTextMark *mark,
gpointer data);
static void gtk_text_view_target_list_notify (GtkTextBuffer *buffer,
const GParamSpec *pspec,
gpointer data);
static void gtk_text_view_get_cursor_location (GtkTextView *text_view,
GdkRectangle *pos);
static void gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
@ -414,10 +418,6 @@ static gint text_window_get_width (GtkTextWindow *win);
static gint text_window_get_height (GtkTextWindow *win);
static const GtkTargetEntry target_table[] = {
{ "GTK_TEXT_BUFFER_CONTENTS", GTK_TARGET_SAME_APP, 0 },
};
static GtkContainerClass *parent_class = NULL;
static guint signals[LAST_SIGNAL] = { 0 };
@ -1062,11 +1062,8 @@ gtk_text_view_init (GtkTextView *text_view)
text_view->tabs = NULL;
text_view->editable = TRUE;
gtk_drag_dest_set (widget,
0,
target_table, G_N_ELEMENTS (target_table),
gtk_drag_dest_set (widget, 0, NULL, 0,
GDK_ACTION_COPY | GDK_ACTION_MOVE);
gtk_drag_dest_add_text_targets (widget);
text_view->virtual_cursor_x = -1;
text_view->virtual_cursor_y = -1;
@ -1190,6 +1187,9 @@ gtk_text_view_set_buffer (GtkTextView *text_view,
g_signal_handlers_disconnect_by_func (text_view->buffer,
gtk_text_view_mark_set_handler,
text_view);
g_signal_handlers_disconnect_by_func (text_view->buffer,
gtk_text_view_target_list_notify,
text_view);
g_object_unref (text_view->buffer);
text_view->dnd_mark = NULL;
@ -1225,7 +1225,13 @@ gtk_text_view_set_buffer (GtkTextView *text_view,
text_view->first_para_pixels = 0;
g_signal_connect (text_view->buffer, "mark_set",
G_CALLBACK (gtk_text_view_mark_set_handler), text_view);
G_CALLBACK (gtk_text_view_mark_set_handler),
text_view);
g_signal_connect (text_view->buffer, "notify::paste-target-list",
G_CALLBACK (gtk_text_view_target_list_notify),
text_view);
gtk_text_view_target_list_notify (text_view->buffer, NULL, text_view);
if (GTK_WIDGET_REALIZED (text_view))
{
@ -5984,21 +5990,6 @@ gtk_text_view_reset_im_context (GtkTextView *text_view)
}
}
static gchar*
_gtk_text_view_get_selected_text (GtkTextView *text_view)
{
GtkTextBuffer *buffer;
GtkTextIter start, end;
gchar *text = NULL;
buffer = gtk_text_view_get_buffer (text_view);
if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
return text;
}
/*
* DND feature
*/
@ -6008,29 +5999,30 @@ drag_begin_cb (GtkWidget *widget,
GdkDragContext *context,
gpointer data)
{
GtkTextView *text_view;
gchar *text;
GdkPixmap *pixmap = NULL;
GtkTextView *text_view = GTK_TEXT_VIEW (widget);
GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
GtkTextIter start;
GtkTextIter end;
GdkPixmap *pixmap = NULL;
g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL);
text_view = GTK_TEXT_VIEW (widget);
text = _gtk_text_view_get_selected_text (text_view);
pixmap = _gtk_text_util_create_drag_icon (widget, text, -1);
if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
pixmap = _gtk_text_util_create_rich_drag_icon (widget, buffer, &start, &end);
if (pixmap)
gtk_drag_set_icon_pixmap (context,
gdk_drawable_get_colormap (pixmap),
pixmap,
NULL,
-2, -2);
{
gtk_drag_set_icon_pixmap (context,
gdk_drawable_get_colormap (pixmap),
pixmap,
NULL,
-2, -2);
g_object_unref (pixmap);
}
else
gtk_drag_set_icon_default (context);
if (pixmap)
g_object_unref (pixmap);
g_free (text);
{
gtk_drag_set_icon_default (context);
}
}
static void
@ -6038,23 +6030,19 @@ gtk_text_view_start_selection_dnd (GtkTextView *text_view,
const GtkTextIter *iter,
GdkEventMotion *event)
{
GtkTargetList *target_list;
GtkTargetList *target_list;
text_view->drag_start_x = -1;
text_view->drag_start_y = -1;
text_view->pending_place_cursor_button = 0;
target_list = gtk_target_list_new (target_table,
G_N_ELEMENTS (target_table));
gtk_target_list_add_text_targets (target_list, 0);
g_signal_connect (text_view, "drag-begin",
target_list = gtk_text_buffer_get_copy_target_list (get_buffer (text_view));
g_signal_connect (text_view, "drag-begin",
G_CALLBACK (drag_begin_cb), NULL);
gtk_drag_begin (GTK_WIDGET (text_view), target_list,
GDK_ACTION_COPY | GDK_ACTION_MOVE,
1, (GdkEvent*)event);
gtk_target_list_unref (target_list);
}
static void
@ -6077,30 +6065,49 @@ gtk_text_view_drag_data_get (GtkWidget *widget,
guint info,
guint time)
{
GtkTextView *text_view;
GtkTextView *text_view = GTK_TEXT_VIEW (widget);
GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
text_view = GTK_TEXT_VIEW (widget);
if (selection_data->target == gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"))
if (info == GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS)
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
gtk_selection_data_set (selection_data,
gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"),
8, /* bytes */
(void*)&buffer,
sizeof (buffer));
}
else
else if (info == GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT)
{
gchar *str;
GtkTextIter start;
GtkTextIter end;
guint8 *str = NULL;
gsize len;
str = NULL;
if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
{
/* Extract the selected text */
str = gtk_text_buffer_serialize (buffer, buffer,
selection_data->target,
&start, &end,
&len);
}
if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
&start, &end))
if (str)
{
gtk_selection_data_set (selection_data,
selection_data->target,
8, /* bytes */
(guchar *) str, len);
g_free (str);
}
}
else
{
GtkTextIter start;
GtkTextIter end;
gchar *str = NULL;
if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
{
/* Extract the selected text */
str = gtk_text_iter_get_visible_text (&start, &end);
@ -6179,7 +6186,7 @@ gtk_text_view_drag_motion (GtkWidget *widget,
/* can't accept any of the offered targets */
}
else if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
&start, &end) &&
&start, &end) &&
gtk_text_iter_compare (&newplace, &start) >= 0 &&
gtk_text_iter_compare (&newplace, &end) <= 0)
{
@ -6284,14 +6291,14 @@ insert_text_data (GtkTextView *text_view,
GtkTextIter *drop_point,
GtkSelectionData *selection_data)
{
gchar *str;
guchar *str;
str = gtk_selection_data_get_text (selection_data);
if (str)
{
gtk_text_buffer_insert_interactive (get_buffer (text_view),
drop_point, str, -1,
drop_point, (gchar *) str, -1,
text_view->editable);
g_free (str);
}
@ -6329,7 +6336,7 @@ gtk_text_view_drag_data_received (GtkWidget *widget,
gtk_text_buffer_begin_user_action (buffer);
if (selection_data->target == gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"))
if (info == GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS)
{
GtkTextBuffer *src_buffer = NULL;
GtkTextIter start, end;
@ -6347,7 +6354,38 @@ gtk_text_view_drag_data_received (GtkWidget *widget,
if (gtk_text_buffer_get_tag_table (src_buffer) !=
gtk_text_buffer_get_tag_table (buffer))
copy_tags = FALSE;
{
/* try to find a suitable rich text target instead */
GdkAtom *atoms;
gint n_atoms;
GList *list;
GdkAtom target = GDK_NONE;
copy_tags = FALSE;
atoms = gtk_text_buffer_get_deserialize_formats (buffer, &n_atoms);
for (list = context->targets; list; list = g_list_next (list))
{
gint i;
for (i = 0; i < n_atoms; i++)
if (GUINT_TO_POINTER (atoms[i]) == list->data)
{
target = atoms[i];
break;
}
}
g_free (atoms);
if (target != GDK_NONE)
{
gtk_drag_get_data (widget, context, target, time);
gtk_text_buffer_end_user_action (buffer);
return;
}
}
if (gtk_text_buffer_get_selection_bounds (src_buffer,
&start,
@ -6371,9 +6409,28 @@ gtk_text_view_drag_data_received (GtkWidget *widget,
}
}
}
else if (selection_data->length > 0 &&
info == GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT)
{
gboolean retval;
GError *error = NULL;
retval = gtk_text_buffer_deserialize (buffer, buffer,
selection_data->target,
&drop_point,
(guint8 *) selection_data->data,
selection_data->length,
&error);
if (!retval)
{
g_warning ("error pasting: %s\n", error->message);
g_clear_error (&error);
}
}
else
insert_text_data (text_view, &drop_point, selection_data);
done:
gtk_drag_finish (context, success,
success && context->action == GDK_ACTION_MOVE,
@ -6825,6 +6882,14 @@ gtk_text_view_mark_set_handler (GtkTextBuffer *buffer,
gtk_text_view_reset_im_context (text_view);
}
static void
gtk_text_view_target_list_notify (GtkTextBuffer *buffer,
const GParamSpec *pspec,
gpointer data)
{
gtk_drag_dest_set_target_list (data, gtk_text_buffer_get_paste_target_list (buffer));
}
static void
gtk_text_view_get_cursor_location (GtkTextView *text_view,
GdkRectangle *pos)

View File

@ -1282,6 +1282,267 @@ do_properties (gpointer callback_data,
create_prop_editor (G_OBJECT (view->text_view), 0);
}
static void
rich_text_store_populate (GtkListStore *store,
GtkTextBuffer *buffer,
gboolean deserialize)
{
GdkAtom *formats;
gint n_formats;
gint i;
gtk_list_store_clear (store);
if (deserialize)
formats = gtk_text_buffer_get_deserialize_formats (buffer, &n_formats);
else
formats = gtk_text_buffer_get_serialize_formats (buffer, &n_formats);
for (i = 0; i < n_formats; i++)
{
GtkTreeIter iter;
gchar *mime_type;
gboolean can_create_tags = FALSE;
mime_type = gdk_atom_name (formats[i]);
if (deserialize)
can_create_tags =
gtk_text_buffer_deserialize_get_can_create_tags (buffer, formats[i]);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
0, formats[i],
1, mime_type,
2, can_create_tags,
-1);
g_free (mime_type);
}
g_free (formats);
}
static void
rich_text_paste_target_list_notify (GtkTextBuffer *buffer,
const GParamSpec *pspec,
GtkListStore *store)
{
rich_text_store_populate (store, buffer, TRUE);
}
static void
rich_text_copy_target_list_notify (GtkTextBuffer *buffer,
const GParamSpec *pspec,
GtkListStore *store)
{
rich_text_store_populate (store, buffer, FALSE);
}
static void
rich_text_can_create_tags_toggled (GtkCellRendererToggle *toggle,
const gchar *path,
GtkTreeModel *model)
{
GtkTreeIter iter;
if (gtk_tree_model_get_iter_from_string (model, &iter, path))
{
GtkTextBuffer *buffer;
GdkAtom format;
gboolean can_create_tags;
buffer = g_object_get_data (G_OBJECT (model), "buffer");
gtk_tree_model_get (model, &iter,
0, &format,
2, &can_create_tags,
-1);
gtk_text_buffer_deserialize_set_can_create_tags (buffer, format,
!can_create_tags);
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2, !can_create_tags,
-1);
}
}
static void
rich_text_unregister_clicked (GtkWidget *button,
GtkTreeView *tv)
{
GtkTreeSelection *sel = gtk_tree_view_get_selection (tv);
GtkTreeModel *model;
GtkTreeIter iter;
if (gtk_tree_selection_get_selected (sel, &model, &iter))
{
GtkTextBuffer *buffer;
gboolean deserialize;
GdkAtom format;
buffer = g_object_get_data (G_OBJECT (model), "buffer");
deserialize = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model),
"deserialize"));
gtk_tree_model_get (model, &iter,
0, &format,
-1);
if (deserialize)
gtk_text_buffer_unregister_deserialize_format (buffer, format);
else
gtk_text_buffer_unregister_serialize_format (buffer, format);
}
}
static void
rich_text_register_clicked (GtkWidget *button,
GtkTreeView *tv)
{
GtkWidget *dialog;
GtkWidget *label;
GtkWidget *entry;
dialog = gtk_dialog_new_with_buttons ("Register new Tagset",
GTK_WINDOW (gtk_widget_get_toplevel (button)),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
label = gtk_label_new ("Enter tagset name or leave blank for "
"unrestricted internal format:");
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label,
FALSE, FALSE, 0);
entry = gtk_entry_new ();
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), entry,
FALSE, FALSE, 0);
gtk_widget_show_all (dialog);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
{
GtkTreeModel *model = gtk_tree_view_get_model (tv);
GtkTextBuffer *buffer = g_object_get_data (G_OBJECT (model), "buffer");
const gchar *tagset = gtk_entry_get_text (GTK_ENTRY (entry));
gboolean deserialize;
deserialize = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model),
"deserialize"));
if (tagset && ! strlen (tagset))
tagset = NULL;
if (deserialize)
gtk_text_buffer_register_deserialize_tagset (buffer, tagset);
else
gtk_text_buffer_register_serialize_tagset (buffer, tagset);
}
gtk_widget_destroy (dialog);
}
static void
do_rich_text (gpointer callback_data,
guint deserialize,
GtkWidget *widget)
{
View *view = view_from_widget (widget);
GtkTextBuffer *buffer;
GtkWidget *dialog;
GtkWidget *tv;
GtkWidget *sw;
GtkWidget *hbox;
GtkWidget *button;
GtkListStore *store;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view->text_view));
dialog = gtk_dialog_new_with_buttons (deserialize ?
"Rich Text Paste & Drop" :
"Rich Text Copy & Drag",
GTK_WINDOW (view->window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CLOSE, 0,
NULL);
g_signal_connect (dialog, "response",
G_CALLBACK (gtk_widget_destroy),
NULL);
store = gtk_list_store_new (3,
G_TYPE_POINTER,
G_TYPE_STRING,
G_TYPE_BOOLEAN);
g_object_set_data (G_OBJECT (store), "buffer", buffer);
g_object_set_data (G_OBJECT (store), "deserialize",
GUINT_TO_POINTER (deserialize));
tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
g_object_unref (store);
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
0, "Rich Text Format",
gtk_cell_renderer_text_new (),
"text", 1,
NULL);
if (deserialize)
{
GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new ();
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
1, "Can Create Tags",
renderer,
"active", 2,
NULL);
g_signal_connect (renderer, "toggled",
G_CALLBACK (rich_text_can_create_tags_toggled),
store);
}
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_set_size_request (sw, 300, 100);
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), sw);
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), tv);
hbox = gtk_hbox_new (FALSE, 6);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
FALSE, FALSE, 0);
button = gtk_button_new_with_label ("Unregister Selected Format");
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
g_signal_connect (button, "clicked",
G_CALLBACK (rich_text_unregister_clicked),
tv);
button = gtk_button_new_with_label ("Register New Tagset\n"
"for the Internal Format");
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
g_signal_connect (button, "clicked",
G_CALLBACK (rich_text_register_clicked),
tv);
if (deserialize)
g_signal_connect_object (buffer, "notify::paste-target-list",
G_CALLBACK (rich_text_paste_target_list_notify),
G_OBJECT (store), 0);
else
g_signal_connect_object (buffer, "notify::copy-target-list",
G_CALLBACK (rich_text_copy_target_list_notify),
G_OBJECT (store), 0);
rich_text_store_populate (store, buffer, deserialize);
gtk_widget_show_all (dialog);
}
enum
{
RESPONSE_FORWARD,
@ -1695,8 +1956,10 @@ static GtkItemFactoryEntry menu_items[] =
{ "/Attributes/Default tabs", NULL, do_apply_tabs, TRUE, NULL },
{ "/Attributes/Color cycles", NULL, do_apply_colors, TRUE, NULL },
{ "/Attributes/No colors", NULL, do_apply_colors, FALSE, NULL },
{ "/Attributes/Remove all tags", NULL, do_remove_tags, 0, NULL },
{ "/Attributes/Properties", NULL, do_properties, 0, NULL },
{ "/Attributes/Remove all tags", NULL, do_remove_tags, 0, NULL },
{ "/Attributes/Properties", NULL, do_properties, 0, NULL },
{ "/Attributes/Rich Text copy & drag", NULL, do_rich_text, 0, NULL },
{ "/Attributes/Rich Text paste & drop", NULL, do_rich_text, 1, NULL },
{ "/_Test", NULL, NULL, 0, "<Branch>" },
{ "/Test/_Example", NULL, do_example, 0, NULL },
{ "/Test/_Insert and scroll", NULL, do_insert_and_scroll, 0, NULL },