1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 /* This file implements most of the work of the ICCM selection protocol.
21 * The code was written after an intensive study of the equivalent part
22 * of John Ousterhout's Tk toolkit, and does many things in much the
25 * The one thing in the ICCM that isn't fully supported here (or in Tk)
26 * is side effects targets. For these to be handled properly, MULTIPLE
27 * targets need to be done in the order specified. This cannot be
28 * guaranteed with the way we do things, since if we are doing INCR
29 * transfers, the order will depend on the timing of the requestor.
31 * By Owen Taylor <owt1@cornell.edu> 8/16/97
34 /* Terminology note: when not otherwise specified, the term "incr" below
35 * refers to the _sending_ part of the INCR protocol. The receiving
36 * portion is referred to just as "retrieval". (Terminology borrowed
37 * from Tk, because there is no good opposite to "retrieval" in English.
38 * "send" can't be made into a noun gracefully and we're already using
39 * "emission" for something else ....)
42 /* The MOTIF entry widget seems to ask for the TARGETS target, then
43 (regardless of the reply) ask for the TEXT target. It's slightly
44 possible though that it somehow thinks we are responding negatively
45 to the TARGETS request, though I don't really think so ... */
48 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
49 * file for a list of people on the GTK+ Team. See the ChangeLog
50 * files for a list of changes. These files are distributed with
51 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
59 #include "gtkselection.h"
60 #include "gtksignal.h"
62 /* #define DEBUG_SELECTION */
64 /* Maximum size of a sent chunk, in bytes. Also the default size of
66 #ifdef GDK_WINDOWING_WIN32
67 /* No chunks on Win32 */
68 #define GTK_SELECTION_MAX_SIZE G_MAXINT
70 #define GTK_SELECTION_MAX_SIZE 4000
80 typedef struct _GtkSelectionInfo GtkSelectionInfo;
81 typedef struct _GtkIncrConversion GtkIncrConversion;
82 typedef struct _GtkIncrInfo GtkIncrInfo;
83 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
85 struct _GtkSelectionInfo
88 GtkWidget *widget; /* widget that owns selection */
89 guint32 time; /* time used to acquire selection */
90 GdkDisplay *display; /* needed in gtk_selection_remove_all */
93 struct _GtkIncrConversion
95 GdkAtom target; /* Requested target */
96 GdkAtom property; /* Property to store in */
97 GtkSelectionData data; /* The data being supplied */
98 gint offset; /* Current offset in sent selection.
100 * -2 => Only the final (empty) portion
106 GdkWindow *requestor; /* Requestor window - we create a GdkWindow
107 so we can receive events */
108 GdkAtom selection; /* Selection we're sending */
110 GtkIncrConversion *conversions; /* Information about requested conversions -
111 * With MULTIPLE requests (benighted 1980's
112 * hardware idea), there can be more than
114 gint num_conversions;
115 gint num_incrs; /* number of remaining INCR style transactions */
120 struct _GtkRetrievalInfo
123 GdkAtom selection; /* Selection being retrieved. */
124 GdkAtom target; /* Form of selection that we requested */
125 guint32 idle_time; /* Number of seconds since we last heard
126 from selection owner */
127 guchar *buffer; /* Buffer in which to accumulate results */
128 gint offset; /* Current offset in buffer, -1 indicates
130 guint32 notify_time; /* Timestamp from SelectionNotify */
133 /* Local Functions */
134 static void gtk_selection_init (void);
135 static gint gtk_selection_incr_timeout (GtkIncrInfo *info);
136 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
137 static void gtk_selection_retrieval_report (GtkRetrievalInfo *info,
143 static void gtk_selection_invoke_handler (GtkWidget *widget,
144 GtkSelectionData *data,
146 static void gtk_selection_default_handler (GtkWidget *widget,
147 GtkSelectionData *data);
148 static int gtk_selection_bytes_per_item (gint format);
151 static gint initialize = TRUE;
152 static GList *current_retrievals = NULL;
153 static GList *current_incrs = NULL;
154 static GList *current_selections = NULL;
156 static GdkAtom gtk_selection_atoms[LAST_ATOM];
157 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
168 gtk_target_list_new (const GtkTargetEntry *targets,
171 GtkTargetList *result = g_new (GtkTargetList, 1);
173 result->ref_count = 1;
176 gtk_target_list_add_table (result, targets, ntargets);
182 gtk_target_list_ref (GtkTargetList *list)
184 g_return_if_fail (list != NULL);
190 gtk_target_list_unref (GtkTargetList *list)
192 g_return_if_fail (list != NULL);
193 g_return_if_fail (list->ref_count > 0);
196 if (list->ref_count == 0)
198 GList *tmp_list = list->list;
201 GtkTargetPair *pair = tmp_list->data;
204 tmp_list = tmp_list->next;
207 g_list_free (list->list);
213 gtk_target_list_add (GtkTargetList *list,
220 g_return_if_fail (list != NULL);
222 pair = g_new (GtkTargetPair, 1);
223 pair->target = target;
227 list->list = g_list_append (list->list, pair);
231 gtk_target_list_add_table (GtkTargetList *list,
232 const GtkTargetEntry *targets,
237 for (i=ntargets-1; i >= 0; i--)
239 GtkTargetPair *pair = g_new (GtkTargetPair, 1);
240 pair->target = gdk_atom_intern (targets[i].target, FALSE);
241 pair->flags = targets[i].flags;
242 pair->info = targets[i].info;
244 list->list = g_list_prepend (list->list, pair);
249 gtk_target_list_remove (GtkTargetList *list,
254 g_return_if_fail (list != NULL);
256 tmp_list = list->list;
259 GtkTargetPair *pair = tmp_list->data;
261 if (pair->target == target)
265 list->list = g_list_remove_link (list->list, tmp_list);
266 g_list_free_1 (tmp_list);
271 tmp_list = tmp_list->next;
276 gtk_target_list_find (GtkTargetList *list,
280 GList *tmp_list = list->list;
283 GtkTargetPair *pair = tmp_list->data;
285 if (pair->target == target)
290 tmp_list = tmp_list->next;
297 * gtk_selection_owner_set_for_display:
298 * @display: the #Gdkdisplay where the selection is set
299 * @widget: new selection owner (a #GdkWidget), or %NULL.
300 * @selection: an interned atom representing the selection to claim.
301 * @time: timestamp with which to claim the selection
303 * Claim ownership of a given selection for a particular widget, or,
304 * if @widget is %NULL, release ownership of the selection.
306 * Return value: TRUE if the operation succeeded
309 gtk_selection_owner_set_for_display (GdkDisplay *display,
315 GtkWidget *old_owner;
316 GtkSelectionInfo *selection_info = NULL;
319 g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
320 g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
321 g_return_val_if_fail (widget == NULL || gtk_widget_get_display (widget) == display, FALSE);
326 window = widget->window;
328 tmp_list = current_selections;
331 if (((GtkSelectionInfo *)tmp_list->data)->selection == selection)
333 selection_info = tmp_list->data;
337 tmp_list = tmp_list->next;
340 if (gdk_selection_owner_set_for_display (display, window, selection, time, TRUE))
348 old_owner = selection_info->widget;
349 current_selections = g_list_remove_link (current_selections,
351 g_list_free (tmp_list);
352 g_free (selection_info);
357 if (selection_info == NULL)
359 selection_info = g_new (GtkSelectionInfo, 1);
360 selection_info->selection = selection;
361 selection_info->widget = widget;
362 selection_info->time = time;
363 selection_info->display = display;
364 current_selections = g_list_prepend (current_selections,
369 old_owner = selection_info->widget;
370 selection_info->widget = widget;
371 selection_info->time = time;
372 selection_info->display = display;
375 /* If another widget in the application lost the selection,
376 * send it a GDK_SELECTION_CLEAR event.
378 if (old_owner && old_owner != widget)
380 GdkEventSelection event;
382 event.type = GDK_SELECTION_CLEAR;
383 event.window = old_owner->window;
384 event.selection = selection;
387 gtk_widget_event (old_owner, (GdkEvent *) &event);
396 * gtk_selection_owner_set:
397 * @widget: a #GtkWidget, or %NULL.
398 * @selection: an interned atom representing the selection to claim
399 * @time: timestamp with which to claim the selection
401 * Claims ownership of a given selection for a particular widget,
402 * or, if @widget is %NULL, release ownership of the selection.
404 * Return value: %TRUE if the operation succeeded
407 gtk_selection_owner_set (GtkWidget *widget,
411 g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
413 return gtk_selection_owner_set_for_display (gdk_get_default_display(),
419 /*************************************************************
420 * gtk_selection_add_target
421 * Add specified target to list of supported targets
424 * widget: The widget for which this target applies
427 * info: guint to pass to to the selection_get signal
430 *************************************************************/
432 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
434 struct _GtkSelectionTargetList {
439 static GtkTargetList *
440 gtk_selection_target_list_get (GtkWidget *widget,
443 GtkSelectionTargetList *sellist;
447 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
452 sellist = tmp_list->data;
453 if (sellist->selection == selection)
454 return sellist->list;
455 tmp_list = tmp_list->next;
458 sellist = g_new (GtkSelectionTargetList, 1);
459 sellist->selection = selection;
460 sellist->list = gtk_target_list_new (NULL, 0);
462 lists = g_list_prepend (lists, sellist);
463 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
465 return sellist->list;
469 gtk_selection_target_list_remove (GtkWidget *widget)
471 GtkSelectionTargetList *sellist;
475 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
480 sellist = tmp_list->data;
482 gtk_target_list_unref (sellist->list);
485 tmp_list = tmp_list->next;
489 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL);
493 * gtk_selection_clear_targets:
494 * @widget: a #GtkWidget
495 * @selection: an atom representing a selection
497 * Remove all targets registered for the given selection for the
501 gtk_selection_clear_targets (GtkWidget *widget,
504 GtkSelectionTargetList *sellist;
508 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
513 sellist = tmp_list->data;
514 if (sellist->selection == selection)
516 lists = g_list_delete_link (lists, tmp_list);
517 gtk_target_list_unref (sellist->list);
523 tmp_list = tmp_list->next;
526 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
530 gtk_selection_add_target (GtkWidget *widget,
537 g_return_if_fail (widget != NULL);
539 list = gtk_selection_target_list_get (widget, selection);
540 gtk_target_list_add (list, target, 0, info);
544 gtk_selection_add_targets (GtkWidget *widget,
546 const GtkTargetEntry *targets,
551 g_return_if_fail (widget != NULL);
552 g_return_if_fail (targets != NULL);
554 list = gtk_selection_target_list_get (widget, selection);
555 gtk_target_list_add_table (list, targets, ntargets);
559 /*************************************************************
560 * gtk_selection_remove_all:
561 * Removes all handlers and unsets ownership of all
562 * selections for a widget. Called when widget is being
568 *************************************************************/
571 gtk_selection_remove_all (GtkWidget *widget)
575 GtkSelectionInfo *selection_info;
577 /* Remove pending requests/incrs for this widget */
579 tmp_list = current_retrievals;
582 next = tmp_list->next;
583 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
585 current_retrievals = g_list_remove_link (current_retrievals,
587 /* structure will be freed in timeout */
588 g_list_free (tmp_list);
593 /* Disclaim ownership of any selections */
595 tmp_list = current_selections;
598 next = tmp_list->next;
599 selection_info = (GtkSelectionInfo *)tmp_list->data;
601 if (selection_info->widget == widget)
603 gdk_selection_owner_set_for_display (selection_info->display,
605 selection_info->selection,
606 GDK_CURRENT_TIME, FALSE);
607 current_selections = g_list_remove_link (current_selections,
609 g_list_free (tmp_list);
610 g_free (selection_info);
616 /* Remove all selection lists */
617 gtk_selection_target_list_remove (widget);
620 /*************************************************************
621 * gtk_selection_convert:
622 * Request the contents of a selection. When received,
623 * a "selection_received" signal will be generated.
626 * widget: The widget which acts as requestor
627 * selection: Which selection to get
628 * target: Form of information desired (e.g., STRING)
629 * time: Time of request (usually of triggering event)
630 * In emergency, you could use GDK_CURRENT_TIME
633 * TRUE if requested succeeded. FALSE if we could not process
634 * request. (e.g., there was already a request in process for
636 *************************************************************/
639 gtk_selection_convert (GtkWidget *widget,
644 GtkRetrievalInfo *info;
646 GdkWindow *owner_window;
649 g_return_val_if_fail (widget != NULL, FALSE);
652 gtk_selection_init ();
654 if (!GTK_WIDGET_REALIZED (widget))
655 gtk_widget_realize (widget);
657 /* Check to see if there are already any retrievals in progress for
658 this widget. If we changed GDK to use the selection for the
659 window property in which to store the retrieved information, then
660 we could support multiple retrievals for different selections.
661 This might be useful for DND. */
663 tmp_list = current_retrievals;
666 info = (GtkRetrievalInfo *)tmp_list->data;
667 if (info->widget == widget)
669 tmp_list = tmp_list->next;
672 info = g_new (GtkRetrievalInfo, 1);
674 info->widget = widget;
675 info->selection = selection;
676 info->target = target;
680 /* Check if this process has current owner. If so, call handler
681 procedure directly to avoid deadlocks with INCR. */
683 display = gtk_widget_get_display (widget);
684 owner_window = gdk_selection_owner_get_for_display (display, selection);
686 if (owner_window != NULL)
688 GtkWidget *owner_widget;
689 GtkSelectionData selection_data;
691 selection_data.selection = selection;
692 selection_data.target = target;
693 selection_data.data = NULL;
694 selection_data.length = -1;
695 selection_data.display = display;
697 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
699 if (owner_widget != NULL)
701 gtk_selection_invoke_handler (owner_widget,
705 gtk_selection_retrieval_report (info,
707 selection_data.format,
709 selection_data.length,
712 g_free (selection_data.data);
719 /* Otherwise, we need to go through X */
721 current_retrievals = g_list_append (current_retrievals, info);
722 gdk_selection_convert (widget->window, selection, target, time);
723 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
728 /*************************************************************
729 * gtk_selection_data_set:
730 * Store new data into a GtkSelectionData object. Should
731 * _only_ by called from a selection handler callback.
732 * Null terminates the stored data.
734 * type: the type of selection data
735 * format: format (number of bits in a unit)
736 * data: pointer to the data (will be copied)
737 * length: length of the data
739 *************************************************************/
742 gtk_selection_data_set (GtkSelectionData *selection_data,
748 if (selection_data->data)
749 g_free (selection_data->data);
751 selection_data->type = type;
752 selection_data->format = format;
756 selection_data->data = g_new (guchar, length+1);
757 memcpy (selection_data->data, data, length);
758 selection_data->data[length] = 0;
762 g_return_if_fail (length <= 0);
765 selection_data->data = NULL;
767 selection_data->data = g_strdup("");
770 selection_data->length = length;
773 static GdkAtom utf8_atom;
774 static GdkAtom text_atom;
775 static GdkAtom ctext_atom;
782 utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
783 text_atom = gdk_atom_intern ("TEXT", FALSE);
784 ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
789 * gtk_selection_data_set_text:
790 * @selection_data: a #GtkSelectionData
791 * @str: a UTF-8 string
792 * @len: the length of @str, or -1 if @str is nul-terminated.
794 * Sets the contents of the selection from a UTF-8 encoded string.
795 * The string is converted to the form determined by
796 * @selection_data->target.
798 * Return value: %TRUE if the selection was successfully set,
802 gtk_selection_data_set_text (GtkSelectionData *selection_data,
806 gboolean result = FALSE;
813 if (selection_data->target == utf8_atom)
815 gtk_selection_data_set (selection_data,
817 8, (guchar *)str, len);
820 else if (selection_data->target == GDK_TARGET_STRING)
822 gchar *tmp = g_strndup (str, len);
823 gchar *latin1 = gdk_utf8_to_string_target (tmp);
828 gtk_selection_data_set (selection_data,
829 GDK_SELECTION_TYPE_STRING,
830 8, latin1, strlen (latin1));
837 else if (selection_data->target == ctext_atom ||
838 selection_data->target == text_atom)
846 tmp = g_strndup (str, len);
847 if (gdk_utf8_to_compound_text_for_display (selection_data->display, tmp,
848 &encoding, &format, &text, &new_length))
850 gtk_selection_data_set (selection_data, encoding, format, text, new_length);
851 gdk_free_compound_text (text);
863 * gtk_selection_data_get_text:
864 * @selection_data: a #GtkSelectionData
866 * Gets the contents of the selection data as a UTF-8 string.
868 * Return value: if the selection data contained a recognized
869 * text type and it could be converted to UTF-8, a newly allocated
870 * string containing the converted text, otherwise %NULL.
871 * If the result is non-%NULL it must be freed with g_free().
874 gtk_selection_data_get_text (GtkSelectionData *selection_data)
876 guchar *result = NULL;
880 if (selection_data->length >= 0 &&
881 (selection_data->type == GDK_TARGET_STRING ||
882 selection_data->type == ctext_atom ||
883 selection_data->type == utf8_atom))
887 gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
888 selection_data->type,
889 selection_data->format,
890 selection_data->data,
891 selection_data->length,
896 for (i = 1; i < count; i++)
905 * gtk_selection_data_get_targets:
906 * @selection_data: a #GtkSelectionData object
907 * @targets: location to store an array of targets. The result
908 * stored here must be freed with g_free().
909 * @n_atoms: location to store number of items in @targets.
911 * Gets the contents of @selection_data as an array of targets.
912 * This can be used to interpret the results of getting
913 * the standard TARGETS target that is always supplied for
916 * Return value: %TRUE if @selection_data contains a valid
917 * array of targets, otherwise %FALSE.
920 gtk_selection_data_get_targets (GtkSelectionData *selection_data,
924 if (selection_data->length >= 0 &&
925 selection_data->format == 32 &&
926 selection_data->type == GDK_SELECTION_TYPE_ATOM)
929 *targets = g_memdup (selection_data->data, selection_data->length);
931 *n_atoms = selection_data->length / sizeof (GdkAtom);
947 * gtk_selection_data_targets_include_text:
948 * @selection_data: a #GtkSelectionData object
950 * Given a #GtkSelectionData object holding a list of targets,
951 * determines if any of the targets in @targets can be used to
954 * Return value: %TRUE if @selection_data holds a list of targets,
955 * and a suitable target for text is included, otherwise %FALSE.
958 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
963 gboolean result = FALSE;
965 if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
967 for (i=0; i < n_targets; i++)
969 if (targets[i] == gdk_atom_intern ("STRING", FALSE) ||
970 targets[i] == gdk_atom_intern ("TEXT", FALSE) ||
971 targets[i] == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
972 targets[i] == gdk_atom_intern ("UTF8_STRING", FALSE))
982 /*************************************************************
983 * gtk_selection_init:
984 * Initialize local variables
988 *************************************************************/
991 gtk_selection_init (void)
993 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
994 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
995 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
996 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
999 /*************************************************************
1000 * gtk_selection_clear:
1001 * Handler for "selection_clear_event"
1006 *************************************************************/
1009 gtk_selection_clear (GtkWidget *widget,
1010 GdkEventSelection *event)
1012 /* Note that we filter clear events in gdkselection-x11.c, so
1013 * that we only will get here if the clear event actually
1014 * represents a change that we didn't do ourself.
1017 GtkSelectionInfo *selection_info = NULL;
1019 tmp_list = current_selections;
1022 selection_info = (GtkSelectionInfo *)tmp_list->data;
1024 if ((selection_info->selection == event->selection) &&
1025 (selection_info->widget == widget))
1028 tmp_list = tmp_list->next;
1033 current_selections = g_list_remove_link (current_selections, tmp_list);
1034 g_list_free (tmp_list);
1035 g_free (selection_info);
1042 /*************************************************************
1043 * gtk_selection_request:
1044 * Handler for "selection_request_event"
1049 *************************************************************/
1052 gtk_selection_request (GtkWidget *widget,
1053 GdkEventSelection *event)
1061 gtk_selection_init ();
1063 /* Check if we own selection */
1065 tmp_list = current_selections;
1068 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1070 if ((selection_info->selection == event->selection) &&
1071 (selection_info->widget == widget))
1074 tmp_list = tmp_list->next;
1077 if (tmp_list == NULL)
1080 info = g_new (GtkIncrInfo, 1);
1082 g_object_ref (widget);
1084 info->selection = event->selection;
1085 info->num_incrs = 0;
1087 /* Create GdkWindow structure for the requestor */
1089 info->requestor = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
1091 if (!info->requestor)
1092 info->requestor = gdk_window_foreign_new_for_display (gtk_widget_get_display (widget),
1095 /* Determine conversions we need to perform */
1097 if (event->target == gtk_selection_atoms[MULTIPLE])
1105 gdk_error_trap_push ();
1106 if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
1107 0, GTK_SELECTION_MAX_SIZE, FALSE,
1108 &type, &format, &length, &mult_atoms))
1110 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1116 g_free (mult_atoms);
1120 gdk_error_trap_pop ();
1122 info->num_conversions = length / (2*sizeof (GdkAtom));
1123 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1125 for (i=0; i<info->num_conversions; i++)
1127 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1128 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1131 else /* only a single conversion */
1133 info->conversions = g_new (GtkIncrConversion, 1);
1134 info->num_conversions = 1;
1135 info->conversions[0].target = event->target;
1136 info->conversions[0].property = event->property;
1137 mult_atoms = (guchar *)info->conversions;
1140 /* Loop through conversions and determine which of these are big
1141 enough to require doing them via INCR */
1142 for (i=0; i<info->num_conversions; i++)
1144 GtkSelectionData data;
1147 data.selection = event->selection;
1148 data.target = info->conversions[i].target;
1151 data.display = gtk_widget_get_display (widget);
1153 #ifdef DEBUG_SELECTION
1154 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1155 event->selection, info->conversions[i].target,
1156 gdk_atom_name (info->conversions[i].target),
1157 event->requestor, event->property);
1160 gtk_selection_invoke_handler (widget, &data, event->time);
1162 if (data.length < 0)
1164 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
1165 info->conversions[i].property = GDK_NONE;
1169 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1171 items = data.length / gtk_selection_bytes_per_item (data.format);
1173 if (data.length > GTK_SELECTION_MAX_SIZE)
1175 /* Sending via INCR */
1177 info->conversions[i].offset = 0;
1178 info->conversions[i].data = data;
1181 gdk_property_change (info->requestor,
1182 info->conversions[i].property,
1183 gtk_selection_atoms[INCR],
1185 GDK_PROP_MODE_REPLACE,
1186 (guchar *)&items, 1);
1190 info->conversions[i].offset = -1;
1192 gdk_property_change (info->requestor,
1193 info->conversions[i].property,
1196 GDK_PROP_MODE_REPLACE,
1203 /* If we have some INCR's, we need to send the rest of the data in
1206 if (info->num_incrs > 0)
1208 /* FIXME: this could be dangerous if window doesn't still
1211 #ifdef DEBUG_SELECTION
1212 g_message ("Starting INCR...");
1215 gdk_window_set_events (info->requestor,
1216 gdk_window_get_events (info->requestor) |
1217 GDK_PROPERTY_CHANGE_MASK);
1218 current_incrs = g_list_append (current_incrs, info);
1219 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
1222 /* If it was a MULTIPLE request, set the property to indicate which
1223 conversions succeeded */
1224 if (event->target == gtk_selection_atoms[MULTIPLE])
1226 gdk_property_change (info->requestor, event->property,
1227 gdk_atom_intern ("ATOM_PAIR", FALSE), 32,
1228 GDK_PROP_MODE_REPLACE,
1229 mult_atoms, 2*info->num_conversions);
1230 g_free (mult_atoms);
1233 if (info->num_conversions == 1 &&
1234 info->conversions[0].property == GDK_NONE)
1236 /* Reject the entire conversion */
1237 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1246 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1254 if (info->num_incrs == 0)
1256 g_free (info->conversions);
1260 g_object_unref (widget);
1265 /*************************************************************
1266 * gtk_selection_incr_event:
1267 * Called whenever an PropertyNotify event occurs for an
1268 * GdkWindow with user_data == NULL. These will be notifications
1269 * that a window we are sending the selection to via the
1270 * INCR protocol has deleted a property and is ready for
1274 * window: the requestor window
1275 * event: the property event structure
1278 *************************************************************/
1281 gtk_selection_incr_event (GdkWindow *window,
1282 GdkEventProperty *event)
1285 GtkIncrInfo *info = NULL;
1291 if (event->state != GDK_PROPERTY_DELETE)
1294 #ifdef DEBUG_SELECTION
1295 g_message ("PropertyDelete, property %ld", event->atom);
1298 /* Now find the appropriate ongoing INCR */
1299 tmp_list = current_incrs;
1302 info = (GtkIncrInfo *)tmp_list->data;
1303 if (info->requestor == event->window)
1306 tmp_list = tmp_list->next;
1309 if (tmp_list == NULL)
1312 /* Find out which target this is for */
1313 for (i=0; i<info->num_conversions; i++)
1315 if (info->conversions[i].property == event->atom &&
1316 info->conversions[i].offset != -1)
1320 info->idle_time = 0;
1322 if (info->conversions[i].offset == -2) /* only the last 0-length
1330 num_bytes = info->conversions[i].data.length -
1331 info->conversions[i].offset;
1332 buffer = info->conversions[i].data.data +
1333 info->conversions[i].offset;
1335 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1337 num_bytes = GTK_SELECTION_MAX_SIZE;
1338 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1341 info->conversions[i].offset = -2;
1343 #ifdef DEBUG_SELECTION
1344 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1345 num_bytes, info->conversions[i].offset,
1346 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1349 bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1350 gdk_property_change (info->requestor, event->atom,
1351 info->conversions[i].data.type,
1352 info->conversions[i].data.format,
1353 GDK_PROP_MODE_REPLACE,
1355 num_bytes / bytes_per_item);
1357 if (info->conversions[i].offset == -2)
1359 g_free (info->conversions[i].data.data);
1360 info->conversions[i].data.data = NULL;
1366 info->conversions[i].offset = -1;
1372 /* Check if we're finished with all the targets */
1374 if (info->num_incrs == 0)
1376 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1377 g_list_free (tmp_list);
1378 /* Let the timeout free it */
1384 /*************************************************************
1385 * gtk_selection_incr_timeout:
1386 * Timeout callback for the sending portion of the INCR
1389 * info: Information about this incr
1391 *************************************************************/
1394 gtk_selection_incr_timeout (GtkIncrInfo *info)
1399 GDK_THREADS_ENTER ();
1401 /* Determine if retrieval has finished by checking if it still in
1402 list of pending retrievals */
1404 tmp_list = current_incrs;
1407 if (info == (GtkIncrInfo *)tmp_list->data)
1409 tmp_list = tmp_list->next;
1412 /* If retrieval is finished */
1413 if (!tmp_list || info->idle_time >= 5)
1415 if (tmp_list && info->idle_time >= 5)
1417 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1418 g_list_free (tmp_list);
1421 g_free (info->conversions);
1422 /* FIXME: we should check if requestor window is still in use,
1423 and if not, remove it? */
1427 retval = FALSE; /* remove timeout */
1433 retval = TRUE; /* timeout will happen again */
1436 GDK_THREADS_LEAVE ();
1441 /*************************************************************
1442 * gtk_selection_notify:
1443 * Handler for "selection_notify_event" signals on windows
1444 * where a retrieval is currently in process. The selection
1445 * owner has responded to our conversion request.
1447 * widget: Widget getting signal
1448 * event: Selection event structure
1449 * info: Information about this retrieval
1451 * was event handled?
1452 *************************************************************/
1455 gtk_selection_notify (GtkWidget *widget,
1456 GdkEventSelection *event)
1459 GtkRetrievalInfo *info = NULL;
1460 guchar *buffer = NULL;
1465 #ifdef DEBUG_SELECTION
1466 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1467 event->selection, event->target, event->property);
1470 tmp_list = current_retrievals;
1473 info = (GtkRetrievalInfo *)tmp_list->data;
1474 if (info->widget == widget && info->selection == event->selection)
1476 tmp_list = tmp_list->next;
1479 if (!tmp_list) /* no retrieval in progress */
1482 if (event->property != GDK_NONE)
1483 length = gdk_selection_property_get (widget->window, &buffer,
1486 length = 0; /* silence gcc */
1488 if (event->property == GDK_NONE || buffer == NULL)
1490 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1491 g_list_free (tmp_list);
1492 /* structure will be freed in timeout */
1493 gtk_selection_retrieval_report (info,
1494 GDK_NONE, 0, NULL, -1, event->time);
1499 if (type == gtk_selection_atoms[INCR])
1501 /* The remainder of the selection will come through PropertyNotify
1504 info->notify_time = event->time;
1505 info->idle_time = 0;
1506 info->offset = 0; /* Mark as OK to proceed */
1507 gdk_window_set_events (widget->window,
1508 gdk_window_get_events (widget->window)
1509 | GDK_PROPERTY_CHANGE_MASK);
1513 /* We don't delete the info structure - that will happen in timeout */
1514 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1515 g_list_free (tmp_list);
1517 info->offset = length;
1518 gtk_selection_retrieval_report (info,
1520 buffer, length, event->time);
1523 gdk_property_delete (widget->window, event->property);
1530 /*************************************************************
1531 * gtk_selection_property_notify:
1532 * Handler for "property_notify_event" signals on windows
1533 * where a retrieval is currently in process. The selection
1534 * owner has added more data.
1536 * widget: Widget getting signal
1537 * event: Property event structure
1538 * info: Information about this retrieval
1540 * was event handled?
1541 *************************************************************/
1544 gtk_selection_property_notify (GtkWidget *widget,
1545 GdkEventProperty *event)
1548 GtkRetrievalInfo *info = NULL;
1554 g_return_val_if_fail (widget != NULL, FALSE);
1555 g_return_val_if_fail (event != NULL, FALSE);
1557 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1558 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1559 (event->atom != gdk_atom_intern ("GDK_SELECTION", FALSE))) /* not the right property */
1563 #ifdef DEBUG_SELECTION
1564 g_message ("PropertyNewValue, property %ld",
1568 tmp_list = current_retrievals;
1571 info = (GtkRetrievalInfo *)tmp_list->data;
1572 if (info->widget == widget)
1574 tmp_list = tmp_list->next;
1577 if (!tmp_list) /* No retrieval in progress */
1580 if (info->offset < 0) /* We haven't got the SelectionNotify
1581 for this retrieval yet */
1584 info->idle_time = 0;
1586 length = gdk_selection_property_get (widget->window, &new_buffer,
1588 gdk_property_delete (widget->window, event->atom);
1590 /* We could do a lot better efficiency-wise by paying attention to
1591 what length was sent in the initial INCR transaction, instead of
1592 doing memory allocation at every step. But its only guaranteed to
1593 be a _lower bound_ (pretty useless!) */
1595 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1597 /* Info structure will be freed in timeout */
1598 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1599 g_list_free (tmp_list);
1600 gtk_selection_retrieval_report (info,
1602 (type == GDK_NONE) ? NULL : info->buffer,
1603 (type == GDK_NONE) ? -1 : info->offset,
1606 else /* append on newly arrived data */
1610 #ifdef DEBUG_SELECTION
1611 g_message ("Start - Adding %d bytes at offset 0",
1614 info->buffer = new_buffer;
1615 info->offset = length;
1620 #ifdef DEBUG_SELECTION
1621 g_message ("Appending %d bytes at offset %d",
1622 length,info->offset);
1624 /* We copy length+1 bytes to preserve guaranteed null termination */
1625 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1626 memcpy (info->buffer + info->offset, new_buffer, length+1);
1627 info->offset += length;
1628 g_free (new_buffer);
1635 /*************************************************************
1636 * gtk_selection_retrieval_timeout:
1637 * Timeout callback while receiving a selection.
1639 * info: Information about this retrieval
1641 *************************************************************/
1644 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1649 GDK_THREADS_ENTER ();
1651 /* Determine if retrieval has finished by checking if it still in
1652 list of pending retrievals */
1654 tmp_list = current_retrievals;
1657 if (info == (GtkRetrievalInfo *)tmp_list->data)
1659 tmp_list = tmp_list->next;
1662 /* If retrieval is finished */
1663 if (!tmp_list || info->idle_time >= 5)
1665 if (tmp_list && info->idle_time >= 5)
1667 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1668 g_list_free (tmp_list);
1669 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1672 g_free (info->buffer);
1675 retval = FALSE; /* remove timeout */
1681 retval = TRUE; /* timeout will happen again */
1684 GDK_THREADS_LEAVE ();
1689 /*************************************************************
1690 * gtk_selection_retrieval_report:
1691 * Emits a "selection_received" signal.
1693 * info: information about the retrieval that completed
1694 * buffer: buffer containing data (NULL => errror)
1695 * time: timestamp for data in buffer
1697 *************************************************************/
1700 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1701 GdkAtom type, gint format,
1702 guchar *buffer, gint length,
1705 GtkSelectionData data;
1707 data.selection = info->selection;
1708 data.target = info->target;
1710 data.format = format;
1712 data.length = length;
1714 data.display = gtk_widget_get_display (info->widget);
1716 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1717 "selection_received",
1721 /*************************************************************
1722 * gtk_selection_invoke_handler:
1723 * Finds and invokes handler for specified
1724 * widget/selection/target combination, calls
1725 * gtk_selection_default_handler if none exists.
1728 * widget: selection owner
1729 * data: selection data [INOUT]
1730 * time: time from requeset
1733 * Number of bytes written to buffer, -1 if error
1734 *************************************************************/
1737 gtk_selection_invoke_handler (GtkWidget *widget,
1738 GtkSelectionData *data,
1741 GtkTargetList *target_list;
1745 g_return_if_fail (widget != NULL);
1747 target_list = gtk_selection_target_list_get (widget, data->selection);
1749 gtk_target_list_find (target_list, data->target, &info))
1751 gtk_signal_emit_by_name (GTK_OBJECT (widget),
1757 gtk_selection_default_handler (widget, data);
1760 /*************************************************************
1761 * gtk_selection_default_handler:
1762 * Handles some default targets that exist for any widget
1763 * If it can't fit results into buffer, returns -1. This
1764 * won't happen in any conceivable case, since it would
1765 * require 1000 selection targets!
1768 * widget: selection owner
1769 * data: selection data [INOUT]
1771 *************************************************************/
1774 gtk_selection_default_handler (GtkWidget *widget,
1775 GtkSelectionData *data)
1777 if (data->target == gtk_selection_atoms[TIMESTAMP])
1779 /* Time which was used to obtain selection */
1781 GtkSelectionInfo *selection_info;
1783 tmp_list = current_selections;
1786 selection_info = (GtkSelectionInfo *)tmp_list->data;
1787 if ((selection_info->widget == widget) &&
1788 (selection_info->selection == data->selection))
1790 gulong time = selection_info->time;
1792 gtk_selection_data_set (data,
1793 GDK_SELECTION_TYPE_INTEGER,
1800 tmp_list = tmp_list->next;
1805 else if (data->target == gtk_selection_atoms[TARGETS])
1807 /* List of all targets supported for this widget/selection pair */
1811 GtkTargetList *target_list;
1812 GtkTargetPair *pair;
1814 target_list = gtk_selection_target_list_get (widget,
1816 count = g_list_length (target_list->list) + 3;
1818 data->type = GDK_SELECTION_TYPE_ATOM;
1820 data->length = count * sizeof (GdkAtom);
1822 p = g_new (GdkAtom, count);
1823 data->data = (guchar *)p;
1825 *p++ = gtk_selection_atoms[TIMESTAMP];
1826 *p++ = gtk_selection_atoms[TARGETS];
1827 *p++ = gtk_selection_atoms[MULTIPLE];
1829 tmp_list = target_list->list;
1832 pair = (GtkTargetPair *)tmp_list->data;
1833 *p++ = pair->target;
1835 tmp_list = tmp_list->next;
1846 gtk_selection_data_copy (GtkSelectionData *selection_data)
1848 GtkSelectionData *new_data;
1850 g_return_val_if_fail (selection_data != NULL, NULL);
1852 new_data = g_new (GtkSelectionData, 1);
1853 *new_data = *selection_data;
1855 if (selection_data->data)
1857 new_data->data = g_malloc (selection_data->length + 1);
1858 memcpy (new_data->data, selection_data->data, selection_data->length + 1);
1865 gtk_selection_data_free (GtkSelectionData *data)
1867 g_return_if_fail (data != NULL);
1870 g_free (data->data);
1876 gtk_selection_data_get_type (void)
1878 static GType our_type = 0;
1881 our_type = g_boxed_type_register_static ("GtkSelectionData",
1882 (GBoxedCopyFunc) gtk_selection_data_copy,
1883 (GBoxedFreeFunc) gtk_selection_data_free);
1889 gtk_selection_bytes_per_item (gint format)
1894 return sizeof (char);
1897 return sizeof (short);
1900 return sizeof (long);
1903 g_assert_not_reached();