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"
61 /* #define DEBUG_SELECTION */
63 /* Maximum size of a sent chunk, in bytes. Also the default size of
65 #ifdef GDK_WINDOWING_WIN32
66 /* No chunks on Win32 */
67 #define GTK_SELECTION_MAX_SIZE G_MAXINT
69 #define GTK_SELECTION_MAX_SIZE 4000
72 #define IDLE_ABORT_TIME 300
82 typedef struct _GtkSelectionInfo GtkSelectionInfo;
83 typedef struct _GtkIncrConversion GtkIncrConversion;
84 typedef struct _GtkIncrInfo GtkIncrInfo;
85 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
87 struct _GtkSelectionInfo
90 GtkWidget *widget; /* widget that owns selection */
91 guint32 time; /* time used to acquire selection */
92 GdkDisplay *display; /* needed in gtk_selection_remove_all */
95 struct _GtkIncrConversion
97 GdkAtom target; /* Requested target */
98 GdkAtom property; /* Property to store in */
99 GtkSelectionData data; /* The data being supplied */
100 gint offset; /* Current offset in sent selection.
102 * -2 => Only the final (empty) portion
108 GdkWindow *requestor; /* Requestor window - we create a GdkWindow
109 so we can receive events */
110 GdkAtom selection; /* Selection we're sending */
112 GtkIncrConversion *conversions; /* Information about requested conversions -
113 * With MULTIPLE requests (benighted 1980's
114 * hardware idea), there can be more than
116 gint num_conversions;
117 gint num_incrs; /* number of remaining INCR style transactions */
122 struct _GtkRetrievalInfo
125 GdkAtom selection; /* Selection being retrieved. */
126 GdkAtom target; /* Form of selection that we requested */
127 guint32 idle_time; /* Number of seconds since we last heard
128 from selection owner */
129 guchar *buffer; /* Buffer in which to accumulate results */
130 gint offset; /* Current offset in buffer, -1 indicates
132 guint32 notify_time; /* Timestamp from SelectionNotify */
135 /* Local Functions */
136 static void gtk_selection_init (void);
137 static gint gtk_selection_incr_timeout (GtkIncrInfo *info);
138 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
139 static void gtk_selection_retrieval_report (GtkRetrievalInfo *info,
145 static void gtk_selection_invoke_handler (GtkWidget *widget,
146 GtkSelectionData *data,
148 static void gtk_selection_default_handler (GtkWidget *widget,
149 GtkSelectionData *data);
150 static int gtk_selection_bytes_per_item (gint format);
153 static gint initialize = TRUE;
154 static GList *current_retrievals = NULL;
155 static GList *current_incrs = NULL;
156 static GList *current_selections = NULL;
158 static GdkAtom gtk_selection_atoms[LAST_ATOM];
159 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
170 gtk_target_list_new (const GtkTargetEntry *targets,
173 GtkTargetList *result = g_new (GtkTargetList, 1);
175 result->ref_count = 1;
178 gtk_target_list_add_table (result, targets, ntargets);
184 gtk_target_list_ref (GtkTargetList *list)
186 g_return_if_fail (list != NULL);
192 gtk_target_list_unref (GtkTargetList *list)
194 g_return_if_fail (list != NULL);
195 g_return_if_fail (list->ref_count > 0);
198 if (list->ref_count == 0)
200 GList *tmp_list = list->list;
203 GtkTargetPair *pair = tmp_list->data;
206 tmp_list = tmp_list->next;
209 g_list_free (list->list);
215 gtk_target_list_add (GtkTargetList *list,
222 g_return_if_fail (list != NULL);
224 pair = g_new (GtkTargetPair, 1);
225 pair->target = target;
229 list->list = g_list_append (list->list, pair);
233 gtk_target_list_add_table (GtkTargetList *list,
234 const GtkTargetEntry *targets,
239 for (i=ntargets-1; i >= 0; i--)
241 GtkTargetPair *pair = g_new (GtkTargetPair, 1);
242 pair->target = gdk_atom_intern (targets[i].target, FALSE);
243 pair->flags = targets[i].flags;
244 pair->info = targets[i].info;
246 list->list = g_list_prepend (list->list, pair);
251 gtk_target_list_remove (GtkTargetList *list,
256 g_return_if_fail (list != NULL);
258 tmp_list = list->list;
261 GtkTargetPair *pair = tmp_list->data;
263 if (pair->target == target)
267 list->list = g_list_remove_link (list->list, tmp_list);
268 g_list_free_1 (tmp_list);
273 tmp_list = tmp_list->next;
278 gtk_target_list_find (GtkTargetList *list,
282 GList *tmp_list = list->list;
285 GtkTargetPair *pair = tmp_list->data;
287 if (pair->target == target)
292 tmp_list = tmp_list->next;
299 * gtk_selection_owner_set_for_display:
300 * @display: the #Gdkdisplay where the selection is set
301 * @widget: new selection owner (a #GdkWidget), or %NULL.
302 * @selection: an interned atom representing the selection to claim.
303 * @time_: timestamp with which to claim the selection
305 * Claim ownership of a given selection for a particular widget, or,
306 * if @widget is %NULL, release ownership of the selection.
308 * Return value: TRUE if the operation succeeded
311 gtk_selection_owner_set_for_display (GdkDisplay *display,
317 GtkWidget *old_owner;
318 GtkSelectionInfo *selection_info = NULL;
321 g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
322 g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
323 g_return_val_if_fail (widget == NULL || gtk_widget_get_display (widget) == display, FALSE);
328 window = widget->window;
330 tmp_list = current_selections;
333 if (((GtkSelectionInfo *)tmp_list->data)->selection == selection)
335 selection_info = tmp_list->data;
339 tmp_list = tmp_list->next;
342 if (gdk_selection_owner_set_for_display (display, window, selection, time, TRUE))
350 old_owner = selection_info->widget;
351 current_selections = g_list_remove_link (current_selections,
353 g_list_free (tmp_list);
354 g_free (selection_info);
359 if (selection_info == NULL)
361 selection_info = g_new (GtkSelectionInfo, 1);
362 selection_info->selection = selection;
363 selection_info->widget = widget;
364 selection_info->time = time;
365 selection_info->display = display;
366 current_selections = g_list_prepend (current_selections,
371 old_owner = selection_info->widget;
372 selection_info->widget = widget;
373 selection_info->time = time;
374 selection_info->display = display;
377 /* If another widget in the application lost the selection,
378 * send it a GDK_SELECTION_CLEAR event.
380 if (old_owner && old_owner != widget)
382 GdkEvent *event = gdk_event_new (GDK_SELECTION_CLEAR);
384 event->selection.window = g_object_ref (old_owner->window);
385 event->selection.selection = selection;
386 event->selection.time = time;
388 gtk_widget_event (old_owner, event);
390 gdk_event_free (event);
399 * gtk_selection_owner_set:
400 * @widget: a #GtkWidget, or %NULL.
401 * @selection: an interned atom representing the selection to claim
402 * @time_: timestamp with which to claim the selection
404 * Claims ownership of a given selection for a particular widget,
405 * or, if @widget is %NULL, release ownership of the selection.
407 * Return value: %TRUE if the operation succeeded
410 gtk_selection_owner_set (GtkWidget *widget,
416 g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
419 display = gtk_widget_get_display (widget);
423 g_warning ("gtk_selection_owner_set (NULL,...) is not multihead safe"));
425 display = gdk_display_get_default ();
428 return gtk_selection_owner_set_for_display (display, widget,
432 /*************************************************************
433 * gtk_selection_add_target
434 * Add specified target to list of supported targets
437 * widget: The widget for which this target applies
440 * info: guint to pass to to the selection_get signal
443 *************************************************************/
445 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
447 struct _GtkSelectionTargetList {
452 static GtkTargetList *
453 gtk_selection_target_list_get (GtkWidget *widget,
456 GtkSelectionTargetList *sellist;
460 lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
465 sellist = tmp_list->data;
466 if (sellist->selection == selection)
467 return sellist->list;
468 tmp_list = tmp_list->next;
471 sellist = g_new (GtkSelectionTargetList, 1);
472 sellist->selection = selection;
473 sellist->list = gtk_target_list_new (NULL, 0);
475 lists = g_list_prepend (lists, sellist);
476 g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
478 return sellist->list;
482 gtk_selection_target_list_remove (GtkWidget *widget)
484 GtkSelectionTargetList *sellist;
488 lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
493 sellist = tmp_list->data;
495 gtk_target_list_unref (sellist->list);
498 tmp_list = tmp_list->next;
502 g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, NULL);
506 * gtk_selection_clear_targets:
507 * @widget: a #GtkWidget
508 * @selection: an atom representing a selection
510 * Remove all targets registered for the given selection for the
514 gtk_selection_clear_targets (GtkWidget *widget,
517 GtkSelectionTargetList *sellist;
521 lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
526 sellist = tmp_list->data;
527 if (sellist->selection == selection)
529 lists = g_list_delete_link (lists, tmp_list);
530 gtk_target_list_unref (sellist->list);
536 tmp_list = tmp_list->next;
539 g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
543 gtk_selection_add_target (GtkWidget *widget,
550 g_return_if_fail (widget != NULL);
552 list = gtk_selection_target_list_get (widget, selection);
553 gtk_target_list_add (list, target, 0, info);
557 gtk_selection_add_targets (GtkWidget *widget,
559 const GtkTargetEntry *targets,
564 g_return_if_fail (widget != NULL);
565 g_return_if_fail (targets != NULL);
567 list = gtk_selection_target_list_get (widget, selection);
568 gtk_target_list_add_table (list, targets, ntargets);
572 /*************************************************************
573 * gtk_selection_remove_all:
574 * Removes all handlers and unsets ownership of all
575 * selections for a widget. Called when widget is being
581 *************************************************************/
584 gtk_selection_remove_all (GtkWidget *widget)
588 GtkSelectionInfo *selection_info;
590 /* Remove pending requests/incrs for this widget */
592 tmp_list = current_retrievals;
595 next = tmp_list->next;
596 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
598 current_retrievals = g_list_remove_link (current_retrievals,
600 /* structure will be freed in timeout */
601 g_list_free (tmp_list);
606 /* Disclaim ownership of any selections */
608 tmp_list = current_selections;
611 next = tmp_list->next;
612 selection_info = (GtkSelectionInfo *)tmp_list->data;
614 if (selection_info->widget == widget)
616 gdk_selection_owner_set_for_display (selection_info->display,
618 selection_info->selection,
619 GDK_CURRENT_TIME, FALSE);
620 current_selections = g_list_remove_link (current_selections,
622 g_list_free (tmp_list);
623 g_free (selection_info);
629 /* Remove all selection lists */
630 gtk_selection_target_list_remove (widget);
633 /*************************************************************
634 * gtk_selection_convert:
635 * Request the contents of a selection. When received,
636 * a "selection_received" signal will be generated.
639 * widget: The widget which acts as requestor
640 * selection: Which selection to get
641 * target: Form of information desired (e.g., STRING)
642 * time: Time of request (usually of triggering event)
643 * In emergency, you could use GDK_CURRENT_TIME
646 * TRUE if requested succeeded. FALSE if we could not process
647 * request. (e.g., there was already a request in process for
649 *************************************************************/
652 gtk_selection_convert (GtkWidget *widget,
657 GtkRetrievalInfo *info;
659 GdkWindow *owner_window;
662 g_return_val_if_fail (widget != NULL, FALSE);
665 gtk_selection_init ();
667 if (!GTK_WIDGET_REALIZED (widget))
668 gtk_widget_realize (widget);
670 /* Check to see if there are already any retrievals in progress for
671 this widget. If we changed GDK to use the selection for the
672 window property in which to store the retrieved information, then
673 we could support multiple retrievals for different selections.
674 This might be useful for DND. */
676 tmp_list = current_retrievals;
679 info = (GtkRetrievalInfo *)tmp_list->data;
680 if (info->widget == widget)
682 tmp_list = tmp_list->next;
685 info = g_new (GtkRetrievalInfo, 1);
687 info->widget = widget;
688 info->selection = selection;
689 info->target = target;
694 /* Check if this process has current owner. If so, call handler
695 procedure directly to avoid deadlocks with INCR. */
697 display = gtk_widget_get_display (widget);
698 owner_window = gdk_selection_owner_get_for_display (display, selection);
700 if (owner_window != NULL)
702 GtkWidget *owner_widget;
703 GtkSelectionData selection_data;
705 selection_data.selection = selection;
706 selection_data.target = target;
707 selection_data.data = NULL;
708 selection_data.length = -1;
709 selection_data.display = display;
711 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
713 if (owner_widget != NULL)
715 gtk_selection_invoke_handler (owner_widget,
719 gtk_selection_retrieval_report (info,
721 selection_data.format,
723 selection_data.length,
726 g_free (selection_data.data);
733 /* Otherwise, we need to go through X */
735 current_retrievals = g_list_append (current_retrievals, info);
736 gdk_selection_convert (widget->window, selection, target, time);
737 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
742 /*************************************************************
743 * gtk_selection_data_set:
744 * Store new data into a GtkSelectionData object. Should
745 * _only_ by called from a selection handler callback.
746 * Null terminates the stored data.
748 * type: the type of selection data
749 * format: format (number of bits in a unit)
750 * data: pointer to the data (will be copied)
751 * length: length of the data
753 *************************************************************/
756 gtk_selection_data_set (GtkSelectionData *selection_data,
762 if (selection_data->data)
763 g_free (selection_data->data);
765 selection_data->type = type;
766 selection_data->format = format;
770 selection_data->data = g_new (guchar, length+1);
771 memcpy (selection_data->data, data, length);
772 selection_data->data[length] = 0;
776 g_return_if_fail (length <= 0);
779 selection_data->data = NULL;
781 selection_data->data = g_strdup("");
784 selection_data->length = length;
787 static GdkAtom utf8_atom;
788 static GdkAtom text_atom;
789 static GdkAtom ctext_atom;
796 utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
797 text_atom = gdk_atom_intern ("TEXT", FALSE);
798 ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
803 * gtk_selection_data_set_text:
804 * @selection_data: a #GtkSelectionData
805 * @str: a UTF-8 string
806 * @len: the length of @str, or -1 if @str is nul-terminated.
808 * Sets the contents of the selection from a UTF-8 encoded string.
809 * The string is converted to the form determined by
810 * @selection_data->target.
812 * Return value: %TRUE if the selection was successfully set,
816 gtk_selection_data_set_text (GtkSelectionData *selection_data,
820 gboolean result = FALSE;
827 if (selection_data->target == utf8_atom)
829 gtk_selection_data_set (selection_data,
831 8, (guchar *)str, len);
834 else if (selection_data->target == GDK_TARGET_STRING)
836 gchar *tmp = g_strndup (str, len);
837 gchar *latin1 = gdk_utf8_to_string_target (tmp);
842 gtk_selection_data_set (selection_data,
843 GDK_SELECTION_TYPE_STRING,
844 8, latin1, strlen (latin1));
851 else if (selection_data->target == ctext_atom ||
852 selection_data->target == text_atom)
860 tmp = g_strndup (str, len);
861 if (gdk_utf8_to_compound_text_for_display (selection_data->display, tmp,
862 &encoding, &format, &text, &new_length))
864 gtk_selection_data_set (selection_data, encoding, format, text, new_length);
865 gdk_free_compound_text (text);
877 * gtk_selection_data_get_text:
878 * @selection_data: a #GtkSelectionData
880 * Gets the contents of the selection data as a UTF-8 string.
882 * Return value: if the selection data contained a recognized
883 * text type and it could be converted to UTF-8, a newly allocated
884 * string containing the converted text, otherwise %NULL.
885 * If the result is non-%NULL it must be freed with g_free().
888 gtk_selection_data_get_text (GtkSelectionData *selection_data)
890 guchar *result = NULL;
894 if (selection_data->length >= 0 &&
895 (selection_data->type == GDK_TARGET_STRING ||
896 selection_data->type == ctext_atom ||
897 selection_data->type == utf8_atom))
901 gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
902 selection_data->type,
903 selection_data->format,
904 selection_data->data,
905 selection_data->length,
910 for (i = 1; i < count; i++)
919 * gtk_selection_data_get_targets:
920 * @selection_data: a #GtkSelectionData object
921 * @targets: location to store an array of targets. The result
922 * stored here must be freed with g_free().
923 * @n_atoms: location to store number of items in @targets.
925 * Gets the contents of @selection_data as an array of targets.
926 * This can be used to interpret the results of getting
927 * the standard TARGETS target that is always supplied for
930 * Return value: %TRUE if @selection_data contains a valid
931 * array of targets, otherwise %FALSE.
934 gtk_selection_data_get_targets (GtkSelectionData *selection_data,
938 if (selection_data->length >= 0 &&
939 selection_data->format == 32 &&
940 selection_data->type == GDK_SELECTION_TYPE_ATOM)
943 *targets = g_memdup (selection_data->data, selection_data->length);
945 *n_atoms = selection_data->length / sizeof (GdkAtom);
961 * gtk_selection_data_targets_include_text:
962 * @selection_data: a #GtkSelectionData object
964 * Given a #GtkSelectionData object holding a list of targets,
965 * determines if any of the targets in @targets can be used to
968 * Return value: %TRUE if @selection_data holds a list of targets,
969 * and a suitable target for text is included, otherwise %FALSE.
972 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
977 gboolean result = FALSE;
979 if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
981 for (i=0; i < n_targets; i++)
983 if (targets[i] == gdk_atom_intern ("STRING", FALSE) ||
984 targets[i] == gdk_atom_intern ("TEXT", FALSE) ||
985 targets[i] == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
986 targets[i] == gdk_atom_intern ("UTF8_STRING", FALSE))
996 /*************************************************************
997 * gtk_selection_init:
998 * Initialize local variables
1002 *************************************************************/
1005 gtk_selection_init (void)
1007 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
1008 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
1009 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
1010 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
1016 * gtk_selection_clear:
1017 * @widget: a #GtkWidget
1020 * The default handler for the GtkWidget::selection_clear_event
1021 * signal. Instead of calling this function, chain up from
1022 * your selection_clear_event handler. Calling this function
1023 * from any other context is illegal. This function will
1024 * be deprecated in future versions of GTK+.
1026 * Return value: %TRUE if the event was handled, otherwise false
1029 gtk_selection_clear (GtkWidget *widget,
1030 GdkEventSelection *event)
1032 /* Note that we filter clear events in gdkselection-x11.c, so
1033 * that we only will get here if the clear event actually
1034 * represents a change that we didn't do ourself.
1037 GtkSelectionInfo *selection_info = NULL;
1039 tmp_list = current_selections;
1042 selection_info = (GtkSelectionInfo *)tmp_list->data;
1044 if ((selection_info->selection == event->selection) &&
1045 (selection_info->widget == widget))
1048 tmp_list = tmp_list->next;
1053 current_selections = g_list_remove_link (current_selections, tmp_list);
1054 g_list_free (tmp_list);
1055 g_free (selection_info);
1062 /*************************************************************
1063 * _gtk_selection_request:
1064 * Handler for "selection_request_event"
1069 *************************************************************/
1072 _gtk_selection_request (GtkWidget *widget,
1073 GdkEventSelection *event)
1081 gtk_selection_init ();
1083 /* Check if we own selection */
1085 tmp_list = current_selections;
1088 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1090 if ((selection_info->selection == event->selection) &&
1091 (selection_info->widget == widget))
1094 tmp_list = tmp_list->next;
1097 if (tmp_list == NULL)
1100 info = g_new (GtkIncrInfo, 1);
1102 g_object_ref (widget);
1104 info->selection = event->selection;
1105 info->num_incrs = 0;
1107 /* Create GdkWindow structure for the requestor */
1109 info->requestor = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
1111 if (!info->requestor)
1112 info->requestor = gdk_window_foreign_new_for_display (gtk_widget_get_display (widget),
1115 /* Determine conversions we need to perform */
1117 if (event->target == gtk_selection_atoms[MULTIPLE])
1125 gdk_error_trap_push ();
1126 if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
1127 0, GTK_SELECTION_MAX_SIZE, FALSE,
1128 &type, &format, &length, &mult_atoms))
1130 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1136 g_free (mult_atoms);
1140 gdk_error_trap_pop ();
1142 info->num_conversions = length / (2*sizeof (GdkAtom));
1143 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1145 for (i=0; i<info->num_conversions; i++)
1147 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1148 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1151 else /* only a single conversion */
1153 info->conversions = g_new (GtkIncrConversion, 1);
1154 info->num_conversions = 1;
1155 info->conversions[0].target = event->target;
1156 info->conversions[0].property = event->property;
1157 mult_atoms = (guchar *)info->conversions;
1160 /* Loop through conversions and determine which of these are big
1161 enough to require doing them via INCR */
1162 for (i=0; i<info->num_conversions; i++)
1164 GtkSelectionData data;
1167 data.selection = event->selection;
1168 data.target = info->conversions[i].target;
1171 data.display = gtk_widget_get_display (widget);
1173 #ifdef DEBUG_SELECTION
1174 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1175 event->selection, info->conversions[i].target,
1176 gdk_atom_name (info->conversions[i].target),
1177 event->requestor, event->property);
1180 gtk_selection_invoke_handler (widget, &data, event->time);
1182 if (data.length < 0)
1184 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
1185 info->conversions[i].property = GDK_NONE;
1189 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1191 items = data.length / gtk_selection_bytes_per_item (data.format);
1193 if (data.length > GTK_SELECTION_MAX_SIZE)
1195 /* Sending via INCR */
1197 info->conversions[i].offset = 0;
1198 info->conversions[i].data = data;
1201 gdk_property_change (info->requestor,
1202 info->conversions[i].property,
1203 gtk_selection_atoms[INCR],
1205 GDK_PROP_MODE_REPLACE,
1206 (guchar *)&items, 1);
1210 info->conversions[i].offset = -1;
1212 gdk_property_change (info->requestor,
1213 info->conversions[i].property,
1216 GDK_PROP_MODE_REPLACE,
1223 /* If we have some INCR's, we need to send the rest of the data in
1226 if (info->num_incrs > 0)
1228 /* FIXME: this could be dangerous if window doesn't still
1231 #ifdef DEBUG_SELECTION
1232 g_message ("Starting INCR...");
1235 gdk_window_set_events (info->requestor,
1236 gdk_window_get_events (info->requestor) |
1237 GDK_PROPERTY_CHANGE_MASK);
1238 current_incrs = g_list_append (current_incrs, info);
1239 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
1242 /* If it was a MULTIPLE request, set the property to indicate which
1243 conversions succeeded */
1244 if (event->target == gtk_selection_atoms[MULTIPLE])
1246 gdk_property_change (info->requestor, event->property,
1247 gdk_atom_intern ("ATOM_PAIR", FALSE), 32,
1248 GDK_PROP_MODE_REPLACE,
1249 mult_atoms, 2*info->num_conversions);
1250 g_free (mult_atoms);
1253 if (info->num_conversions == 1 &&
1254 info->conversions[0].property == GDK_NONE)
1256 /* Reject the entire conversion */
1257 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1266 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1274 if (info->num_incrs == 0)
1276 g_free (info->conversions);
1280 g_object_unref (widget);
1285 /*************************************************************
1286 * _gtk_selection_incr_event:
1287 * Called whenever an PropertyNotify event occurs for an
1288 * GdkWindow with user_data == NULL. These will be notifications
1289 * that a window we are sending the selection to via the
1290 * INCR protocol has deleted a property and is ready for
1294 * window: the requestor window
1295 * event: the property event structure
1298 *************************************************************/
1301 _gtk_selection_incr_event (GdkWindow *window,
1302 GdkEventProperty *event)
1305 GtkIncrInfo *info = NULL;
1311 if (event->state != GDK_PROPERTY_DELETE)
1314 #ifdef DEBUG_SELECTION
1315 g_message ("PropertyDelete, property %ld", event->atom);
1318 /* Now find the appropriate ongoing INCR */
1319 tmp_list = current_incrs;
1322 info = (GtkIncrInfo *)tmp_list->data;
1323 if (info->requestor == event->window)
1326 tmp_list = tmp_list->next;
1329 if (tmp_list == NULL)
1332 /* Find out which target this is for */
1333 for (i=0; i<info->num_conversions; i++)
1335 if (info->conversions[i].property == event->atom &&
1336 info->conversions[i].offset != -1)
1340 info->idle_time = 0;
1342 if (info->conversions[i].offset == -2) /* only the last 0-length
1350 num_bytes = info->conversions[i].data.length -
1351 info->conversions[i].offset;
1352 buffer = info->conversions[i].data.data +
1353 info->conversions[i].offset;
1355 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1357 num_bytes = GTK_SELECTION_MAX_SIZE;
1358 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1361 info->conversions[i].offset = -2;
1363 #ifdef DEBUG_SELECTION
1364 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1365 num_bytes, info->conversions[i].offset,
1366 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1369 bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1370 gdk_property_change (info->requestor, event->atom,
1371 info->conversions[i].data.type,
1372 info->conversions[i].data.format,
1373 GDK_PROP_MODE_REPLACE,
1375 num_bytes / bytes_per_item);
1377 if (info->conversions[i].offset == -2)
1379 g_free (info->conversions[i].data.data);
1380 info->conversions[i].data.data = NULL;
1386 info->conversions[i].offset = -1;
1392 /* Check if we're finished with all the targets */
1394 if (info->num_incrs == 0)
1396 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1397 g_list_free (tmp_list);
1398 /* Let the timeout free it */
1404 /*************************************************************
1405 * gtk_selection_incr_timeout:
1406 * Timeout callback for the sending portion of the INCR
1409 * info: Information about this incr
1411 *************************************************************/
1414 gtk_selection_incr_timeout (GtkIncrInfo *info)
1419 GDK_THREADS_ENTER ();
1421 /* Determine if retrieval has finished by checking if it still in
1422 list of pending retrievals */
1424 tmp_list = current_incrs;
1427 if (info == (GtkIncrInfo *)tmp_list->data)
1429 tmp_list = tmp_list->next;
1432 /* If retrieval is finished */
1433 if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1435 if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1437 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1438 g_list_free (tmp_list);
1441 g_free (info->conversions);
1442 /* FIXME: we should check if requestor window is still in use,
1443 and if not, remove it? */
1447 retval = FALSE; /* remove timeout */
1453 retval = TRUE; /* timeout will happen again */
1456 GDK_THREADS_LEAVE ();
1461 /*************************************************************
1462 * _gtk_selection_notify:
1463 * Handler for "selection_notify_event" signals on windows
1464 * where a retrieval is currently in process. The selection
1465 * owner has responded to our conversion request.
1467 * widget: Widget getting signal
1468 * event: Selection event structure
1469 * info: Information about this retrieval
1471 * was event handled?
1472 *************************************************************/
1475 _gtk_selection_notify (GtkWidget *widget,
1476 GdkEventSelection *event)
1479 GtkRetrievalInfo *info = NULL;
1480 guchar *buffer = NULL;
1485 #ifdef DEBUG_SELECTION
1486 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1487 event->selection, event->target, event->property);
1490 tmp_list = current_retrievals;
1493 info = (GtkRetrievalInfo *)tmp_list->data;
1494 if (info->widget == widget && info->selection == event->selection)
1496 tmp_list = tmp_list->next;
1499 if (!tmp_list) /* no retrieval in progress */
1502 if (event->property != GDK_NONE)
1503 length = gdk_selection_property_get (widget->window, &buffer,
1506 length = 0; /* silence gcc */
1508 if (event->property == GDK_NONE || buffer == NULL)
1510 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1511 g_list_free (tmp_list);
1512 /* structure will be freed in timeout */
1513 gtk_selection_retrieval_report (info,
1514 GDK_NONE, 0, NULL, -1, event->time);
1519 if (type == gtk_selection_atoms[INCR])
1521 /* The remainder of the selection will come through PropertyNotify
1524 info->notify_time = event->time;
1525 info->idle_time = 0;
1526 info->offset = 0; /* Mark as OK to proceed */
1527 gdk_window_set_events (widget->window,
1528 gdk_window_get_events (widget->window)
1529 | GDK_PROPERTY_CHANGE_MASK);
1533 /* We don't delete the info structure - that will happen in timeout */
1534 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1535 g_list_free (tmp_list);
1537 info->offset = length;
1538 gtk_selection_retrieval_report (info,
1540 buffer, length, event->time);
1543 gdk_property_delete (widget->window, event->property);
1550 /*************************************************************
1551 * _gtk_selection_property_notify:
1552 * Handler for "property_notify_event" signals on windows
1553 * where a retrieval is currently in process. The selection
1554 * owner has added more data.
1556 * widget: Widget getting signal
1557 * event: Property event structure
1558 * info: Information about this retrieval
1560 * was event handled?
1561 *************************************************************/
1564 _gtk_selection_property_notify (GtkWidget *widget,
1565 GdkEventProperty *event)
1568 GtkRetrievalInfo *info = NULL;
1574 g_return_val_if_fail (widget != NULL, FALSE);
1575 g_return_val_if_fail (event != NULL, FALSE);
1577 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1578 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1579 (event->atom != gdk_atom_intern ("GDK_SELECTION", FALSE))) /* not the right property */
1583 #ifdef DEBUG_SELECTION
1584 g_message ("PropertyNewValue, property %ld",
1588 tmp_list = current_retrievals;
1591 info = (GtkRetrievalInfo *)tmp_list->data;
1592 if (info->widget == widget)
1594 tmp_list = tmp_list->next;
1597 if (!tmp_list) /* No retrieval in progress */
1600 if (info->offset < 0) /* We haven't got the SelectionNotify
1601 for this retrieval yet */
1604 info->idle_time = 0;
1606 length = gdk_selection_property_get (widget->window, &new_buffer,
1608 gdk_property_delete (widget->window, event->atom);
1610 /* We could do a lot better efficiency-wise by paying attention to
1611 what length was sent in the initial INCR transaction, instead of
1612 doing memory allocation at every step. But its only guaranteed to
1613 be a _lower bound_ (pretty useless!) */
1615 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1617 /* Info structure will be freed in timeout */
1618 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1619 g_list_free (tmp_list);
1620 gtk_selection_retrieval_report (info,
1622 (type == GDK_NONE) ? NULL : info->buffer,
1623 (type == GDK_NONE) ? -1 : info->offset,
1626 else /* append on newly arrived data */
1630 #ifdef DEBUG_SELECTION
1631 g_message ("Start - Adding %d bytes at offset 0",
1634 info->buffer = new_buffer;
1635 info->offset = length;
1640 #ifdef DEBUG_SELECTION
1641 g_message ("Appending %d bytes at offset %d",
1642 length,info->offset);
1644 /* We copy length+1 bytes to preserve guaranteed null termination */
1645 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1646 memcpy (info->buffer + info->offset, new_buffer, length+1);
1647 info->offset += length;
1648 g_free (new_buffer);
1655 /*************************************************************
1656 * gtk_selection_retrieval_timeout:
1657 * Timeout callback while receiving a selection.
1659 * info: Information about this retrieval
1661 *************************************************************/
1664 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1669 GDK_THREADS_ENTER ();
1671 /* Determine if retrieval has finished by checking if it still in
1672 list of pending retrievals */
1674 tmp_list = current_retrievals;
1677 if (info == (GtkRetrievalInfo *)tmp_list->data)
1679 tmp_list = tmp_list->next;
1682 /* If retrieval is finished */
1683 if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1685 if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1687 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1688 g_list_free (tmp_list);
1689 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1692 g_free (info->buffer);
1695 retval = FALSE; /* remove timeout */
1701 retval = TRUE; /* timeout will happen again */
1704 GDK_THREADS_LEAVE ();
1709 /*************************************************************
1710 * gtk_selection_retrieval_report:
1711 * Emits a "selection_received" signal.
1713 * info: information about the retrieval that completed
1714 * buffer: buffer containing data (NULL => errror)
1715 * time: timestamp for data in buffer
1717 *************************************************************/
1720 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1721 GdkAtom type, gint format,
1722 guchar *buffer, gint length,
1725 GtkSelectionData data;
1727 data.selection = info->selection;
1728 data.target = info->target;
1730 data.format = format;
1732 data.length = length;
1734 data.display = gtk_widget_get_display (info->widget);
1736 g_signal_emit_by_name (info->widget,
1737 "selection_received",
1741 /*************************************************************
1742 * gtk_selection_invoke_handler:
1743 * Finds and invokes handler for specified
1744 * widget/selection/target combination, calls
1745 * gtk_selection_default_handler if none exists.
1748 * widget: selection owner
1749 * data: selection data [INOUT]
1750 * time: time from requeset
1753 * Number of bytes written to buffer, -1 if error
1754 *************************************************************/
1757 gtk_selection_invoke_handler (GtkWidget *widget,
1758 GtkSelectionData *data,
1761 GtkTargetList *target_list;
1765 g_return_if_fail (widget != NULL);
1767 target_list = gtk_selection_target_list_get (widget, data->selection);
1769 gtk_target_list_find (target_list, data->target, &info))
1771 g_signal_emit_by_name (widget,
1777 gtk_selection_default_handler (widget, data);
1780 /*************************************************************
1781 * gtk_selection_default_handler:
1782 * Handles some default targets that exist for any widget
1783 * If it can't fit results into buffer, returns -1. This
1784 * won't happen in any conceivable case, since it would
1785 * require 1000 selection targets!
1788 * widget: selection owner
1789 * data: selection data [INOUT]
1791 *************************************************************/
1794 gtk_selection_default_handler (GtkWidget *widget,
1795 GtkSelectionData *data)
1797 if (data->target == gtk_selection_atoms[TIMESTAMP])
1799 /* Time which was used to obtain selection */
1801 GtkSelectionInfo *selection_info;
1803 tmp_list = current_selections;
1806 selection_info = (GtkSelectionInfo *)tmp_list->data;
1807 if ((selection_info->widget == widget) &&
1808 (selection_info->selection == data->selection))
1810 gulong time = selection_info->time;
1812 gtk_selection_data_set (data,
1813 GDK_SELECTION_TYPE_INTEGER,
1820 tmp_list = tmp_list->next;
1825 else if (data->target == gtk_selection_atoms[TARGETS])
1827 /* List of all targets supported for this widget/selection pair */
1831 GtkTargetList *target_list;
1832 GtkTargetPair *pair;
1834 target_list = gtk_selection_target_list_get (widget,
1836 count = g_list_length (target_list->list) + 3;
1838 data->type = GDK_SELECTION_TYPE_ATOM;
1840 data->length = count * sizeof (GdkAtom);
1842 p = g_new (GdkAtom, count);
1843 data->data = (guchar *)p;
1845 *p++ = gtk_selection_atoms[TIMESTAMP];
1846 *p++ = gtk_selection_atoms[TARGETS];
1847 *p++ = gtk_selection_atoms[MULTIPLE];
1849 tmp_list = target_list->list;
1852 pair = (GtkTargetPair *)tmp_list->data;
1853 *p++ = pair->target;
1855 tmp_list = tmp_list->next;
1866 gtk_selection_data_copy (GtkSelectionData *selection_data)
1868 GtkSelectionData *new_data;
1870 g_return_val_if_fail (selection_data != NULL, NULL);
1872 new_data = g_new (GtkSelectionData, 1);
1873 *new_data = *selection_data;
1875 if (selection_data->data)
1877 new_data->data = g_malloc (selection_data->length + 1);
1878 memcpy (new_data->data, selection_data->data, selection_data->length + 1);
1885 gtk_selection_data_free (GtkSelectionData *data)
1887 g_return_if_fail (data != NULL);
1890 g_free (data->data);
1896 gtk_selection_data_get_type (void)
1898 static GType our_type = 0;
1901 our_type = g_boxed_type_register_static ("GtkSelectionData",
1902 (GBoxedCopyFunc) gtk_selection_data_copy,
1903 (GBoxedFreeFunc) gtk_selection_data_free);
1909 gtk_selection_bytes_per_item (gint format)
1914 return sizeof (char);
1917 return sizeof (short);
1920 return sizeof (long);
1923 g_assert_not_reached();