X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkselection.c;h=8ea2a3c93ae3c877215a365b4337b4aafc81b402;hb=4dc4227c70a49e67c930e0ebaeab74e3d28f004b;hp=47c58c9e9862d8b7da17a737f6bddcf81e5729f6;hpb=52658cd3dc2ec01fe9cfcb5f9253b00febc8e039;p=~andy%2Fgtk diff --git a/gtk/gtkselection.c b/gtk/gtkselection.c index 47c58c9e9..8ea2a3c93 100644 --- a/gtk/gtkselection.c +++ b/gtk/gtkselection.c @@ -17,12 +17,12 @@ * Boston, MA 02111-1307, USA. */ -/* This file implements most of the work of the ICCM selection protocol. +/* This file implements most of the work of the ICCCM selection protocol. * The code was written after an intensive study of the equivalent part * of John Ousterhout's Tk toolkit, and does many things in much the * same way. * - * The one thing in the ICCM that isn't fully supported here (or in Tk) + * The one thing in the ICCCM that isn't fully supported here (or in Tk) * is side effects targets. For these to be handled properly, MULTIPLE * targets need to be done in the order specified. This cannot be * guaranteed with the way we do things, since if we are doing INCR @@ -51,34 +51,44 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +#include "config.h" #include #include #include "gdk.h" -#if defined (GDK_WINDOWING_X11) -#include "x11/gdkx.h" /* For gdk_window_lookup() */ -#elif defined (GDK_WINDOWING_WIN32) -#include "win32/gdkwin32.h" /* For gdk_window_lookup() */ -#elif defined (GDK_WINDOWING_FB) -#include "linux-fb/gdkfb.h" /* For gdk_window_lookup() */ -#elif defined (GDK_WINDOWING_NANOX) -#include "nanox/gdkprivate-nanox.h" /* For gdk_window_lookup() */ -#endif - #include "gtkmain.h" #include "gtkselection.h" -#include "gtksignal.h" +#include "gtktextbufferrichtext.h" +#include "gtkintl.h" +#include "gdk-pixbuf/gdk-pixbuf.h" + +#ifdef GDK_WINDOWING_X11 +#include "x11/gdkx.h" +#endif + +#ifdef GDK_WINDOWING_WIN32 +#include "win32/gdkwin32.h" +#endif + +#include "gtkalias.h" -/* #define DEBUG_SELECTION */ +#undef DEBUG_SELECTION /* Maximum size of a sent chunk, in bytes. Also the default size of our buffers */ -#ifdef GDK_WINDOWING_WIN32 -/* No chunks on Win32 */ -#define GTK_SELECTION_MAX_SIZE G_MAXINT +#ifdef GDK_WINDOWING_X11 +#define GTK_SELECTION_MAX_SIZE(display) \ + MIN(262144, \ + XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) == 0 \ + ? XMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100 \ + : XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100) #else -#define GTK_SELECTION_MAX_SIZE 4000 +/* No chunks on Win32 */ +#define GTK_SELECTION_MAX_SIZE(display) G_MAXINT #endif + +#define IDLE_ABORT_TIME 30 + enum { INCR, MULTIPLE, @@ -94,9 +104,10 @@ typedef struct _GtkRetrievalInfo GtkRetrievalInfo; struct _GtkSelectionInfo { - GdkAtom selection; - GtkWidget *widget; /* widget that owns selection */ - guint32 time; /* time used to acquire selection */ + GdkAtom selection; + GtkWidget *widget; /* widget that owns selection */ + guint32 time; /* time used to acquire selection */ + GdkDisplay *display; /* needed in gtk_selection_remove_all */ }; struct _GtkIncrConversion @@ -112,7 +123,6 @@ struct _GtkIncrConversion struct _GtkIncrInfo { - GtkWidget *widget; /* Selection owner */ GdkWindow *requestor; /* Requestor window - we create a GdkWindow so we can receive events */ GdkAtom selection; /* Selection we're sending */ @@ -142,8 +152,8 @@ struct _GtkRetrievalInfo /* Local Functions */ static void gtk_selection_init (void); -static gint gtk_selection_incr_timeout (GtkIncrInfo *info); -static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info); +static gboolean gtk_selection_incr_timeout (GtkIncrInfo *info); +static gboolean gtk_selection_retrieval_timeout (GtkRetrievalInfo *info); static void gtk_selection_retrieval_report (GtkRetrievalInfo *info, GdkAtom type, gint format, @@ -164,7 +174,7 @@ static GList *current_incrs = NULL; static GList *current_selections = NULL; static GdkAtom gtk_selection_atoms[LAST_ATOM]; -static const char *gtk_selection_handler_key = "gtk-selection-handlers"; +static const char gtk_selection_handler_key[] = "gtk-selection-handlers"; /**************** * Target Lists * @@ -174,11 +184,21 @@ static const char *gtk_selection_handler_key = "gtk-selection-handlers"; * Target lists */ + +/** + * gtk_target_list_new: + * @targets: Pointer to an array of #GtkTargetEntry + * @ntargets: number of entries in @targets. + * + * Creates a new #GtkTargetList from an array of #GtkTargetEntry. + * + * Return value: the new #GtkTargetList. + **/ GtkTargetList * gtk_target_list_new (const GtkTargetEntry *targets, guint ntargets) { - GtkTargetList *result = g_new (GtkTargetList, 1); + GtkTargetList *result = g_slice_new (GtkTargetList); result->list = NULL; result->ref_count = 1; @@ -188,14 +208,31 @@ gtk_target_list_new (const GtkTargetEntry *targets, return result; } -void +/** + * gtk_target_list_ref: + * @list: a #GtkTargetList + * + * Increases the reference count of a #GtkTargetList by one. + * + * Return value: the passed in #GtkTargetList. + **/ +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; } +/** + * gtk_target_list_unref: + * @list: a #GtkTargetList + * + * Decreases the reference count of a #GtkTargetList by one. + * If the resulting reference count is zero, frees the list. + **/ void gtk_target_list_unref (GtkTargetList *list) { @@ -209,27 +246,36 @@ gtk_target_list_unref (GtkTargetList *list) while (tmp_list) { GtkTargetPair *pair = tmp_list->data; - g_free (pair); + g_slice_free (GtkTargetPair, pair); tmp_list = tmp_list->next; } g_list_free (list->list); - g_free (list); + g_slice_free (GtkTargetList, list); } } +/** + * gtk_target_list_add: + * @list: a #GtkTargetList + * @target: the interned atom representing the target + * @flags: the flags for this target + * @info: an ID that will be passed back to the application + * + * Appends another target to a #GtkTargetList. + **/ void gtk_target_list_add (GtkTargetList *list, - GdkAtom target, - guint flags, - guint info) + GdkAtom target, + guint flags, + guint info) { GtkTargetPair *pair; g_return_if_fail (list != NULL); - pair = g_new (GtkTargetPair, 1); + pair = g_slice_new (GtkTargetPair); pair->target = target; pair->flags = flags; pair->info = info; @@ -237,6 +283,198 @@ gtk_target_list_add (GtkTargetList *list, list->list = g_list_append (list->list, pair); } +static GdkAtom utf8_atom; +static GdkAtom text_atom; +static GdkAtom ctext_atom; +static GdkAtom text_plain_atom; +static GdkAtom text_plain_utf8_atom; +static GdkAtom text_plain_locale_atom; +static GdkAtom text_uri_list_atom; + +static void +init_atoms (void) +{ + gchar *tmp; + const gchar *charset; + + if (!utf8_atom) + { + utf8_atom = gdk_atom_intern_static_string ("UTF8_STRING"); + text_atom = gdk_atom_intern_static_string ("TEXT"); + ctext_atom = gdk_atom_intern_static_string ("COMPOUND_TEXT"); + text_plain_atom = gdk_atom_intern_static_string ("text/plain"); + text_plain_utf8_atom = gdk_atom_intern_static_string ("text/plain;charset=utf-8"); + g_get_charset (&charset); + tmp = g_strdup_printf ("text/plain;charset=%s", charset); + text_plain_locale_atom = gdk_atom_intern (tmp, FALSE); + g_free (tmp); + + text_uri_list_atom = gdk_atom_intern_static_string ("text/uri-list"); + } +} + +/** + * gtk_target_list_add_text_targets: + * @list: a #GtkTargetList + * @info: an ID that will be passed back to the application + * + * Appends the text targets supported by #GtkSelection to + * the target list. All targets are added with the same @info. + * + * Since: 2.6 + **/ +void +gtk_target_list_add_text_targets (GtkTargetList *list, + guint info) +{ + g_return_if_fail (list != NULL); + + init_atoms (); + + /* Keep in sync with gtk_selection_data_targets_include_text() + */ + gtk_target_list_add (list, utf8_atom, 0, info); + gtk_target_list_add (list, ctext_atom, 0, info); + gtk_target_list_add (list, text_atom, 0, info); + gtk_target_list_add (list, GDK_TARGET_STRING, 0, info); + gtk_target_list_add (list, text_plain_utf8_atom, 0, info); + if (!g_get_charset (NULL)) + gtk_target_list_add (list, text_plain_locale_atom, 0, info); + 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 + * @info: an ID that will be passed back to the application + * @writable: whether to add only targets for which GTK+ knows + * how to convert a pixbuf into the format + * + * Appends the image targets supported by #GtkSelection to + * the target list. All targets are added with the same @info. + * + * Since: 2.6 + **/ +void +gtk_target_list_add_image_targets (GtkTargetList *list, + guint info, + gboolean writable) +{ + GSList *formats, *f; + gchar **mimes, **m; + GdkAtom atom; + + g_return_if_fail (list != NULL); + + formats = gdk_pixbuf_get_formats (); + + /* Make sure png comes first */ + for (f = formats; f; f = f->next) + { + GdkPixbufFormat *fmt = f->data; + gchar *name; + + name = gdk_pixbuf_format_get_name (fmt); + if (strcmp (name, "png") == 0) + { + formats = g_slist_delete_link (formats, f); + formats = g_slist_prepend (formats, fmt); + + g_free (name); + + break; + } + + g_free (name); + } + + for (f = formats; f; f = f->next) + { + GdkPixbufFormat *fmt = f->data; + + if (writable && !gdk_pixbuf_format_is_writable (fmt)) + continue; + + mimes = gdk_pixbuf_format_get_mime_types (fmt); + for (m = mimes; *m; m++) + { + atom = gdk_atom_intern (*m, FALSE); + gtk_target_list_add (list, atom, 0, info); + } + g_strfreev (mimes); + } + + g_slist_free (formats); +} + +/** + * gtk_target_list_add_uri_targets: + * @list: a #GtkTargetList + * @info: an ID that will be passed back to the application + * + * Appends the URI targets supported by #GtkSelection to + * the target list. All targets are added with the same @info. + * + * Since: 2.6 + **/ +void +gtk_target_list_add_uri_targets (GtkTargetList *list, + guint info) +{ + g_return_if_fail (list != NULL); + + init_atoms (); + + gtk_target_list_add (list, text_uri_list_atom, 0, info); +} + +/** + * gtk_target_list_add_table: + * @list: a #GtkTargetList + * @targets: the table of #GtkTargetEntry + * @ntargets: number of targets in the table + * + * Prepends a table of #GtkTargetEntry to a target list. + **/ void gtk_target_list_add_table (GtkTargetList *list, const GtkTargetEntry *targets, @@ -246,7 +484,7 @@ gtk_target_list_add_table (GtkTargetList *list, for (i=ntargets-1; i >= 0; i--) { - GtkTargetPair *pair = g_new (GtkTargetPair, 1); + GtkTargetPair *pair = g_slice_new (GtkTargetPair); pair->target = gdk_atom_intern (targets[i].target, FALSE); pair->flags = targets[i].flags; pair->info = targets[i].info; @@ -255,6 +493,13 @@ gtk_target_list_add_table (GtkTargetList *list, } } +/** + * gtk_target_list_remove: + * @list: a #GtkTargetList + * @target: the interned atom representing the target + * + * Removes a target from a target list. + **/ void gtk_target_list_remove (GtkTargetList *list, GdkAtom target) @@ -270,7 +515,7 @@ gtk_target_list_remove (GtkTargetList *list, if (pair->target == target) { - g_free (pair); + g_slice_free (GtkTargetPair, pair); list->list = g_list_remove_link (list->list, tmp_list); g_list_free_1 (tmp_list); @@ -282,50 +527,140 @@ gtk_target_list_remove (GtkTargetList *list, } } +/** + * gtk_target_list_find: + * @list: a #GtkTargetList + * @target: an interned atom representing the target to search for + * @info: a pointer to the location to store application info for target, + * or %NULL + * + * Looks up a given target in a #GtkTargetList. + * + * Return value: %TRUE if the target was found, otherwise %FALSE + **/ gboolean gtk_target_list_find (GtkTargetList *list, GdkAtom target, guint *info) { - GList *tmp_list = list->list; + GList *tmp_list; + + g_return_val_if_fail (list != NULL, FALSE); + + tmp_list = list->list; while (tmp_list) { GtkTargetPair *pair = tmp_list->data; if (pair->target == target) { - *info = pair->info; + if (info) + *info = pair->info; + return TRUE; } + tmp_list = tmp_list->next; } 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; -/************************************************************* - * gtk_selection_owner_set: - * Claim ownership of a selection. - * arguments: - * widget: new selection owner - * selection: which selection - * time: time (use GDK_CURRENT_TIME only if necessary) + 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 * - * results: - *************************************************************/ + * 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 + * @widget: new selection owner (a #GdkWidget), or %NULL. + * @selection: an interned atom representing the selection to claim. + * @time_: timestamp with which to claim the selection + * + * Claim ownership of a given selection for a particular widget, or, + * if @widget is %NULL, release ownership of the selection. + * + * Return value: TRUE if the operation succeeded + * + * Since: 2.2 + */ gboolean -gtk_selection_owner_set (GtkWidget *widget, - GdkAtom selection, - guint32 time) +gtk_selection_owner_set_for_display (GdkDisplay *display, + GtkWidget *widget, + GdkAtom selection, + guint32 time) { GList *tmp_list; GtkWidget *old_owner; GtkSelectionInfo *selection_info = NULL; GdkWindow *window; + g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE); + g_return_val_if_fail (selection != GDK_NONE, FALSE); g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE); + g_return_val_if_fail (widget == NULL || gtk_widget_get_display (widget) == display, FALSE); if (widget == NULL) window = NULL; @@ -344,7 +679,7 @@ gtk_selection_owner_set (GtkWidget *widget, tmp_list = tmp_list->next; } - if (gdk_selection_owner_set (window, selection, time, TRUE)) + if (gdk_selection_owner_set_for_display (display, window, selection, time, TRUE)) { old_owner = NULL; @@ -356,18 +691,19 @@ gtk_selection_owner_set (GtkWidget *widget, current_selections = g_list_remove_link (current_selections, tmp_list); g_list_free (tmp_list); - g_free (selection_info); + g_slice_free (GtkSelectionInfo, selection_info); } } else { if (selection_info == NULL) { - selection_info = g_new (GtkSelectionInfo, 1); + selection_info = g_slice_new (GtkSelectionInfo); selection_info->selection = selection; selection_info->widget = widget; selection_info->time = time; - current_selections = g_list_prepend (current_selections, + selection_info->display = display; + current_selections = g_list_prepend (current_selections, selection_info); } else @@ -375,6 +711,7 @@ gtk_selection_owner_set (GtkWidget *widget, old_owner = selection_info->widget; selection_info->widget = widget; selection_info->time = time; + selection_info->display = display; } } /* If another widget in the application lost the selection, @@ -382,14 +719,15 @@ gtk_selection_owner_set (GtkWidget *widget, */ if (old_owner && old_owner != widget) { - GdkEventSelection event; + GdkEvent *event = gdk_event_new (GDK_SELECTION_CLEAR); - event.type = GDK_SELECTION_CLEAR; - event.window = old_owner->window; - event.selection = selection; - event.time = time; + event->selection.window = g_object_ref (old_owner->window); + event->selection.selection = selection; + event->selection.time = time; - gtk_widget_event (old_owner, (GdkEvent *) &event); + gtk_widget_event (old_owner, event); + + gdk_event_free (event); } return TRUE; } @@ -397,18 +735,40 @@ gtk_selection_owner_set (GtkWidget *widget, return FALSE; } -/************************************************************* - * gtk_selection_add_target - * Add specified target to list of supported targets - * - * arguments: - * widget: The widget for which this target applies - * selection: - * target: - * info: guint to pass to to the selection_get signal - * - * results: - *************************************************************/ +/** + * gtk_selection_owner_set: + * @widget: a #GtkWidget, or %NULL. + * @selection: an interned atom representing the selection to claim + * @time_: timestamp with which to claim the selection + * + * Claims ownership of a given selection for a particular widget, + * or, if @widget is %NULL, release ownership of the selection. + * + * Return value: %TRUE if the operation succeeded + **/ +gboolean +gtk_selection_owner_set (GtkWidget *widget, + GdkAtom selection, + guint32 time) +{ + GdkDisplay *display; + + g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE); + g_return_val_if_fail (selection != GDK_NONE, FALSE); + + if (widget) + display = gtk_widget_get_display (widget); + else + { + GTK_NOTE (MULTIHEAD, + g_warning ("gtk_selection_owner_set (NULL,...) is not multihead safe")); + + display = gdk_display_get_default (); + } + + return gtk_selection_owner_set_for_display (display, widget, + selection, time); +} typedef struct _GtkSelectionTargetList GtkSelectionTargetList; @@ -425,7 +785,7 @@ gtk_selection_target_list_get (GtkWidget *widget, GList *tmp_list; GList *lists; - lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key); + lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key); tmp_list = lists; while (tmp_list) @@ -436,12 +796,12 @@ gtk_selection_target_list_get (GtkWidget *widget, tmp_list = tmp_list->next; } - sellist = g_new (GtkSelectionTargetList, 1); + sellist = g_slice_new (GtkSelectionTargetList); sellist->selection = selection; sellist->list = gtk_target_list_new (NULL, 0); lists = g_list_prepend (lists, sellist); - gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists); + g_object_set_data (G_OBJECT (widget), I_(gtk_selection_handler_key), lists); return sellist->list; } @@ -453,7 +813,7 @@ gtk_selection_target_list_remove (GtkWidget *widget) GList *tmp_list; GList *lists; - lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key); + lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key); tmp_list = lists; while (tmp_list) @@ -462,12 +822,12 @@ gtk_selection_target_list_remove (GtkWidget *widget) gtk_target_list_unref (sellist->list); - g_free (sellist); + g_slice_free (GtkSelectionTargetList, sellist); tmp_list = tmp_list->next; } g_list_free (lists); - gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL); + g_object_set_data (G_OBJECT (widget), I_(gtk_selection_handler_key), NULL); } /** @@ -486,7 +846,10 @@ gtk_selection_clear_targets (GtkWidget *widget, GList *tmp_list; GList *lists; - lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (selection != GDK_NONE); + + lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key); tmp_list = lists; while (tmp_list) @@ -496,7 +859,7 @@ gtk_selection_clear_targets (GtkWidget *widget, { lists = g_list_delete_link (lists, tmp_list); gtk_target_list_unref (sellist->list); - g_free (sellist); + g_slice_free (GtkSelectionTargetList, sellist); break; } @@ -504,9 +867,19 @@ gtk_selection_clear_targets (GtkWidget *widget, tmp_list = tmp_list->next; } - gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists); + g_object_set_data (G_OBJECT (widget), I_(gtk_selection_handler_key), lists); } +/** + * gtk_selection_add_target: + * @widget: a #GtkTarget + * @selection: the selection + * @target: target to add. + * @info: A unsigned integer which will be passed back to the application. + * + * Appends a specified target to the list of supported targets for a + * given widget and selection. + **/ void gtk_selection_add_target (GtkWidget *widget, GdkAtom selection, @@ -515,12 +888,26 @@ gtk_selection_add_target (GtkWidget *widget, { GtkTargetList *list; - g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (selection != GDK_NONE); list = gtk_selection_target_list_get (widget, selection); gtk_target_list_add (list, target, 0, info); +#ifdef GDK_WINDOWING_WIN32 + gdk_win32_selection_add_targets (widget->window, selection, 1, &target); +#endif } +/** + * gtk_selection_add_targets: + * @widget: a #GtkWidget + * @selection: the selection + * @targets: a table of targets to add + * @ntargets: number of entries in @targets + * + * Prepends a table of targets to the list of supported targets + * for a given widget and selection. + **/ void gtk_selection_add_targets (GtkWidget *widget, GdkAtom selection, @@ -528,48 +915,48 @@ gtk_selection_add_targets (GtkWidget *widget, guint ntargets) { GtkTargetList *list; - - g_return_if_fail (widget != NULL); + + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (selection != GDK_NONE); g_return_if_fail (targets != NULL); list = gtk_selection_target_list_get (widget, selection); gtk_target_list_add_table (list, targets, ntargets); + +#ifdef GDK_WINDOWING_WIN32 + { + int i; + GdkAtom *atoms = g_new (GdkAtom, ntargets); + + for (i = 0; i < ntargets; ++i) + atoms[i] = gdk_atom_intern (targets[i].target, FALSE); + gdk_win32_selection_add_targets (widget->window, selection, ntargets, atoms); + g_free (atoms); + } +#endif } -/************************************************************* +/** * gtk_selection_remove_all: - * Removes all handlers and unsets ownership of all - * selections for a widget. Called when widget is being - * destroyed - * - * arguments: - * widget: The widget - * results: - *************************************************************/ - + * @widget: a #GtkWidget + * + * Removes all handlers and unsets ownership of all + * selections for a widget. Called when widget is being + * destroyed. This function will not generally be + * called by applications. + **/ void gtk_selection_remove_all (GtkWidget *widget) { GList *tmp_list; GList *next; GtkSelectionInfo *selection_info; - + + g_return_if_fail (GTK_IS_WIDGET (widget)); + /* Remove pending requests/incrs for this widget */ - tmp_list = current_incrs; - while (tmp_list) - { - next = tmp_list->next; - if (((GtkIncrInfo *)tmp_list->data)->widget == widget) - { - current_incrs = g_list_remove_link (current_incrs, tmp_list); - /* structure will be freed in timeout */ - g_list_free (tmp_list); - } - tmp_list = next; - } - tmp_list = current_retrievals; while (tmp_list) { @@ -594,13 +981,14 @@ gtk_selection_remove_all (GtkWidget *widget) if (selection_info->widget == widget) { - gdk_selection_owner_set (NULL, - selection_info->selection, - GDK_CURRENT_TIME, FALSE); - current_selections = g_list_remove_link (current_selections, + gdk_selection_owner_set_for_display (selection_info->display, + NULL, + selection_info->selection, + GDK_CURRENT_TIME, FALSE); + current_selections = g_list_remove_link (current_selections, tmp_list); g_list_free (tmp_list); - g_free (selection_info); + g_slice_free (GtkSelectionInfo, selection_info); } tmp_list = next; @@ -610,35 +998,35 @@ gtk_selection_remove_all (GtkWidget *widget) gtk_selection_target_list_remove (widget); } -/************************************************************* - * gtk_selection_convert: - * Request the contents of a selection. When received, - * a "selection_received" signal will be generated. - * - * arguments: - * widget: The widget which acts as requestor - * selection: Which selection to get - * target: Form of information desired (e.g., STRING) - * time: Time of request (usually of triggering event) - * In emergency, you could use GDK_CURRENT_TIME - * - * results: - * TRUE if requested succeeded. FALSE if we could not process - * request. (e.g., there was already a request in process for - * this widget). - *************************************************************/ +/** + * gtk_selection_convert: + * @widget: The widget which acts as requestor + * @selection: Which selection to get + * @target: Form of information desired (e.g., STRING) + * @time_: Time of request (usually of triggering event) + In emergency, you could use #GDK_CURRENT_TIME + * + * Requests the contents of a selection. When received, + * a "selection-received" signal will be generated. + * + * Return value: %TRUE if requested succeeded. %FALSE if we could not process + * request. (e.g., there was already a request in process for + * this widget). + **/ gboolean gtk_selection_convert (GtkWidget *widget, GdkAtom selection, GdkAtom target, - guint32 time) + guint32 time_) { GtkRetrievalInfo *info; GList *tmp_list; GdkWindow *owner_window; + GdkDisplay *display; - g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + g_return_val_if_fail (selection != GDK_NONE, FALSE); if (initialize) gtk_selection_init (); @@ -661,47 +1049,54 @@ gtk_selection_convert (GtkWidget *widget, tmp_list = tmp_list->next; } - info = g_new (GtkRetrievalInfo, 1); + info = g_slice_new (GtkRetrievalInfo); info->widget = widget; info->selection = selection; info->target = target; + info->idle_time = 0; info->buffer = NULL; info->offset = -1; /* Check if this process has current owner. If so, call handler procedure directly to avoid deadlocks with INCR. */ - - owner_window = gdk_selection_owner_get (selection); + + display = gtk_widget_get_display (widget); + owner_window = gdk_selection_owner_get_for_display (display, selection); if (owner_window != NULL) { GtkWidget *owner_widget; + gpointer owner_widget_ptr; GtkSelectionData selection_data; selection_data.selection = selection; selection_data.target = target; selection_data.data = NULL; selection_data.length = -1; + selection_data.display = display; - gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget); + gdk_window_get_user_data (owner_window, &owner_widget_ptr); + owner_widget = owner_widget_ptr; if (owner_widget != NULL) { gtk_selection_invoke_handler (owner_widget, &selection_data, - time); + time_); gtk_selection_retrieval_report (info, selection_data.type, selection_data.format, selection_data.data, selection_data.length, - time); + time_); g_free (selection_data.data); + selection_data.data = NULL; + selection_data.length = -1; - g_free (info); + g_slice_free (GtkRetrievalInfo, info); return TRUE; } } @@ -709,173 +1104,1056 @@ gtk_selection_convert (GtkWidget *widget, /* Otherwise, we need to go through X */ current_retrievals = g_list_append (current_retrievals, info); - gdk_selection_convert (widget->window, selection, target, time); - gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info); + gdk_selection_convert (widget->window, selection, target, time_); + gdk_threads_add_timeout (1000, + (GSourceFunc) gtk_selection_retrieval_timeout, info); return TRUE; } -/************************************************************* - * gtk_selection_data_set: - * Store new data into a GtkSelectionData object. Should - * _only_ by called from a selection handler callback. - * Null terminates the stored data. - * arguments: - * type: the type of selection data - * format: format (number of bits in a unit) - * data: pointer to the data (will be copied) - * length: length of the data - * results: - *************************************************************/ - -void -gtk_selection_data_set (GtkSelectionData *selection_data, - GdkAtom type, +/** + * gtk_selection_data_get_selection: + * @selection_data: a pointer to a #GtkSelectionData structure. + * + * Retrieves the selection #GdkAtom of the selection data. + * + * Returns: the selection #GdkAtom of the selection data. + * + * Since: 2.16 + **/ +GdkAtom +gtk_selection_data_get_selection (GtkSelectionData *selection_data) +{ + g_return_val_if_fail (selection_data != NULL, 0); + + return selection_data->selection; +} + +/** + * gtk_selection_data_get_target: + * @selection_data: a pointer to a #GtkSelectionData structure. + * + * Retrieves the target of the selection. + * + * Returns: the target of the selection. + * + * Since: 2.14 + **/ +GdkAtom +gtk_selection_data_get_target (GtkSelectionData *selection_data) +{ + g_return_val_if_fail (selection_data != NULL, 0); + + return selection_data->target; +} + +/** + * gtk_selection_data_get_data_type: + * @selection_data: a pointer to a #GtkSelectionData structure. + * + * Retrieves the data type of the selection. + * + * Returns: the data type of the selection. + * + * Since: 2.14 + **/ +GdkAtom +gtk_selection_data_get_data_type (GtkSelectionData *selection_data) +{ + g_return_val_if_fail (selection_data != NULL, 0); + + return selection_data->type; +} + +/** + * gtk_selection_data_get_format: + * @selection_data: a pointer to a #GtkSelectionData structure. + * + * Retrieves the format of the selection. + * + * Returns: the format of the selection. + * + * Since: 2.14 + **/ +gint +gtk_selection_data_get_format (GtkSelectionData *selection_data) +{ + g_return_val_if_fail (selection_data != NULL, 0); + + return selection_data->format; +} + +/** + * gtk_selection_data_get_data: + * @selection_data: a pointer to a #GtkSelectionData structure. + * + * Retrieves the raw data of the selection. + * + * Returns: the raw data of the selection. + * + * Since: 2.14 + **/ +const guchar* +gtk_selection_data_get_data (GtkSelectionData *selection_data) +{ + g_return_val_if_fail (selection_data != NULL, NULL); + + return selection_data->data; +} + +/** + * gtk_selection_data_get_length: + * @selection_data: a pointer to a #GtkSelectionData structure. + * + * Retrieves the length of the raw data of the selection. + * + * Returns: the length of the data of the selection. + * + * Since: 2.14 + */ +gint +gtk_selection_data_get_length (GtkSelectionData *selection_data) +{ + g_return_val_if_fail (selection_data != NULL, -1); + + return selection_data->length; +} + +/** + * gtk_selection_data_get_display: + * @selection_data: a pointer to a #GtkSelectionData structure. + * + * Retrieves the display of the selection. + * + * Returns: the display of the selection. + * + * Since: 2.14 + **/ +GdkDisplay * +gtk_selection_data_get_display (GtkSelectionData *selection_data) +{ + g_return_val_if_fail (selection_data != NULL, NULL); + + return selection_data->display; +} + +/** + * gtk_selection_data_set: + * @selection_data: a pointer to a #GtkSelectionData structure. + * @type: the type of selection data + * @format: format (number of bits in a unit) + * @data: pointer to the data (will be copied) + * @length: length of the data + * + * Stores new data into a #GtkSelectionData object. Should + * only be called from a selection handler callback. + * Zero-terminates the stored data. + **/ +void +gtk_selection_data_set (GtkSelectionData *selection_data, + GdkAtom type, gint format, const guchar *data, gint length) { - if (selection_data->data) - g_free (selection_data->data); + g_return_if_fail (selection_data != NULL); + + g_free (selection_data->data); selection_data->type = type; selection_data->format = format; if (data) { - selection_data->data = g_new (guchar, length+1); - memcpy (selection_data->data, data, length); - selection_data->data[length] = 0; + selection_data->data = g_new (guchar, length+1); + memcpy (selection_data->data, data, length); + selection_data->data[length] = 0; + } + else + { + g_return_if_fail (length <= 0); + + if (length < 0) + selection_data->data = NULL; + else + selection_data->data = (guchar *) g_strdup (""); + } + + selection_data->length = length; +} + +static gboolean +selection_set_string (GtkSelectionData *selection_data, + const gchar *str, + gint len) +{ + gchar *tmp = g_strndup (str, len); + gchar *latin1 = gdk_utf8_to_string_target (tmp); + g_free (tmp); + + if (latin1) + { + gtk_selection_data_set (selection_data, + GDK_SELECTION_TYPE_STRING, + 8, (guchar *) latin1, strlen (latin1)); + g_free (latin1); + + return TRUE; + } + else + return FALSE; +} + +static gboolean +selection_set_compound_text (GtkSelectionData *selection_data, + const gchar *str, + gint len) +{ + gchar *tmp; + guchar *text; + GdkAtom encoding; + gint format; + gint new_length; + gboolean result = FALSE; + + tmp = g_strndup (str, len); + if (gdk_utf8_to_compound_text_for_display (selection_data->display, tmp, + &encoding, &format, &text, &new_length)) + { + gtk_selection_data_set (selection_data, encoding, format, text, new_length); + gdk_free_compound_text (text); + + result = TRUE; + } + + g_free (tmp); + + return result; +} + +/* Normalize \r and \n into \r\n + */ +static gchar * +normalize_to_crlf (const gchar *str, + gint len) +{ + GString *result = g_string_sized_new (len); + const gchar *p = str; + const gchar *end = str + len; + + while (p < end) + { + if (*p == '\n') + g_string_append_c (result, '\r'); + + if (*p == '\r') + { + g_string_append_c (result, *p); + p++; + if (p == end || *p != '\n') + g_string_append_c (result, '\n'); + if (p == end) + break; + } + + g_string_append_c (result, *p); + p++; + } + + return g_string_free (result, FALSE); +} + +/* Normalize \r and \r\n into \n + */ +static gchar * +normalize_to_lf (gchar *str, + gint len) +{ + GString *result = g_string_sized_new (len); + const gchar *p = str; + + while (1) + { + if (*p == '\r') + { + p++; + if (*p != '\n') + g_string_append_c (result, '\n'); + } + + if (*p == '\0') + break; + + g_string_append_c (result, *p); + p++; + } + + return g_string_free (result, FALSE); +} + +static gboolean +selection_set_text_plain (GtkSelectionData *selection_data, + const gchar *str, + gint len) +{ + const gchar *charset = NULL; + gchar *result; + GError *error = NULL; + + result = normalize_to_crlf (str, len); + if (selection_data->target == text_plain_atom) + charset = "ASCII"; + else if (selection_data->target == text_plain_locale_atom) + g_get_charset (&charset); + + if (charset) + { + gchar *tmp = result; + result = g_convert_with_fallback (tmp, -1, + charset, "UTF-8", + NULL, NULL, NULL, &error); + g_free (tmp); + } + + if (!result) + { + g_warning ("Error converting from %s to %s: %s", + "UTF-8", charset, error->message); + g_error_free (error); + + return FALSE; + } + + gtk_selection_data_set (selection_data, + selection_data->target, + 8, (guchar *) result, strlen (result)); + g_free (result); + + return TRUE; +} + +static guchar * +selection_get_text_plain (GtkSelectionData *selection_data) +{ + const gchar *charset = NULL; + gchar *str, *result; + gsize len; + GError *error = NULL; + + str = g_strdup ((const gchar *) selection_data->data); + len = selection_data->length; + + if (selection_data->type == text_plain_atom) + charset = "ISO-8859-1"; + else if (selection_data->type == text_plain_locale_atom) + g_get_charset (&charset); + + if (charset) + { + gchar *tmp = str; + str = g_convert_with_fallback (tmp, len, + "UTF-8", charset, + NULL, NULL, &len, &error); + g_free (tmp); + + if (!str) + { + g_warning ("Error converting from %s to %s: %s", + charset, "UTF-8", error->message); + g_error_free (error); + + return NULL; + } + } + else if (!g_utf8_validate (str, -1, NULL)) + { + g_warning ("Error converting from %s to %s: %s", + "text/plain;charset=utf-8", "UTF-8", "invalid UTF-8"); + g_free (str); + + return NULL; + } + + result = normalize_to_lf (str, len); + g_free (str); + + return (guchar *) result; +} + +/** + * gtk_selection_data_set_text: + * @selection_data: a #GtkSelectionData + * @str: a UTF-8 string + * @len: the length of @str, or -1 if @str is nul-terminated. + * + * Sets the contents of the selection from a UTF-8 encoded string. + * The string is converted to the form determined by + * @selection_data->target. + * + * Return value: %TRUE if the selection was successfully set, + * otherwise %FALSE. + **/ +gboolean +gtk_selection_data_set_text (GtkSelectionData *selection_data, + const gchar *str, + gint len) +{ + g_return_val_if_fail (selection_data != NULL, FALSE); + + if (len < 0) + len = strlen (str); + + init_atoms (); + + if (selection_data->target == utf8_atom) + { + gtk_selection_data_set (selection_data, + utf8_atom, + 8, (guchar *)str, len); + return TRUE; + } + else if (selection_data->target == GDK_TARGET_STRING) + { + return selection_set_string (selection_data, str, len); + } + else if (selection_data->target == ctext_atom || + selection_data->target == text_atom) + { + if (selection_set_compound_text (selection_data, str, len)) + return TRUE; + else if (selection_data->target == text_atom) + return selection_set_string (selection_data, str, len); + } + else if (selection_data->target == text_plain_atom || + selection_data->target == text_plain_utf8_atom || + selection_data->target == text_plain_locale_atom) + { + return selection_set_text_plain (selection_data, str, len); + } + + return FALSE; +} + +/** + * gtk_selection_data_get_text: + * @selection_data: a #GtkSelectionData + * + * Gets the contents of the selection data as a UTF-8 string. + * + * Return value: if the selection data contained a recognized + * text type and it could be converted to UTF-8, a newly allocated + * string containing the converted text, otherwise %NULL. + * If the result is non-%NULL it must be freed with g_free(). + **/ +guchar * +gtk_selection_data_get_text (GtkSelectionData *selection_data) +{ + guchar *result = NULL; + + g_return_val_if_fail (selection_data != NULL, NULL); + + init_atoms (); + + if (selection_data->length >= 0 && + (selection_data->type == GDK_TARGET_STRING || + selection_data->type == ctext_atom || + selection_data->type == utf8_atom)) + { + gchar **list; + gint i; + gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display, + selection_data->type, + selection_data->format, + selection_data->data, + selection_data->length, + &list); + if (count > 0) + result = (guchar *) list[0]; + + for (i = 1; i < count; i++) + g_free (list[i]); + g_free (list); + } + else if (selection_data->length >= 0 && + (selection_data->type == text_plain_atom || + selection_data->type == text_plain_utf8_atom || + selection_data->type == text_plain_locale_atom)) + { + result = selection_get_text_plain (selection_data); + } + + return result; +} + +/** + * gtk_selection_data_set_pixbuf: + * @selection_data: a #GtkSelectionData + * @pixbuf: a #GdkPixbuf + * + * Sets the contents of the selection from a #GdkPixbuf + * The pixbuf is converted to the form determined by + * @selection_data->target. + * + * Return value: %TRUE if the selection was successfully set, + * otherwise %FALSE. + * + * Since: 2.6 + **/ +gboolean +gtk_selection_data_set_pixbuf (GtkSelectionData *selection_data, + GdkPixbuf *pixbuf) +{ + GSList *formats, *f; + gchar **mimes, **m; + GdkAtom atom; + gboolean result; + gchar *str, *type; + gsize len; + + g_return_val_if_fail (selection_data != NULL, FALSE); + g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE); + + formats = gdk_pixbuf_get_formats (); + + for (f = formats; f; f = f->next) + { + GdkPixbufFormat *fmt = f->data; + + mimes = gdk_pixbuf_format_get_mime_types (fmt); + for (m = mimes; *m; m++) + { + atom = gdk_atom_intern (*m, FALSE); + if (selection_data->target == atom) + { + str = NULL; + type = gdk_pixbuf_format_get_name (fmt); + result = gdk_pixbuf_save_to_buffer (pixbuf, &str, &len, + type, NULL, + ((strcmp (type, "png") == 0) ? + "compression" : NULL), "2", + NULL); + if (result) + gtk_selection_data_set (selection_data, + atom, 8, (guchar *)str, len); + g_free (type); + g_free (str); + g_strfreev (mimes); + g_slist_free (formats); + + return result; + } + } + + g_strfreev (mimes); + } + + g_slist_free (formats); + + return FALSE; +} + +/** + * gtk_selection_data_get_pixbuf: + * @selection_data: a #GtkSelectionData + * + * Gets the contents of the selection data as a #GdkPixbuf. + * + * Return value: if the selection data contained a recognized + * image type and it could be converted to a #GdkPixbuf, a + * newly allocated pixbuf is returned, otherwise %NULL. + * If the result is non-%NULL it must be freed with g_object_unref(). + * + * Since: 2.6 + **/ +GdkPixbuf * +gtk_selection_data_get_pixbuf (GtkSelectionData *selection_data) +{ + GdkPixbufLoader *loader; + GdkPixbuf *result = NULL; + + g_return_val_if_fail (selection_data != NULL, NULL); + + if (selection_data->length > 0) + { + loader = gdk_pixbuf_loader_new (); + + gdk_pixbuf_loader_write (loader, + selection_data->data, + selection_data->length, + NULL); + gdk_pixbuf_loader_close (loader, NULL); + result = gdk_pixbuf_loader_get_pixbuf (loader); + + if (result) + g_object_ref (result); + + g_object_unref (loader); + } + + return result; +} + +/** + * gtk_selection_data_set_uris: + * @selection_data: a #GtkSelectionData + * @uris: a %NULL-terminated array of strings hilding URIs + * + * Sets the contents of the selection from a list of URIs. + * The string is converted to the form determined by + * @selection_data->target. + * + * Return value: %TRUE if the selection was successfully set, + * otherwise %FALSE. + * + * Since: 2.6 + **/ +gboolean +gtk_selection_data_set_uris (GtkSelectionData *selection_data, + gchar **uris) +{ + g_return_val_if_fail (selection_data != NULL, FALSE); + g_return_val_if_fail (uris != NULL, FALSE); + + init_atoms (); + + if (selection_data->target == text_uri_list_atom) + { + GString *list; + gint i; + gchar *result; + gsize length; + + list = g_string_new (NULL); + for (i = 0; uris[i]; i++) + { + g_string_append (list, uris[i]); + g_string_append (list, "\r\n"); + } + + result = g_convert (list->str, list->len, + "ASCII", "UTF-8", + NULL, &length, NULL); + g_string_free (list, TRUE); + + if (result) + { + gtk_selection_data_set (selection_data, + text_uri_list_atom, + 8, (guchar *)result, length); + + g_free (result); + + return TRUE; + } + } + + return FALSE; +} + +/** + * gtk_selection_data_get_uris: + * @selection_data: a #GtkSelectionData + * + * Gets the contents of the selection data as array of URIs. + * + * Return value: if the selection data contains a list of + * URIs, a newly allocated %NULL-terminated string array + * containing the URIs, otherwise %NULL. If the result is + * non-%NULL it must be freed with g_strfreev(). + * + * Since: 2.6 + **/ +gchar ** +gtk_selection_data_get_uris (GtkSelectionData *selection_data) +{ + gchar **result = NULL; + + g_return_val_if_fail (selection_data != NULL, NULL); + + init_atoms (); + + if (selection_data->length >= 0 && + selection_data->type == text_uri_list_atom) + { + gchar **list; + gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display, + utf8_atom, + selection_data->format, + selection_data->data, + selection_data->length, + &list); + if (count > 0) + result = g_uri_list_extract_uris (list[0]); + + g_strfreev (list); + } + + return result; +} + + +/** + * gtk_selection_data_get_targets: + * @selection_data: a #GtkSelectionData object + * @targets: location to store an array of targets. The result + * stored here must be freed with g_free(). + * @n_atoms: location to store number of items in @targets. + * + * Gets the contents of @selection_data as an array of targets. + * This can be used to interpret the results of getting + * the standard TARGETS target that is always supplied for + * any selection. + * + * Return value: %TRUE if @selection_data contains a valid + * array of targets, otherwise %FALSE. + **/ +gboolean +gtk_selection_data_get_targets (GtkSelectionData *selection_data, + GdkAtom **targets, + gint *n_atoms) +{ + g_return_val_if_fail (selection_data != NULL, FALSE); + + if (selection_data->length >= 0 && + selection_data->format == 32 && + selection_data->type == GDK_SELECTION_TYPE_ATOM) + { + if (targets) + *targets = g_memdup (selection_data->data, selection_data->length); + if (n_atoms) + *n_atoms = selection_data->length / sizeof (GdkAtom); + + return TRUE; } else { - g_return_if_fail (length <= 0); - - if (length < 0) - selection_data->data = NULL; - else - selection_data->data = g_strdup(""); + if (targets) + *targets = NULL; + if (n_atoms) + *n_atoms = -1; + + return FALSE; } - - selection_data->length = length; } -static GdkAtom utf8_atom; -static GdkAtom text_atom; -static GdkAtom ctext_atom; +/** + * gtk_targets_include_text: + * @targets: an array of #GdkAtoms + * @n_targets: the length of @targets + * + * Determines if any of the targets in @targets can be used to + * provide text. + * + * Return value: %TRUE if @targets include a suitable target for text, + * otherwise %FALSE. + * + * Since: 2.10 + **/ +gboolean +gtk_targets_include_text (GdkAtom *targets, + gint n_targets) +{ + gint i; + gboolean result = FALSE; -static void -init_atoms (void) + g_return_val_if_fail (targets != NULL || n_targets == 0, FALSE); + + /* Keep in sync with gtk_target_list_add_text_targets() + */ + + init_atoms (); + + for (i = 0; i < n_targets; i++) + { + if (targets[i] == utf8_atom || + targets[i] == text_atom || + targets[i] == GDK_TARGET_STRING || + targets[i] == ctext_atom || + targets[i] == text_plain_atom || + targets[i] == text_plain_utf8_atom || + targets[i] == text_plain_locale_atom) + { + result = TRUE; + break; + } + } + + return result; +} + +/** + * gtk_targets_include_rich_text: + * @targets: an array of #GdkAtoms + * @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) { - if (!utf8_atom) + GdkAtom *rich_targets; + gint n_rich_targets; + gint i, j; + gboolean result = FALSE; + + g_return_val_if_fail (targets != NULL || n_targets == 0, FALSE); + g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE); + + init_atoms (); + + rich_targets = gtk_text_buffer_get_deserialize_formats (buffer, + &n_rich_targets); + + for (i = 0; i < n_targets; i++) { - utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE); - text_atom = gdk_atom_intern ("TEXT", FALSE); - ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE); + 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_set_text: - * @selection_data: a #GtkSelectionData - * @str: a UTF-8 string + * gtk_selection_data_targets_include_text: + * @selection_data: a #GtkSelectionData object * - * Sets the contents of the selection from a UTF-8 encoded string. - * The string is converted to the form determined by - * @selection_data->target. + * Given a #GtkSelectionData object holding a list of targets, + * determines if any of the targets in @targets can be used to + * provide text. * - * Return value: %TRUE if the selection was succesfully set, - * otherwise %FALSE. + * Return value: %TRUE if @selection_data holds a list of targets, + * and a suitable target for text is included, otherwise %FALSE. **/ gboolean -gtk_selection_data_set_text (GtkSelectionData *selection_data, - const guchar *str) +gtk_selection_data_targets_include_text (GtkSelectionData *selection_data) { + GdkAtom *targets; + gint n_targets; + gboolean result = FALSE; + + g_return_val_if_fail (selection_data != NULL, FALSE); + init_atoms (); - if (selection_data->target == utf8_atom) + if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets)) { - gtk_selection_data_set (selection_data, - utf8_atom, - 8, (guchar *)str, strlen (str)); - return TRUE; + result = gtk_targets_include_text (targets, n_targets); + g_free (targets); } - else if (selection_data->target == GDK_TARGET_STRING) + + 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 (selection_data != NULL, 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)) { - gchar *latin1 = gdk_utf8_to_string_target (str); + result = gtk_targets_include_rich_text (targets, n_targets, buffer); + g_free (targets); + } + + return result; +} + +/** + * gtk_targets_include_image: + * @targets: an array of #GdkAtoms + * @n_targets: the length of @targets + * @writable: whether to accept only targets for which GTK+ knows + * how to convert a pixbuf into the format + * + * Determines if any of the targets in @targets can be used to + * provide a #GdkPixbuf. + * + * Return value: %TRUE if @targets include a suitable target for images, + * otherwise %FALSE. + * + * Since: 2.10 + **/ +gboolean +gtk_targets_include_image (GdkAtom *targets, + gint n_targets, + gboolean writable) +{ + GtkTargetList *list; + GList *l; + gint i; + gboolean result = FALSE; + + g_return_val_if_fail (targets != NULL || n_targets == 0, FALSE); - if (latin1) + list = gtk_target_list_new (NULL, 0); + gtk_target_list_add_image_targets (list, 0, writable); + for (i = 0; i < n_targets && !result; i++) + { + for (l = list->list; l; l = l->next) { - gtk_selection_data_set (selection_data, - GDK_SELECTION_TYPE_STRING, - 8, latin1, strlen (latin1)); - g_free(latin1); - - return TRUE; + GtkTargetPair *pair = (GtkTargetPair *)l->data; + if (pair->target == targets[i]) + { + result = TRUE; + break; + } } + } + gtk_target_list_unref (list); + + return result; +} + +/** + * gtk_selection_data_targets_include_image: + * @selection_data: a #GtkSelectionData object + * @writable: whether to accept only targets for which GTK+ knows + * how to convert a pixbuf into the format + * + * Given a #GtkSelectionData object holding a list of targets, + * determines if any of the targets in @targets can be used to + * provide a #GdkPixbuf. + * + * Return value: %TRUE if @selection_data holds a list of targets, + * and a suitable target for images is included, otherwise %FALSE. + * + * Since: 2.6 + **/ +gboolean +gtk_selection_data_targets_include_image (GtkSelectionData *selection_data, + gboolean writable) +{ + GdkAtom *targets; + gint n_targets; + gboolean result = FALSE; + + g_return_val_if_fail (selection_data != NULL, FALSE); + + init_atoms (); + if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets)) + { + result = gtk_targets_include_image (targets, n_targets, writable); + g_free (targets); } - else if (selection_data->target == ctext_atom || - selection_data->target == text_atom) + + return result; +} + +/** + * gtk_targets_include_uri: + * @targets: an array of #GdkAtoms + * @n_targets: the length of @targets + * + * Determines if any of the targets in @targets can be used to + * provide an uri list. + * + * Return value: %TRUE if @targets include a suitable target for uri lists, + * otherwise %FALSE. + * + * Since: 2.10 + **/ +gboolean +gtk_targets_include_uri (GdkAtom *targets, + gint n_targets) +{ + gint i; + gboolean result = FALSE; + + g_return_val_if_fail (targets != NULL || n_targets == 0, FALSE); + + /* Keep in sync with gtk_target_list_add_uri_targets() + */ + + init_atoms (); + + for (i = 0; i < n_targets; i++) { - guchar *text; - GdkAtom encoding; - gint format; - gint new_length; - - if (gdk_utf8_to_compound_text (str, &encoding, &format, &text, &new_length)) + if (targets[i] == text_uri_list_atom) { - gtk_selection_data_set (selection_data, encoding, format, text, new_length); - gdk_free_compound_text (text); - - return TRUE; + result = TRUE; + break; } } - return FALSE; + return result; } /** - * gtk_selection_data_get_text: - * @selection_data: a #GtkSelectionData + * gtk_selection_data_targets_include_uri: + * @selection_data: a #GtkSelectionData object * - * Gets the contents of the selection data as a UTF-8 string. + * Given a #GtkSelectionData object holding a list of targets, + * determines if any of the targets in @targets can be used to + * provide a list or URIs. * - * Return value: if the selection data contained a recognized - * text type and it could be converted to UTF-8, a newly allocated - * string containing the converted text, otherwise %NULL. - * If the result is non-%NULL it must be freed with g_free(). + * Return value: %TRUE if @selection_data holds a list of targets, + * and a suitable target for URI lists is included, otherwise %FALSE. + * + * Since: 2.10 **/ -guchar * -gtk_selection_data_get_text (GtkSelectionData *selection_data) +gboolean +gtk_selection_data_targets_include_uri (GtkSelectionData *selection_data) { - guchar *result = NULL; + GdkAtom *targets; + gint n_targets; + gboolean result = FALSE; + + g_return_val_if_fail (selection_data != NULL, FALSE); init_atoms (); - - if (selection_data->length >= 0 && - (selection_data->type == GDK_TARGET_STRING || - selection_data->type == ctext_atom || - selection_data->type == utf8_atom)) - { - gchar **list; - gint i; - gint count = gdk_text_property_to_utf8_list (selection_data->type, - selection_data->format, - selection_data->data, - selection_data->length, - &list); - if (count > 0) - result = list[0]; - for (i = 1; i < count; i++) - g_free (list[i]); - g_free (list); + if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets)) + { + result = gtk_targets_include_uri (targets, n_targets); + g_free (targets); } return result; } + /************************************************************* * gtk_selection_init: * Initialize local variables @@ -887,21 +2165,30 @@ gtk_selection_data_get_text (GtkSelectionData *selection_data) static void gtk_selection_init (void) { - gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE); - gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE); - gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE); - gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE); + gtk_selection_atoms[INCR] = gdk_atom_intern_static_string ("INCR"); + gtk_selection_atoms[MULTIPLE] = gdk_atom_intern_static_string ("MULTIPLE"); + gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern_static_string ("TIMESTAMP"); + gtk_selection_atoms[TARGETS] = gdk_atom_intern_static_string ("TARGETS"); + + initialize = FALSE; } -/************************************************************* +/** * gtk_selection_clear: - * Handler for "selection_clear_event" - * arguments: - * widget: - * event: - * results: - *************************************************************/ - + * @widget: a #GtkWidget + * @event: the event + * + * The default handler for the GtkWidget::selection_clear_event + * signal. + * + * Return value: %TRUE if the event was handled, otherwise false + * + * Since: 2.2 + * + * Deprecated: 2.4: Instead of calling this function, chain up from + * your selection_clear_event handler. Calling this function + * from any other context is illegal. + **/ gboolean gtk_selection_clear (GtkWidget *widget, GdkEventSelection *event) @@ -929,7 +2216,7 @@ gtk_selection_clear (GtkWidget *widget, { current_selections = g_list_remove_link (current_selections, tmp_list); g_list_free (tmp_list); - g_free (selection_info); + g_slice_free (GtkSelectionInfo, selection_info); } return TRUE; @@ -937,7 +2224,7 @@ gtk_selection_clear (GtkWidget *widget, /************************************************************* - * gtk_selection_request: + * _gtk_selection_request: * Handler for "selection_request_event" * arguments: * widget: @@ -946,17 +2233,20 @@ gtk_selection_clear (GtkWidget *widget, *************************************************************/ gboolean -gtk_selection_request (GtkWidget *widget, - GdkEventSelection *event) +_gtk_selection_request (GtkWidget *widget, + GdkEventSelection *event) { + GdkDisplay *display = gtk_widget_get_display (widget); GtkIncrInfo *info; GList *tmp_list; - guchar *mult_atoms; int i; - + gulong selection_max_size; + if (initialize) gtk_selection_init (); + selection_max_size = GTK_SELECTION_MAX_SIZE (display); + /* Check if we own selection */ tmp_list = current_selections; @@ -974,52 +2264,84 @@ gtk_selection_request (GtkWidget *widget, if (tmp_list == NULL) return FALSE; - info = g_new(GtkIncrInfo, 1); + info = g_slice_new (GtkIncrInfo); + + g_object_ref (widget); - info->widget = widget; info->selection = event->selection; info->num_incrs = 0; /* Create GdkWindow structure for the requestor */ -#if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_FB) - info->requestor = gdk_window_lookup (event->requestor); + info->requestor = gdk_window_lookup_for_display (display, + event->requestor); if (!info->requestor) - info->requestor = gdk_window_foreign_new (event->requestor); -#else - info->requestor = NULL; -#endif + info->requestor = gdk_window_foreign_new_for_display (display, + event->requestor); /* Determine conversions we need to perform */ if (event->target == gtk_selection_atoms[MULTIPLE]) { GdkAtom type; + guchar *mult_atoms; gint format; gint length; mult_atoms = NULL; - gdk_error_trap_push(); - if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */ - 0, GTK_SELECTION_MAX_SIZE, FALSE, + gdk_error_trap_push (); + if (!gdk_property_get (info->requestor, event->property, GDK_NONE, /* AnyPropertyType */ + 0, selection_max_size, FALSE, &type, &format, &length, &mult_atoms)) { - gdk_selection_send_notify (event->requestor, event->selection, - event->target, GDK_NONE, event->time); + gdk_selection_send_notify_for_display (display, + event->requestor, + event->selection, + event->target, + GDK_NONE, + event->time); g_free (mult_atoms); - g_free (info); + g_slice_free (GtkIncrInfo, info); + gdk_error_trap_pop (); return TRUE; } - gdk_error_trap_pop(); - - info->num_conversions = length / (2*sizeof (GdkAtom)); - info->conversions = g_new (GtkIncrConversion, info->num_conversions); - - for (i=0; inum_conversions; i++) + gdk_error_trap_pop (); + + /* This is annoying; the ICCCM doesn't specify the property type + * used for the property contents, so the autoconversion for + * ATOM / ATOM_PAIR in GDK doesn't work properly. + */ +#ifdef GDK_WINDOWING_X11 + if (type != GDK_SELECTION_TYPE_ATOM && + type != gdk_atom_intern_static_string ("ATOM_PAIR")) + { + info->num_conversions = length / (2*sizeof (glong)); + info->conversions = g_new (GtkIncrConversion, info->num_conversions); + + for (i=0; inum_conversions; i++) + { + info->conversions[i].target = gdk_x11_xatom_to_atom_for_display (display, + ((glong *)mult_atoms)[2*i]); + info->conversions[i].property = gdk_x11_xatom_to_atom_for_display (display, + ((glong *)mult_atoms)[2*i + 1]); + } + + g_free (mult_atoms); + } + else +#endif { - info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i]; - info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1]; + info->num_conversions = length / (2*sizeof (GdkAtom)); + info->conversions = g_new (GtkIncrConversion, info->num_conversions); + + for (i=0; inum_conversions; i++) + { + info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i]; + info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1]; + } + + g_free (mult_atoms); } } else /* only a single conversion */ @@ -1028,7 +2350,6 @@ gtk_selection_request (GtkWidget *widget, info->num_conversions = 1; info->conversions[0].target = event->target; info->conversions[0].property = event->property; - mult_atoms = (guchar *)info->conversions; } /* Loop through conversions and determine which of these are big @@ -1042,19 +2363,20 @@ gtk_selection_request (GtkWidget *widget, data.target = info->conversions[i].target; data.data = NULL; data.length = -1; + data.display = gtk_widget_get_display (widget); #ifdef DEBUG_SELECTION g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)", - event->selection, info->conversions[i].target, - gdk_atom_name(info->conversions[i].target), - event->requestor, event->property); + event->selection, + info->conversions[i].target, + gdk_atom_name (info->conversions[i].target), + event->requestor, info->conversions[i].property); #endif gtk_selection_invoke_handler (widget, &data, event->time); if (data.length < 0) { - ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE; info->conversions[i].property = GDK_NONE; continue; } @@ -1063,9 +2385,13 @@ gtk_selection_request (GtkWidget *widget, items = data.length / gtk_selection_bytes_per_item (data.format); - if (data.length > GTK_SELECTION_MAX_SIZE) + if (data.length > selection_max_size) { /* Sending via INCR */ +#ifdef DEBUG_SELECTION + g_message ("Target larger (%d) than max. request size (%ld), sending incrementally\n", + data.length, selection_max_size); +#endif info->conversions[i].offset = 0; info->conversions[i].data = data; @@ -1109,17 +2435,24 @@ gtk_selection_request (GtkWidget *widget, gdk_window_get_events (info->requestor) | GDK_PROPERTY_CHANGE_MASK); current_incrs = g_list_append (current_incrs, info); - gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info); + gdk_threads_add_timeout (1000, (GSourceFunc) gtk_selection_incr_timeout, info); } /* If it was a MULTIPLE request, set the property to indicate which conversions succeeded */ if (event->target == gtk_selection_atoms[MULTIPLE]) { + GdkAtom *mult_atoms = g_new (GdkAtom, 2 * info->num_conversions); + for (i = 0; i < info->num_conversions; i++) + { + mult_atoms[2*i] = info->conversions[i].target; + mult_atoms[2*i+1] = info->conversions[i].property; + } + gdk_property_change (info->requestor, event->property, - GDK_SELECTION_TYPE_ATOM, 32, + gdk_atom_intern_static_string ("ATOM_PAIR"), 32, GDK_PROP_MODE_REPLACE, - mult_atoms, 2*info->num_conversions); + (guchar *)mult_atoms, 2*info->num_conversions); g_free (mult_atoms); } @@ -1127,26 +2460,36 @@ gtk_selection_request (GtkWidget *widget, info->conversions[0].property == GDK_NONE) { /* Reject the entire conversion */ - gdk_selection_send_notify (event->requestor, event->selection, - event->target, GDK_NONE, event->time); + gdk_selection_send_notify_for_display (gtk_widget_get_display (widget), + event->requestor, + event->selection, + event->target, + GDK_NONE, + event->time); } else { - gdk_selection_send_notify (event->requestor, event->selection, - event->target, event->property, event->time); + gdk_selection_send_notify_for_display (gtk_widget_get_display (widget), + event->requestor, + event->selection, + event->target, + event->property, + event->time); } - + if (info->num_incrs == 0) { g_free (info->conversions); - g_free (info); + g_slice_free (GtkIncrInfo, info); } + + g_object_unref (widget); return TRUE; } /************************************************************* - * gtk_selection_incr_event: + * _gtk_selection_incr_event: * Called whenever an PropertyNotify event occurs for an * GdkWindow with user_data == NULL. These will be notifications * that a window we are sending the selection to via the @@ -1161,13 +2504,14 @@ gtk_selection_request (GtkWidget *widget, *************************************************************/ gboolean -gtk_selection_incr_event (GdkWindow *window, - GdkEventProperty *event) +_gtk_selection_incr_event (GdkWindow *window, + GdkEventProperty *event) { GList *tmp_list; GtkIncrInfo *info = NULL; gint num_bytes; guchar *buffer; + gulong selection_max_size; int i; @@ -1177,7 +2521,9 @@ gtk_selection_incr_event (GdkWindow *window, #ifdef DEBUG_SELECTION g_message ("PropertyDelete, property %ld", event->atom); #endif - + + selection_max_size = GTK_SELECTION_MAX_SIZE (gdk_drawable_get_display (window)); + /* Now find the appropriate ongoing INCR */ tmp_list = current_incrs; while (tmp_list) @@ -1215,10 +2561,10 @@ gtk_selection_incr_event (GdkWindow *window, buffer = info->conversions[i].data.data + info->conversions[i].offset; - if (num_bytes > GTK_SELECTION_MAX_SIZE) + if (num_bytes > selection_max_size) { - num_bytes = GTK_SELECTION_MAX_SIZE; - info->conversions[i].offset += GTK_SELECTION_MAX_SIZE; + num_bytes = selection_max_size; + info->conversions[i].offset += selection_max_size; } else info->conversions[i].offset = -2; @@ -1249,7 +2595,6 @@ gtk_selection_incr_event (GdkWindow *window, info->conversions[i].offset = -1; } } - break; } /* Check if we're finished with all the targets */ @@ -1279,8 +2624,6 @@ gtk_selection_incr_timeout (GtkIncrInfo *info) GList *tmp_list; gboolean retval; - GDK_THREADS_ENTER (); - /* Determine if retrieval has finished by checking if it still in list of pending retrievals */ @@ -1293,9 +2636,9 @@ gtk_selection_incr_timeout (GtkIncrInfo *info) } /* If retrieval is finished */ - if (!tmp_list || info->idle_time >= 5) + if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME) { - if (tmp_list && info->idle_time >= 5) + if (tmp_list && info->idle_time >= IDLE_ABORT_TIME) { current_incrs = g_list_remove_link (current_incrs, tmp_list); g_list_free (tmp_list); @@ -1305,7 +2648,7 @@ gtk_selection_incr_timeout (GtkIncrInfo *info) /* FIXME: we should check if requestor window is still in use, and if not, remove it? */ - g_free (info); + g_slice_free (GtkIncrInfo, info); retval = FALSE; /* remove timeout */ } @@ -1316,14 +2659,12 @@ gtk_selection_incr_timeout (GtkIncrInfo *info) retval = TRUE; /* timeout will happen again */ } - GDK_THREADS_LEAVE (); - return retval; } /************************************************************* - * gtk_selection_notify: - * Handler for "selection_notify_event" signals on windows + * _gtk_selection_notify: + * Handler for "selection-notify-event" signals on windows * where a retrieval is currently in process. The selection * owner has responded to our conversion request. * arguments: @@ -1335,8 +2676,8 @@ gtk_selection_incr_timeout (GtkIncrInfo *info) *************************************************************/ gboolean -gtk_selection_notify (GtkWidget *widget, - GdkEventSelection *event) +_gtk_selection_notify (GtkWidget *widget, + GdkEventSelection *event) { GList *tmp_list; GtkRetrievalInfo *info = NULL; @@ -1365,7 +2706,9 @@ gtk_selection_notify (GtkWidget *widget, if (event->property != GDK_NONE) length = gdk_selection_property_get (widget->window, &buffer, &type, &format); - + else + length = 0; /* silence gcc */ + if (event->property == GDK_NONE || buffer == NULL) { current_retrievals = g_list_remove_link (current_retrievals, tmp_list); @@ -1409,8 +2752,8 @@ gtk_selection_notify (GtkWidget *widget, } /************************************************************* - * gtk_selection_property_notify: - * Handler for "property_notify_event" signals on windows + * _gtk_selection_property_notify: + * Handler for "property-notify-event" signals on windows * where a retrieval is currently in process. The selection * owner has added more data. * arguments: @@ -1422,8 +2765,8 @@ gtk_selection_notify (GtkWidget *widget, *************************************************************/ gboolean -gtk_selection_property_notify (GtkWidget *widget, - GdkEventProperty *event) +_gtk_selection_property_notify (GtkWidget *widget, + GdkEventProperty *event) { GList *tmp_list; GtkRetrievalInfo *info = NULL; @@ -1437,7 +2780,7 @@ gtk_selection_property_notify (GtkWidget *widget, #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11) if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */ - (event->atom != gdk_selection_property)) /* not the right property */ + (event->atom != gdk_atom_intern_static_string ("GDK_SELECTION"))) /* not the right property */ #endif return FALSE; @@ -1521,14 +2864,12 @@ gtk_selection_property_notify (GtkWidget *widget, * results: *************************************************************/ -static gint +static gboolean gtk_selection_retrieval_timeout (GtkRetrievalInfo *info) { GList *tmp_list; gboolean retval; - GDK_THREADS_ENTER (); - /* Determine if retrieval has finished by checking if it still in list of pending retrievals */ @@ -1541,9 +2882,9 @@ gtk_selection_retrieval_timeout (GtkRetrievalInfo *info) } /* If retrieval is finished */ - if (!tmp_list || info->idle_time >= 5) + if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME) { - if (tmp_list && info->idle_time >= 5) + if (tmp_list && info->idle_time >= IDLE_ABORT_TIME) { current_retrievals = g_list_remove_link (current_retrievals, tmp_list); g_list_free (tmp_list); @@ -1551,7 +2892,7 @@ gtk_selection_retrieval_timeout (GtkRetrievalInfo *info) } g_free (info->buffer); - g_free (info); + g_slice_free (GtkRetrievalInfo, info); retval = FALSE; /* remove timeout */ } @@ -1562,14 +2903,12 @@ gtk_selection_retrieval_timeout (GtkRetrievalInfo *info) retval = TRUE; /* timeout will happen again */ } - GDK_THREADS_LEAVE (); - return retval; } /************************************************************* * gtk_selection_retrieval_report: - * Emits a "selection_received" signal. + * Emits a "selection-received" signal. * arguments: * info: information about the retrieval that completed * buffer: buffer containing data (NULL => errror) @@ -1592,10 +2931,11 @@ gtk_selection_retrieval_report (GtkRetrievalInfo *info, data.length = length; data.data = buffer; + data.display = gtk_widget_get_display (info->widget); - gtk_signal_emit_by_name (GTK_OBJECT(info->widget), - "selection_received", - &data, time); + g_signal_emit_by_name (info->widget, + "selection-received", + &data, time); } /************************************************************* @@ -1628,10 +2968,10 @@ gtk_selection_invoke_handler (GtkWidget *widget, if (target_list && gtk_target_list_find (target_list, data->target, &info)) { - gtk_signal_emit_by_name (GTK_OBJECT (widget), - "selection_get", - data, - info, time); + g_signal_emit_by_name (widget, + "selection-get", + data, + info, time); } else gtk_selection_default_handler (widget, data); @@ -1698,9 +3038,12 @@ gtk_selection_default_handler (GtkWidget *widget, data->type = GDK_SELECTION_TYPE_ATOM; data->format = 32; data->length = count * sizeof (GdkAtom); - - p = g_new (GdkAtom, count); + + /* selection data is always terminated by a trailing \0 + */ + p = g_malloc (data->length + 1); data->data = (guchar *)p; + data->data[data->length] = '\0'; *p++ = gtk_selection_atoms[TIMESTAMP]; *p++ = gtk_selection_atoms[TARGETS]; @@ -1722,34 +3065,74 @@ gtk_selection_default_handler (GtkWidget *widget, } +/** + * gtk_selection_data_copy: + * @data: a pointer to a #GtkSelectionData structure. + * + * Makes a copy of a #GtkSelectionData structure and its data. + * + * Return value: a pointer to a copy of @data. + **/ GtkSelectionData* -gtk_selection_data_copy (GtkSelectionData *selection_data) +gtk_selection_data_copy (GtkSelectionData *data) { GtkSelectionData *new_data; - g_return_val_if_fail (selection_data != NULL, NULL); + g_return_val_if_fail (data != NULL, NULL); - new_data = g_new (GtkSelectionData, 1); - *new_data = *selection_data; + new_data = g_slice_new (GtkSelectionData); + *new_data = *data; - if (selection_data->data) + if (data->data) { - new_data->data = g_malloc (selection_data->length + 1); - memcpy (new_data->data, selection_data->data, selection_data->length + 1); + new_data->data = g_malloc (data->length + 1); + memcpy (new_data->data, data->data, data->length + 1); } return new_data; } +/** + * gtk_selection_data_free: + * @data: a pointer to a #GtkSelectionData structure. + * + * Frees a #GtkSelectionData structure returned from + * gtk_selection_data_copy(). + **/ void gtk_selection_data_free (GtkSelectionData *data) { g_return_if_fail (data != NULL); - if (data->data) - g_free (data->data); + g_free (data->data); + + g_slice_free (GtkSelectionData, data); +} + +GType +gtk_selection_data_get_type (void) +{ + static GType our_type = 0; - g_free (data); + if (our_type == 0) + our_type = g_boxed_type_register_static (I_("GtkSelectionData"), + (GBoxedCopyFunc) gtk_selection_data_copy, + (GBoxedFreeFunc) gtk_selection_data_free); + + 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 @@ -1771,3 +3154,6 @@ gtk_selection_bytes_per_item (gint format) } return 0; } + +#define __GTK_SELECTION_C__ +#include "gtkaliasdef.c"