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
313 gtk_selection_owner_set_for_display (GdkDisplay *display,
319 GtkWidget *old_owner;
320 GtkSelectionInfo *selection_info = NULL;
323 g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
324 g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
325 g_return_val_if_fail (widget == NULL || gtk_widget_get_display (widget) == display, FALSE);
330 window = widget->window;
332 tmp_list = current_selections;
335 if (((GtkSelectionInfo *)tmp_list->data)->selection == selection)
337 selection_info = tmp_list->data;
341 tmp_list = tmp_list->next;
344 if (gdk_selection_owner_set_for_display (display, window, selection, time, TRUE))
352 old_owner = selection_info->widget;
353 current_selections = g_list_remove_link (current_selections,
355 g_list_free (tmp_list);
356 g_free (selection_info);
361 if (selection_info == NULL)
363 selection_info = g_new (GtkSelectionInfo, 1);
364 selection_info->selection = selection;
365 selection_info->widget = widget;
366 selection_info->time = time;
367 selection_info->display = display;
368 current_selections = g_list_prepend (current_selections,
373 old_owner = selection_info->widget;
374 selection_info->widget = widget;
375 selection_info->time = time;
376 selection_info->display = display;
379 /* If another widget in the application lost the selection,
380 * send it a GDK_SELECTION_CLEAR event.
382 if (old_owner && old_owner != widget)
384 GdkEvent *event = gdk_event_new (GDK_SELECTION_CLEAR);
386 event->selection.window = g_object_ref (old_owner->window);
387 event->selection.selection = selection;
388 event->selection.time = time;
390 gtk_widget_event (old_owner, event);
392 gdk_event_free (event);
401 * gtk_selection_owner_set:
402 * @widget: a #GtkWidget, or %NULL.
403 * @selection: an interned atom representing the selection to claim
404 * @time_: timestamp with which to claim the selection
406 * Claims ownership of a given selection for a particular widget,
407 * or, if @widget is %NULL, release ownership of the selection.
409 * Return value: %TRUE if the operation succeeded
412 gtk_selection_owner_set (GtkWidget *widget,
418 g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
421 display = gtk_widget_get_display (widget);
425 g_warning ("gtk_selection_owner_set (NULL,...) is not multihead safe"));
427 display = gdk_display_get_default ();
430 return gtk_selection_owner_set_for_display (display, widget,
434 /*************************************************************
435 * gtk_selection_add_target
436 * Add specified target to list of supported targets
439 * widget: The widget for which this target applies
442 * info: guint to pass to to the selection_get signal
445 *************************************************************/
447 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
449 struct _GtkSelectionTargetList {
454 static GtkTargetList *
455 gtk_selection_target_list_get (GtkWidget *widget,
458 GtkSelectionTargetList *sellist;
462 lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
467 sellist = tmp_list->data;
468 if (sellist->selection == selection)
469 return sellist->list;
470 tmp_list = tmp_list->next;
473 sellist = g_new (GtkSelectionTargetList, 1);
474 sellist->selection = selection;
475 sellist->list = gtk_target_list_new (NULL, 0);
477 lists = g_list_prepend (lists, sellist);
478 g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
480 return sellist->list;
484 gtk_selection_target_list_remove (GtkWidget *widget)
486 GtkSelectionTargetList *sellist;
490 lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
495 sellist = tmp_list->data;
497 gtk_target_list_unref (sellist->list);
500 tmp_list = tmp_list->next;
504 g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, NULL);
508 * gtk_selection_clear_targets:
509 * @widget: a #GtkWidget
510 * @selection: an atom representing a selection
512 * Remove all targets registered for the given selection for the
516 gtk_selection_clear_targets (GtkWidget *widget,
519 GtkSelectionTargetList *sellist;
523 lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
528 sellist = tmp_list->data;
529 if (sellist->selection == selection)
531 lists = g_list_delete_link (lists, tmp_list);
532 gtk_target_list_unref (sellist->list);
538 tmp_list = tmp_list->next;
541 g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
545 gtk_selection_add_target (GtkWidget *widget,
552 g_return_if_fail (widget != NULL);
554 list = gtk_selection_target_list_get (widget, selection);
555 gtk_target_list_add (list, target, 0, info);
559 gtk_selection_add_targets (GtkWidget *widget,
561 const GtkTargetEntry *targets,
566 g_return_if_fail (widget != NULL);
567 g_return_if_fail (targets != NULL);
569 list = gtk_selection_target_list_get (widget, selection);
570 gtk_target_list_add_table (list, targets, ntargets);
574 /*************************************************************
575 * gtk_selection_remove_all:
576 * Removes all handlers and unsets ownership of all
577 * selections for a widget. Called when widget is being
583 *************************************************************/
586 gtk_selection_remove_all (GtkWidget *widget)
590 GtkSelectionInfo *selection_info;
592 /* Remove pending requests/incrs for this widget */
594 tmp_list = current_retrievals;
597 next = tmp_list->next;
598 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
600 current_retrievals = g_list_remove_link (current_retrievals,
602 /* structure will be freed in timeout */
603 g_list_free (tmp_list);
608 /* Disclaim ownership of any selections */
610 tmp_list = current_selections;
613 next = tmp_list->next;
614 selection_info = (GtkSelectionInfo *)tmp_list->data;
616 if (selection_info->widget == widget)
618 gdk_selection_owner_set_for_display (selection_info->display,
620 selection_info->selection,
621 GDK_CURRENT_TIME, FALSE);
622 current_selections = g_list_remove_link (current_selections,
624 g_list_free (tmp_list);
625 g_free (selection_info);
631 /* Remove all selection lists */
632 gtk_selection_target_list_remove (widget);
635 /*************************************************************
636 * gtk_selection_convert:
637 * Request the contents of a selection. When received,
638 * a "selection_received" signal will be generated.
641 * widget: The widget which acts as requestor
642 * selection: Which selection to get
643 * target: Form of information desired (e.g., STRING)
644 * time: Time of request (usually of triggering event)
645 * In emergency, you could use GDK_CURRENT_TIME
648 * TRUE if requested succeeded. FALSE if we could not process
649 * request. (e.g., there was already a request in process for
651 *************************************************************/
654 gtk_selection_convert (GtkWidget *widget,
659 GtkRetrievalInfo *info;
661 GdkWindow *owner_window;
664 g_return_val_if_fail (widget != NULL, FALSE);
667 gtk_selection_init ();
669 if (!GTK_WIDGET_REALIZED (widget))
670 gtk_widget_realize (widget);
672 /* Check to see if there are already any retrievals in progress for
673 this widget. If we changed GDK to use the selection for the
674 window property in which to store the retrieved information, then
675 we could support multiple retrievals for different selections.
676 This might be useful for DND. */
678 tmp_list = current_retrievals;
681 info = (GtkRetrievalInfo *)tmp_list->data;
682 if (info->widget == widget)
684 tmp_list = tmp_list->next;
687 info = g_new (GtkRetrievalInfo, 1);
689 info->widget = widget;
690 info->selection = selection;
691 info->target = target;
696 /* Check if this process has current owner. If so, call handler
697 procedure directly to avoid deadlocks with INCR. */
699 display = gtk_widget_get_display (widget);
700 owner_window = gdk_selection_owner_get_for_display (display, selection);
702 if (owner_window != NULL)
704 GtkWidget *owner_widget;
705 GtkSelectionData selection_data;
707 selection_data.selection = selection;
708 selection_data.target = target;
709 selection_data.data = NULL;
710 selection_data.length = -1;
711 selection_data.display = display;
713 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
715 if (owner_widget != NULL)
717 gtk_selection_invoke_handler (owner_widget,
721 gtk_selection_retrieval_report (info,
723 selection_data.format,
725 selection_data.length,
728 g_free (selection_data.data);
735 /* Otherwise, we need to go through X */
737 current_retrievals = g_list_append (current_retrievals, info);
738 gdk_selection_convert (widget->window, selection, target, time);
739 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
744 /*************************************************************
745 * gtk_selection_data_set:
746 * Store new data into a GtkSelectionData object. Should
747 * _only_ by called from a selection handler callback.
748 * Null terminates the stored data.
750 * type: the type of selection data
751 * format: format (number of bits in a unit)
752 * data: pointer to the data (will be copied)
753 * length: length of the data
755 *************************************************************/
758 gtk_selection_data_set (GtkSelectionData *selection_data,
764 if (selection_data->data)
765 g_free (selection_data->data);
767 selection_data->type = type;
768 selection_data->format = format;
772 selection_data->data = g_new (guchar, length+1);
773 memcpy (selection_data->data, data, length);
774 selection_data->data[length] = 0;
778 g_return_if_fail (length <= 0);
781 selection_data->data = NULL;
783 selection_data->data = g_strdup("");
786 selection_data->length = length;
789 static GdkAtom utf8_atom;
790 static GdkAtom text_atom;
791 static GdkAtom ctext_atom;
798 utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
799 text_atom = gdk_atom_intern ("TEXT", FALSE);
800 ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
805 * gtk_selection_data_set_text:
806 * @selection_data: a #GtkSelectionData
807 * @str: a UTF-8 string
808 * @len: the length of @str, or -1 if @str is nul-terminated.
810 * Sets the contents of the selection from a UTF-8 encoded string.
811 * The string is converted to the form determined by
812 * @selection_data->target.
814 * Return value: %TRUE if the selection was successfully set,
818 gtk_selection_data_set_text (GtkSelectionData *selection_data,
822 gboolean result = FALSE;
829 if (selection_data->target == utf8_atom)
831 gtk_selection_data_set (selection_data,
833 8, (guchar *)str, len);
836 else if (selection_data->target == GDK_TARGET_STRING)
838 gchar *tmp = g_strndup (str, len);
839 gchar *latin1 = gdk_utf8_to_string_target (tmp);
844 gtk_selection_data_set (selection_data,
845 GDK_SELECTION_TYPE_STRING,
846 8, latin1, strlen (latin1));
853 else if (selection_data->target == ctext_atom ||
854 selection_data->target == text_atom)
862 tmp = g_strndup (str, len);
863 if (gdk_utf8_to_compound_text_for_display (selection_data->display, tmp,
864 &encoding, &format, &text, &new_length))
866 gtk_selection_data_set (selection_data, encoding, format, text, new_length);
867 gdk_free_compound_text (text);
879 * gtk_selection_data_get_text:
880 * @selection_data: a #GtkSelectionData
882 * Gets the contents of the selection data as a UTF-8 string.
884 * Return value: if the selection data contained a recognized
885 * text type and it could be converted to UTF-8, a newly allocated
886 * string containing the converted text, otherwise %NULL.
887 * If the result is non-%NULL it must be freed with g_free().
890 gtk_selection_data_get_text (GtkSelectionData *selection_data)
892 guchar *result = NULL;
896 if (selection_data->length >= 0 &&
897 (selection_data->type == GDK_TARGET_STRING ||
898 selection_data->type == ctext_atom ||
899 selection_data->type == utf8_atom))
903 gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
904 selection_data->type,
905 selection_data->format,
906 selection_data->data,
907 selection_data->length,
912 for (i = 1; i < count; i++)
921 * gtk_selection_data_get_targets:
922 * @selection_data: a #GtkSelectionData object
923 * @targets: location to store an array of targets. The result
924 * stored here must be freed with g_free().
925 * @n_atoms: location to store number of items in @targets.
927 * Gets the contents of @selection_data as an array of targets.
928 * This can be used to interpret the results of getting
929 * the standard TARGETS target that is always supplied for
932 * Return value: %TRUE if @selection_data contains a valid
933 * array of targets, otherwise %FALSE.
936 gtk_selection_data_get_targets (GtkSelectionData *selection_data,
940 if (selection_data->length >= 0 &&
941 selection_data->format == 32 &&
942 selection_data->type == GDK_SELECTION_TYPE_ATOM)
945 *targets = g_memdup (selection_data->data, selection_data->length);
947 *n_atoms = selection_data->length / sizeof (GdkAtom);
963 * gtk_selection_data_targets_include_text:
964 * @selection_data: a #GtkSelectionData object
966 * Given a #GtkSelectionData object holding a list of targets,
967 * determines if any of the targets in @targets can be used to
970 * Return value: %TRUE if @selection_data holds a list of targets,
971 * and a suitable target for text is included, otherwise %FALSE.
974 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
979 gboolean result = FALSE;
981 if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
983 for (i=0; i < n_targets; i++)
985 if (targets[i] == gdk_atom_intern ("STRING", FALSE) ||
986 targets[i] == gdk_atom_intern ("TEXT", FALSE) ||
987 targets[i] == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
988 targets[i] == gdk_atom_intern ("UTF8_STRING", FALSE))
998 /*************************************************************
999 * gtk_selection_init:
1000 * Initialize local variables
1004 *************************************************************/
1007 gtk_selection_init (void)
1009 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
1010 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
1011 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
1012 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
1018 * gtk_selection_clear:
1019 * @widget: a #GtkWidget
1022 * The default handler for the GtkWidget::selection_clear_event
1023 * signal. Instead of calling this function, chain up from
1024 * your selection_clear_event handler. Calling this function
1025 * from any other context is illegal. This function will
1026 * be deprecated in future versions of GTK+.
1028 * Return value: %TRUE if the event was handled, otherwise false
1033 gtk_selection_clear (GtkWidget *widget,
1034 GdkEventSelection *event)
1036 /* Note that we filter clear events in gdkselection-x11.c, so
1037 * that we only will get here if the clear event actually
1038 * represents a change that we didn't do ourself.
1041 GtkSelectionInfo *selection_info = NULL;
1043 tmp_list = current_selections;
1046 selection_info = (GtkSelectionInfo *)tmp_list->data;
1048 if ((selection_info->selection == event->selection) &&
1049 (selection_info->widget == widget))
1052 tmp_list = tmp_list->next;
1057 current_selections = g_list_remove_link (current_selections, tmp_list);
1058 g_list_free (tmp_list);
1059 g_free (selection_info);
1066 /*************************************************************
1067 * _gtk_selection_request:
1068 * Handler for "selection_request_event"
1073 *************************************************************/
1076 _gtk_selection_request (GtkWidget *widget,
1077 GdkEventSelection *event)
1085 gtk_selection_init ();
1087 /* Check if we own selection */
1089 tmp_list = current_selections;
1092 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1094 if ((selection_info->selection == event->selection) &&
1095 (selection_info->widget == widget))
1098 tmp_list = tmp_list->next;
1101 if (tmp_list == NULL)
1104 info = g_new (GtkIncrInfo, 1);
1106 g_object_ref (widget);
1108 info->selection = event->selection;
1109 info->num_incrs = 0;
1111 /* Create GdkWindow structure for the requestor */
1113 info->requestor = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
1115 if (!info->requestor)
1116 info->requestor = gdk_window_foreign_new_for_display (gtk_widget_get_display (widget),
1119 /* Determine conversions we need to perform */
1121 if (event->target == gtk_selection_atoms[MULTIPLE])
1129 gdk_error_trap_push ();
1130 if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
1131 0, GTK_SELECTION_MAX_SIZE, FALSE,
1132 &type, &format, &length, &mult_atoms))
1134 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1140 g_free (mult_atoms);
1144 gdk_error_trap_pop ();
1146 info->num_conversions = length / (2*sizeof (GdkAtom));
1147 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1149 for (i=0; i<info->num_conversions; i++)
1151 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1152 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1155 else /* only a single conversion */
1157 info->conversions = g_new (GtkIncrConversion, 1);
1158 info->num_conversions = 1;
1159 info->conversions[0].target = event->target;
1160 info->conversions[0].property = event->property;
1161 mult_atoms = (guchar *)info->conversions;
1164 /* Loop through conversions and determine which of these are big
1165 enough to require doing them via INCR */
1166 for (i=0; i<info->num_conversions; i++)
1168 GtkSelectionData data;
1171 data.selection = event->selection;
1172 data.target = info->conversions[i].target;
1175 data.display = gtk_widget_get_display (widget);
1177 #ifdef DEBUG_SELECTION
1178 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1179 event->selection, info->conversions[i].target,
1180 gdk_atom_name (info->conversions[i].target),
1181 event->requestor, event->property);
1184 gtk_selection_invoke_handler (widget, &data, event->time);
1186 if (data.length < 0)
1188 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
1189 info->conversions[i].property = GDK_NONE;
1193 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1195 items = data.length / gtk_selection_bytes_per_item (data.format);
1197 if (data.length > GTK_SELECTION_MAX_SIZE)
1199 /* Sending via INCR */
1201 info->conversions[i].offset = 0;
1202 info->conversions[i].data = data;
1205 gdk_property_change (info->requestor,
1206 info->conversions[i].property,
1207 gtk_selection_atoms[INCR],
1209 GDK_PROP_MODE_REPLACE,
1210 (guchar *)&items, 1);
1214 info->conversions[i].offset = -1;
1216 gdk_property_change (info->requestor,
1217 info->conversions[i].property,
1220 GDK_PROP_MODE_REPLACE,
1227 /* If we have some INCR's, we need to send the rest of the data in
1230 if (info->num_incrs > 0)
1232 /* FIXME: this could be dangerous if window doesn't still
1235 #ifdef DEBUG_SELECTION
1236 g_message ("Starting INCR...");
1239 gdk_window_set_events (info->requestor,
1240 gdk_window_get_events (info->requestor) |
1241 GDK_PROPERTY_CHANGE_MASK);
1242 current_incrs = g_list_append (current_incrs, info);
1243 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
1246 /* If it was a MULTIPLE request, set the property to indicate which
1247 conversions succeeded */
1248 if (event->target == gtk_selection_atoms[MULTIPLE])
1250 gdk_property_change (info->requestor, event->property,
1251 gdk_atom_intern ("ATOM_PAIR", FALSE), 32,
1252 GDK_PROP_MODE_REPLACE,
1253 mult_atoms, 2*info->num_conversions);
1254 g_free (mult_atoms);
1257 if (info->num_conversions == 1 &&
1258 info->conversions[0].property == GDK_NONE)
1260 /* Reject the entire conversion */
1261 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1270 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1278 if (info->num_incrs == 0)
1280 g_free (info->conversions);
1284 g_object_unref (widget);
1289 /*************************************************************
1290 * _gtk_selection_incr_event:
1291 * Called whenever an PropertyNotify event occurs for an
1292 * GdkWindow with user_data == NULL. These will be notifications
1293 * that a window we are sending the selection to via the
1294 * INCR protocol has deleted a property and is ready for
1298 * window: the requestor window
1299 * event: the property event structure
1302 *************************************************************/
1305 _gtk_selection_incr_event (GdkWindow *window,
1306 GdkEventProperty *event)
1309 GtkIncrInfo *info = NULL;
1315 if (event->state != GDK_PROPERTY_DELETE)
1318 #ifdef DEBUG_SELECTION
1319 g_message ("PropertyDelete, property %ld", event->atom);
1322 /* Now find the appropriate ongoing INCR */
1323 tmp_list = current_incrs;
1326 info = (GtkIncrInfo *)tmp_list->data;
1327 if (info->requestor == event->window)
1330 tmp_list = tmp_list->next;
1333 if (tmp_list == NULL)
1336 /* Find out which target this is for */
1337 for (i=0; i<info->num_conversions; i++)
1339 if (info->conversions[i].property == event->atom &&
1340 info->conversions[i].offset != -1)
1344 info->idle_time = 0;
1346 if (info->conversions[i].offset == -2) /* only the last 0-length
1354 num_bytes = info->conversions[i].data.length -
1355 info->conversions[i].offset;
1356 buffer = info->conversions[i].data.data +
1357 info->conversions[i].offset;
1359 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1361 num_bytes = GTK_SELECTION_MAX_SIZE;
1362 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1365 info->conversions[i].offset = -2;
1367 #ifdef DEBUG_SELECTION
1368 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1369 num_bytes, info->conversions[i].offset,
1370 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1373 bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1374 gdk_property_change (info->requestor, event->atom,
1375 info->conversions[i].data.type,
1376 info->conversions[i].data.format,
1377 GDK_PROP_MODE_REPLACE,
1379 num_bytes / bytes_per_item);
1381 if (info->conversions[i].offset == -2)
1383 g_free (info->conversions[i].data.data);
1384 info->conversions[i].data.data = NULL;
1390 info->conversions[i].offset = -1;
1396 /* Check if we're finished with all the targets */
1398 if (info->num_incrs == 0)
1400 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1401 g_list_free (tmp_list);
1402 /* Let the timeout free it */
1408 /*************************************************************
1409 * gtk_selection_incr_timeout:
1410 * Timeout callback for the sending portion of the INCR
1413 * info: Information about this incr
1415 *************************************************************/
1418 gtk_selection_incr_timeout (GtkIncrInfo *info)
1423 GDK_THREADS_ENTER ();
1425 /* Determine if retrieval has finished by checking if it still in
1426 list of pending retrievals */
1428 tmp_list = current_incrs;
1431 if (info == (GtkIncrInfo *)tmp_list->data)
1433 tmp_list = tmp_list->next;
1436 /* If retrieval is finished */
1437 if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1439 if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1441 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1442 g_list_free (tmp_list);
1445 g_free (info->conversions);
1446 /* FIXME: we should check if requestor window is still in use,
1447 and if not, remove it? */
1451 retval = FALSE; /* remove timeout */
1457 retval = TRUE; /* timeout will happen again */
1460 GDK_THREADS_LEAVE ();
1465 /*************************************************************
1466 * _gtk_selection_notify:
1467 * Handler for "selection_notify_event" signals on windows
1468 * where a retrieval is currently in process. The selection
1469 * owner has responded to our conversion request.
1471 * widget: Widget getting signal
1472 * event: Selection event structure
1473 * info: Information about this retrieval
1475 * was event handled?
1476 *************************************************************/
1479 _gtk_selection_notify (GtkWidget *widget,
1480 GdkEventSelection *event)
1483 GtkRetrievalInfo *info = NULL;
1484 guchar *buffer = NULL;
1489 #ifdef DEBUG_SELECTION
1490 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1491 event->selection, event->target, event->property);
1494 tmp_list = current_retrievals;
1497 info = (GtkRetrievalInfo *)tmp_list->data;
1498 if (info->widget == widget && info->selection == event->selection)
1500 tmp_list = tmp_list->next;
1503 if (!tmp_list) /* no retrieval in progress */
1506 if (event->property != GDK_NONE)
1507 length = gdk_selection_property_get (widget->window, &buffer,
1510 length = 0; /* silence gcc */
1512 if (event->property == GDK_NONE || buffer == NULL)
1514 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1515 g_list_free (tmp_list);
1516 /* structure will be freed in timeout */
1517 gtk_selection_retrieval_report (info,
1518 GDK_NONE, 0, NULL, -1, event->time);
1523 if (type == gtk_selection_atoms[INCR])
1525 /* The remainder of the selection will come through PropertyNotify
1528 info->notify_time = event->time;
1529 info->idle_time = 0;
1530 info->offset = 0; /* Mark as OK to proceed */
1531 gdk_window_set_events (widget->window,
1532 gdk_window_get_events (widget->window)
1533 | GDK_PROPERTY_CHANGE_MASK);
1537 /* We don't delete the info structure - that will happen in timeout */
1538 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1539 g_list_free (tmp_list);
1541 info->offset = length;
1542 gtk_selection_retrieval_report (info,
1544 buffer, length, event->time);
1547 gdk_property_delete (widget->window, event->property);
1554 /*************************************************************
1555 * _gtk_selection_property_notify:
1556 * Handler for "property_notify_event" signals on windows
1557 * where a retrieval is currently in process. The selection
1558 * owner has added more data.
1560 * widget: Widget getting signal
1561 * event: Property event structure
1562 * info: Information about this retrieval
1564 * was event handled?
1565 *************************************************************/
1568 _gtk_selection_property_notify (GtkWidget *widget,
1569 GdkEventProperty *event)
1572 GtkRetrievalInfo *info = NULL;
1578 g_return_val_if_fail (widget != NULL, FALSE);
1579 g_return_val_if_fail (event != NULL, FALSE);
1581 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1582 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1583 (event->atom != gdk_atom_intern ("GDK_SELECTION", FALSE))) /* not the right property */
1587 #ifdef DEBUG_SELECTION
1588 g_message ("PropertyNewValue, property %ld",
1592 tmp_list = current_retrievals;
1595 info = (GtkRetrievalInfo *)tmp_list->data;
1596 if (info->widget == widget)
1598 tmp_list = tmp_list->next;
1601 if (!tmp_list) /* No retrieval in progress */
1604 if (info->offset < 0) /* We haven't got the SelectionNotify
1605 for this retrieval yet */
1608 info->idle_time = 0;
1610 length = gdk_selection_property_get (widget->window, &new_buffer,
1612 gdk_property_delete (widget->window, event->atom);
1614 /* We could do a lot better efficiency-wise by paying attention to
1615 what length was sent in the initial INCR transaction, instead of
1616 doing memory allocation at every step. But its only guaranteed to
1617 be a _lower bound_ (pretty useless!) */
1619 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1621 /* Info structure will be freed in timeout */
1622 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1623 g_list_free (tmp_list);
1624 gtk_selection_retrieval_report (info,
1626 (type == GDK_NONE) ? NULL : info->buffer,
1627 (type == GDK_NONE) ? -1 : info->offset,
1630 else /* append on newly arrived data */
1634 #ifdef DEBUG_SELECTION
1635 g_message ("Start - Adding %d bytes at offset 0",
1638 info->buffer = new_buffer;
1639 info->offset = length;
1644 #ifdef DEBUG_SELECTION
1645 g_message ("Appending %d bytes at offset %d",
1646 length,info->offset);
1648 /* We copy length+1 bytes to preserve guaranteed null termination */
1649 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1650 memcpy (info->buffer + info->offset, new_buffer, length+1);
1651 info->offset += length;
1652 g_free (new_buffer);
1659 /*************************************************************
1660 * gtk_selection_retrieval_timeout:
1661 * Timeout callback while receiving a selection.
1663 * info: Information about this retrieval
1665 *************************************************************/
1668 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1673 GDK_THREADS_ENTER ();
1675 /* Determine if retrieval has finished by checking if it still in
1676 list of pending retrievals */
1678 tmp_list = current_retrievals;
1681 if (info == (GtkRetrievalInfo *)tmp_list->data)
1683 tmp_list = tmp_list->next;
1686 /* If retrieval is finished */
1687 if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1689 if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1691 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1692 g_list_free (tmp_list);
1693 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1696 g_free (info->buffer);
1699 retval = FALSE; /* remove timeout */
1705 retval = TRUE; /* timeout will happen again */
1708 GDK_THREADS_LEAVE ();
1713 /*************************************************************
1714 * gtk_selection_retrieval_report:
1715 * Emits a "selection_received" signal.
1717 * info: information about the retrieval that completed
1718 * buffer: buffer containing data (NULL => errror)
1719 * time: timestamp for data in buffer
1721 *************************************************************/
1724 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1725 GdkAtom type, gint format,
1726 guchar *buffer, gint length,
1729 GtkSelectionData data;
1731 data.selection = info->selection;
1732 data.target = info->target;
1734 data.format = format;
1736 data.length = length;
1738 data.display = gtk_widget_get_display (info->widget);
1740 g_signal_emit_by_name (info->widget,
1741 "selection_received",
1745 /*************************************************************
1746 * gtk_selection_invoke_handler:
1747 * Finds and invokes handler for specified
1748 * widget/selection/target combination, calls
1749 * gtk_selection_default_handler if none exists.
1752 * widget: selection owner
1753 * data: selection data [INOUT]
1754 * time: time from requeset
1757 * Number of bytes written to buffer, -1 if error
1758 *************************************************************/
1761 gtk_selection_invoke_handler (GtkWidget *widget,
1762 GtkSelectionData *data,
1765 GtkTargetList *target_list;
1769 g_return_if_fail (widget != NULL);
1771 target_list = gtk_selection_target_list_get (widget, data->selection);
1773 gtk_target_list_find (target_list, data->target, &info))
1775 g_signal_emit_by_name (widget,
1781 gtk_selection_default_handler (widget, data);
1784 /*************************************************************
1785 * gtk_selection_default_handler:
1786 * Handles some default targets that exist for any widget
1787 * If it can't fit results into buffer, returns -1. This
1788 * won't happen in any conceivable case, since it would
1789 * require 1000 selection targets!
1792 * widget: selection owner
1793 * data: selection data [INOUT]
1795 *************************************************************/
1798 gtk_selection_default_handler (GtkWidget *widget,
1799 GtkSelectionData *data)
1801 if (data->target == gtk_selection_atoms[TIMESTAMP])
1803 /* Time which was used to obtain selection */
1805 GtkSelectionInfo *selection_info;
1807 tmp_list = current_selections;
1810 selection_info = (GtkSelectionInfo *)tmp_list->data;
1811 if ((selection_info->widget == widget) &&
1812 (selection_info->selection == data->selection))
1814 gulong time = selection_info->time;
1816 gtk_selection_data_set (data,
1817 GDK_SELECTION_TYPE_INTEGER,
1824 tmp_list = tmp_list->next;
1829 else if (data->target == gtk_selection_atoms[TARGETS])
1831 /* List of all targets supported for this widget/selection pair */
1835 GtkTargetList *target_list;
1836 GtkTargetPair *pair;
1838 target_list = gtk_selection_target_list_get (widget,
1840 count = g_list_length (target_list->list) + 3;
1842 data->type = GDK_SELECTION_TYPE_ATOM;
1844 data->length = count * sizeof (GdkAtom);
1846 p = g_new (GdkAtom, count);
1847 data->data = (guchar *)p;
1849 *p++ = gtk_selection_atoms[TIMESTAMP];
1850 *p++ = gtk_selection_atoms[TARGETS];
1851 *p++ = gtk_selection_atoms[MULTIPLE];
1853 tmp_list = target_list->list;
1856 pair = (GtkTargetPair *)tmp_list->data;
1857 *p++ = pair->target;
1859 tmp_list = tmp_list->next;
1870 gtk_selection_data_copy (GtkSelectionData *selection_data)
1872 GtkSelectionData *new_data;
1874 g_return_val_if_fail (selection_data != NULL, NULL);
1876 new_data = g_new (GtkSelectionData, 1);
1877 *new_data = *selection_data;
1879 if (selection_data->data)
1881 new_data->data = g_malloc (selection_data->length + 1);
1882 memcpy (new_data->data, selection_data->data, selection_data->length + 1);
1889 gtk_selection_data_free (GtkSelectionData *data)
1891 g_return_if_fail (data != NULL);
1894 g_free (data->data);
1900 gtk_selection_data_get_type (void)
1902 static GType our_type = 0;
1905 our_type = g_boxed_type_register_static ("GtkSelectionData",
1906 (GBoxedCopyFunc) gtk_selection_data_copy,
1907 (GBoxedFreeFunc) gtk_selection_data_free);
1913 gtk_selection_bytes_per_item (gint format)
1918 return sizeof (char);
1921 return sizeof (short);
1924 return sizeof (long);
1927 g_assert_not_reached();