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 */
92 struct _GtkIncrConversion
94 GdkAtom target; /* Requested target */
95 GdkAtom property; /* Property to store in */
96 GtkSelectionData data; /* The data being supplied */
97 gint offset; /* Current offset in sent selection.
99 * -2 => Only the final (empty) portion
105 GdkWindow *requestor; /* Requestor window - we create a GdkWindow
106 so we can receive events */
107 GdkAtom selection; /* Selection we're sending */
109 GtkIncrConversion *conversions; /* Information about requested conversions -
110 * With MULTIPLE requests (benighted 1980's
111 * hardware idea), there can be more than
113 gint num_conversions;
114 gint num_incrs; /* number of remaining INCR style transactions */
119 struct _GtkRetrievalInfo
122 GdkAtom selection; /* Selection being retrieved. */
123 GdkAtom target; /* Form of selection that we requested */
124 guint32 idle_time; /* Number of seconds since we last heard
125 from selection owner */
126 guchar *buffer; /* Buffer in which to accumulate results */
127 gint offset; /* Current offset in buffer, -1 indicates
129 guint32 notify_time; /* Timestamp from SelectionNotify */
132 /* Local Functions */
133 static void gtk_selection_init (void);
134 static gint gtk_selection_incr_timeout (GtkIncrInfo *info);
135 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
136 static void gtk_selection_retrieval_report (GtkRetrievalInfo *info,
142 static void gtk_selection_invoke_handler (GtkWidget *widget,
143 GtkSelectionData *data,
145 static void gtk_selection_default_handler (GtkWidget *widget,
146 GtkSelectionData *data);
147 static int gtk_selection_bytes_per_item (gint format);
150 static gint initialize = TRUE;
151 static GList *current_retrievals = NULL;
152 static GList *current_incrs = NULL;
153 static GList *current_selections = NULL;
155 static GdkAtom gtk_selection_atoms[LAST_ATOM];
156 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
167 gtk_target_list_new (const GtkTargetEntry *targets,
170 GtkTargetList *result = g_new (GtkTargetList, 1);
172 result->ref_count = 1;
175 gtk_target_list_add_table (result, targets, ntargets);
181 gtk_target_list_ref (GtkTargetList *list)
183 g_return_if_fail (list != NULL);
189 gtk_target_list_unref (GtkTargetList *list)
191 g_return_if_fail (list != NULL);
192 g_return_if_fail (list->ref_count > 0);
195 if (list->ref_count == 0)
197 GList *tmp_list = list->list;
200 GtkTargetPair *pair = tmp_list->data;
203 tmp_list = tmp_list->next;
206 g_list_free (list->list);
212 gtk_target_list_add (GtkTargetList *list,
219 g_return_if_fail (list != NULL);
221 pair = g_new (GtkTargetPair, 1);
222 pair->target = target;
226 list->list = g_list_append (list->list, pair);
230 gtk_target_list_add_table (GtkTargetList *list,
231 const GtkTargetEntry *targets,
236 for (i=ntargets-1; i >= 0; i--)
238 GtkTargetPair *pair = g_new (GtkTargetPair, 1);
239 pair->target = gdk_atom_intern (targets[i].target, FALSE);
240 pair->flags = targets[i].flags;
241 pair->info = targets[i].info;
243 list->list = g_list_prepend (list->list, pair);
248 gtk_target_list_remove (GtkTargetList *list,
253 g_return_if_fail (list != NULL);
255 tmp_list = list->list;
258 GtkTargetPair *pair = tmp_list->data;
260 if (pair->target == target)
264 list->list = g_list_remove_link (list->list, tmp_list);
265 g_list_free_1 (tmp_list);
270 tmp_list = tmp_list->next;
275 gtk_target_list_find (GtkTargetList *list,
279 GList *tmp_list = list->list;
282 GtkTargetPair *pair = tmp_list->data;
284 if (pair->target == target)
289 tmp_list = tmp_list->next;
296 /*************************************************************
297 * gtk_selection_owner_set:
298 * Claim ownership of a selection.
300 * widget: new selection owner
301 * selection: which selection
302 * time: time (use GDK_CURRENT_TIME only if necessary)
305 *************************************************************/
308 gtk_selection_owner_set (GtkWidget *widget,
313 GtkWidget *old_owner;
314 GtkSelectionInfo *selection_info = NULL;
317 g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
322 window = widget->window;
324 tmp_list = current_selections;
327 if (((GtkSelectionInfo *)tmp_list->data)->selection == selection)
329 selection_info = tmp_list->data;
333 tmp_list = tmp_list->next;
336 if (gdk_selection_owner_set (window, selection, time, TRUE))
344 old_owner = selection_info->widget;
345 current_selections = g_list_remove_link (current_selections,
347 g_list_free (tmp_list);
348 g_free (selection_info);
353 if (selection_info == NULL)
355 selection_info = g_new (GtkSelectionInfo, 1);
356 selection_info->selection = selection;
357 selection_info->widget = widget;
358 selection_info->time = time;
359 current_selections = g_list_prepend (current_selections,
364 old_owner = selection_info->widget;
365 selection_info->widget = widget;
366 selection_info->time = time;
369 /* If another widget in the application lost the selection,
370 * send it a GDK_SELECTION_CLEAR event.
372 if (old_owner && old_owner != widget)
374 GdkEventSelection event;
376 event.type = GDK_SELECTION_CLEAR;
377 event.window = old_owner->window;
378 event.selection = selection;
381 gtk_widget_event (old_owner, (GdkEvent *) &event);
389 /*************************************************************
390 * gtk_selection_add_target
391 * Add specified target to list of supported targets
394 * widget: The widget for which this target applies
397 * info: guint to pass to to the selection_get signal
400 *************************************************************/
402 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
404 struct _GtkSelectionTargetList {
409 static GtkTargetList *
410 gtk_selection_target_list_get (GtkWidget *widget,
413 GtkSelectionTargetList *sellist;
417 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
422 sellist = tmp_list->data;
423 if (sellist->selection == selection)
424 return sellist->list;
425 tmp_list = tmp_list->next;
428 sellist = g_new (GtkSelectionTargetList, 1);
429 sellist->selection = selection;
430 sellist->list = gtk_target_list_new (NULL, 0);
432 lists = g_list_prepend (lists, sellist);
433 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
435 return sellist->list;
439 gtk_selection_target_list_remove (GtkWidget *widget)
441 GtkSelectionTargetList *sellist;
445 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
450 sellist = tmp_list->data;
452 gtk_target_list_unref (sellist->list);
455 tmp_list = tmp_list->next;
459 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL);
463 * gtk_selection_clear_targets:
464 * @widget: a #GtkWidget
465 * @selection: an atom representing a selection
467 * Remove all targets registered for the given selection for the
471 gtk_selection_clear_targets (GtkWidget *widget,
474 GtkSelectionTargetList *sellist;
478 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
483 sellist = tmp_list->data;
484 if (sellist->selection == selection)
486 lists = g_list_delete_link (lists, tmp_list);
487 gtk_target_list_unref (sellist->list);
493 tmp_list = tmp_list->next;
496 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
500 gtk_selection_add_target (GtkWidget *widget,
507 g_return_if_fail (widget != NULL);
509 list = gtk_selection_target_list_get (widget, selection);
510 gtk_target_list_add (list, target, 0, info);
514 gtk_selection_add_targets (GtkWidget *widget,
516 const GtkTargetEntry *targets,
521 g_return_if_fail (widget != NULL);
522 g_return_if_fail (targets != NULL);
524 list = gtk_selection_target_list_get (widget, selection);
525 gtk_target_list_add_table (list, targets, ntargets);
529 /*************************************************************
530 * gtk_selection_remove_all:
531 * Removes all handlers and unsets ownership of all
532 * selections for a widget. Called when widget is being
538 *************************************************************/
541 gtk_selection_remove_all (GtkWidget *widget)
545 GtkSelectionInfo *selection_info;
547 /* Remove pending requests/incrs for this widget */
549 tmp_list = current_retrievals;
552 next = tmp_list->next;
553 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
555 current_retrievals = g_list_remove_link (current_retrievals,
557 /* structure will be freed in timeout */
558 g_list_free (tmp_list);
563 /* Disclaim ownership of any selections */
565 tmp_list = current_selections;
568 next = tmp_list->next;
569 selection_info = (GtkSelectionInfo *)tmp_list->data;
571 if (selection_info->widget == widget)
573 gdk_selection_owner_set (NULL,
574 selection_info->selection,
575 GDK_CURRENT_TIME, FALSE);
576 current_selections = g_list_remove_link (current_selections,
578 g_list_free (tmp_list);
579 g_free (selection_info);
585 /* Remove all selection lists */
586 gtk_selection_target_list_remove (widget);
589 /*************************************************************
590 * gtk_selection_convert:
591 * Request the contents of a selection. When received,
592 * a "selection_received" signal will be generated.
595 * widget: The widget which acts as requestor
596 * selection: Which selection to get
597 * target: Form of information desired (e.g., STRING)
598 * time: Time of request (usually of triggering event)
599 * In emergency, you could use GDK_CURRENT_TIME
602 * TRUE if requested succeeded. FALSE if we could not process
603 * request. (e.g., there was already a request in process for
605 *************************************************************/
608 gtk_selection_convert (GtkWidget *widget,
613 GtkRetrievalInfo *info;
615 GdkWindow *owner_window;
617 g_return_val_if_fail (widget != NULL, FALSE);
620 gtk_selection_init ();
622 if (!GTK_WIDGET_REALIZED (widget))
623 gtk_widget_realize (widget);
625 /* Check to see if there are already any retrievals in progress for
626 this widget. If we changed GDK to use the selection for the
627 window property in which to store the retrieved information, then
628 we could support multiple retrievals for different selections.
629 This might be useful for DND. */
631 tmp_list = current_retrievals;
634 info = (GtkRetrievalInfo *)tmp_list->data;
635 if (info->widget == widget)
637 tmp_list = tmp_list->next;
640 info = g_new (GtkRetrievalInfo, 1);
642 info->widget = widget;
643 info->selection = selection;
644 info->target = target;
648 /* Check if this process has current owner. If so, call handler
649 procedure directly to avoid deadlocks with INCR. */
651 owner_window = gdk_selection_owner_get (selection);
653 if (owner_window != NULL)
655 GtkWidget *owner_widget;
656 GtkSelectionData selection_data;
658 selection_data.selection = selection;
659 selection_data.target = target;
660 selection_data.data = NULL;
661 selection_data.length = -1;
663 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
665 if (owner_widget != NULL)
667 gtk_selection_invoke_handler (owner_widget,
671 gtk_selection_retrieval_report (info,
673 selection_data.format,
675 selection_data.length,
678 g_free (selection_data.data);
685 /* Otherwise, we need to go through X */
687 current_retrievals = g_list_append (current_retrievals, info);
688 gdk_selection_convert (widget->window, selection, target, time);
689 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
694 /*************************************************************
695 * gtk_selection_data_set:
696 * Store new data into a GtkSelectionData object. Should
697 * _only_ by called from a selection handler callback.
698 * Null terminates the stored data.
700 * type: the type of selection data
701 * format: format (number of bits in a unit)
702 * data: pointer to the data (will be copied)
703 * length: length of the data
705 *************************************************************/
708 gtk_selection_data_set (GtkSelectionData *selection_data,
714 if (selection_data->data)
715 g_free (selection_data->data);
717 selection_data->type = type;
718 selection_data->format = format;
722 selection_data->data = g_new (guchar, length+1);
723 memcpy (selection_data->data, data, length);
724 selection_data->data[length] = 0;
728 g_return_if_fail (length <= 0);
731 selection_data->data = NULL;
733 selection_data->data = g_strdup("");
736 selection_data->length = length;
739 static GdkAtom utf8_atom;
740 static GdkAtom text_atom;
741 static GdkAtom ctext_atom;
748 utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
749 text_atom = gdk_atom_intern ("TEXT", FALSE);
750 ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
755 * gtk_selection_data_set_text:
756 * @selection_data: a #GtkSelectionData
757 * @str: a UTF-8 string
758 * @len: the length of @str, or -1 if @str is nul-terminated.
760 * Sets the contents of the selection from a UTF-8 encoded string.
761 * The string is converted to the form determined by
762 * @selection_data->target.
764 * Return value: %TRUE if the selection was successfully set,
768 gtk_selection_data_set_text (GtkSelectionData *selection_data,
772 gboolean result = FALSE;
779 if (selection_data->target == utf8_atom)
781 gtk_selection_data_set (selection_data,
783 8, (guchar *)str, len);
786 else if (selection_data->target == GDK_TARGET_STRING)
788 gchar *tmp = g_strndup (str, len);
789 gchar *latin1 = gdk_utf8_to_string_target (tmp);
794 gtk_selection_data_set (selection_data,
795 GDK_SELECTION_TYPE_STRING,
796 8, latin1, strlen (latin1));
803 else if (selection_data->target == ctext_atom ||
804 selection_data->target == text_atom)
812 tmp = g_strndup (str, len);
813 if (gdk_utf8_to_compound_text (tmp, &encoding, &format, &text, &new_length))
815 gtk_selection_data_set (selection_data, encoding, format, text, new_length);
816 gdk_free_compound_text (text);
828 * gtk_selection_data_get_text:
829 * @selection_data: a #GtkSelectionData
831 * Gets the contents of the selection data as a UTF-8 string.
833 * Return value: if the selection data contained a recognized
834 * text type and it could be converted to UTF-8, a newly allocated
835 * string containing the converted text, otherwise %NULL.
836 * If the result is non-%NULL it must be freed with g_free().
839 gtk_selection_data_get_text (GtkSelectionData *selection_data)
841 guchar *result = NULL;
845 if (selection_data->length >= 0 &&
846 (selection_data->type == GDK_TARGET_STRING ||
847 selection_data->type == ctext_atom ||
848 selection_data->type == utf8_atom))
852 gint count = gdk_text_property_to_utf8_list (selection_data->type,
853 selection_data->format,
854 selection_data->data,
855 selection_data->length,
860 for (i = 1; i < count; i++)
869 * gtk_selection_data_get_targets:
870 * @selection_data: a #GtkSelectionData object
871 * @targets: location to store an array of targets. The result
872 * stored here must be freed with g_free().
873 * @n_atoms: location to store number of items in @targets.
875 * Gets the contents of @selection_data as an array of targets.
876 * This can be used to interpret the results of getting
877 * the standard TARGETS target that is always supplied for
880 * Return value: %TRUE if @selection_data contains a valid
881 * array of targets, otherwise %FALSE.
884 gtk_selection_data_get_targets (GtkSelectionData *selection_data,
888 if (selection_data->length >= 0 &&
889 selection_data->format == 32 &&
890 selection_data->type == GDK_SELECTION_TYPE_ATOM)
893 *targets = g_memdup (selection_data->data, selection_data->length);
895 *n_atoms = selection_data->length / sizeof (GdkAtom);
911 * gtk_selection_data_targets_include_text:
912 * @selection_data: a #GtkSelectionData object
914 * Given a #GtkSelectionData object holding a list of targets,
915 * determines if any of the targets in @targets can be used to
918 * Return value: %TRUE if @selection_data holds a list of targets,
919 * and a suitable target for text is included, otherwise %FALSE.
922 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
927 gboolean result = FALSE;
929 if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
931 for (i=0; i < n_targets; i++)
933 if (targets[i] == gdk_atom_intern ("STRING", FALSE) ||
934 targets[i] == gdk_atom_intern ("TEXT", FALSE) ||
935 targets[i] == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
936 targets[i] == gdk_atom_intern ("UTF8_STRING", FALSE))
946 /*************************************************************
947 * gtk_selection_init:
948 * Initialize local variables
952 *************************************************************/
955 gtk_selection_init (void)
957 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
958 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
959 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
960 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
963 /*************************************************************
964 * gtk_selection_clear:
965 * Handler for "selection_clear_event"
970 *************************************************************/
973 gtk_selection_clear (GtkWidget *widget,
974 GdkEventSelection *event)
976 /* Note that we filter clear events in gdkselection-x11.c, so
977 * that we only will get here if the clear event actually
978 * represents a change that we didn't do ourself.
981 GtkSelectionInfo *selection_info = NULL;
983 tmp_list = current_selections;
986 selection_info = (GtkSelectionInfo *)tmp_list->data;
988 if ((selection_info->selection == event->selection) &&
989 (selection_info->widget == widget))
992 tmp_list = tmp_list->next;
997 current_selections = g_list_remove_link (current_selections, tmp_list);
998 g_list_free (tmp_list);
999 g_free (selection_info);
1006 /*************************************************************
1007 * gtk_selection_request:
1008 * Handler for "selection_request_event"
1013 *************************************************************/
1016 gtk_selection_request (GtkWidget *widget,
1017 GdkEventSelection *event)
1025 gtk_selection_init ();
1027 /* Check if we own selection */
1029 tmp_list = current_selections;
1032 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1034 if ((selection_info->selection == event->selection) &&
1035 (selection_info->widget == widget))
1038 tmp_list = tmp_list->next;
1041 if (tmp_list == NULL)
1044 info = g_new (GtkIncrInfo, 1);
1046 g_object_ref (widget);
1048 info->selection = event->selection;
1049 info->num_incrs = 0;
1051 /* Create GdkWindow structure for the requestor */
1052 info->requestor = gdk_window_lookup (event->requestor);
1053 if (!info->requestor)
1054 info->requestor = gdk_window_foreign_new (event->requestor);
1056 /* Determine conversions we need to perform */
1058 if (event->target == gtk_selection_atoms[MULTIPLE])
1066 gdk_error_trap_push ();
1067 if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
1068 0, GTK_SELECTION_MAX_SIZE, FALSE,
1069 &type, &format, &length, &mult_atoms))
1071 gdk_selection_send_notify (event->requestor, event->selection,
1072 event->target, GDK_NONE, event->time);
1073 g_free (mult_atoms);
1077 gdk_error_trap_pop ();
1079 info->num_conversions = length / (2*sizeof (GdkAtom));
1080 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1082 for (i=0; i<info->num_conversions; i++)
1084 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1085 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1088 else /* only a single conversion */
1090 info->conversions = g_new (GtkIncrConversion, 1);
1091 info->num_conversions = 1;
1092 info->conversions[0].target = event->target;
1093 info->conversions[0].property = event->property;
1094 mult_atoms = (guchar *)info->conversions;
1097 /* Loop through conversions and determine which of these are big
1098 enough to require doing them via INCR */
1099 for (i=0; i<info->num_conversions; i++)
1101 GtkSelectionData data;
1104 data.selection = event->selection;
1105 data.target = info->conversions[i].target;
1109 #ifdef DEBUG_SELECTION
1110 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1111 event->selection, info->conversions[i].target,
1112 gdk_atom_name (info->conversions[i].target),
1113 event->requestor, event->property);
1116 gtk_selection_invoke_handler (widget, &data, event->time);
1118 if (data.length < 0)
1120 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
1121 info->conversions[i].property = GDK_NONE;
1125 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1127 items = data.length / gtk_selection_bytes_per_item (data.format);
1129 if (data.length > GTK_SELECTION_MAX_SIZE)
1131 /* Sending via INCR */
1133 info->conversions[i].offset = 0;
1134 info->conversions[i].data = data;
1137 gdk_property_change (info->requestor,
1138 info->conversions[i].property,
1139 gtk_selection_atoms[INCR],
1141 GDK_PROP_MODE_REPLACE,
1142 (guchar *)&items, 1);
1146 info->conversions[i].offset = -1;
1148 gdk_property_change (info->requestor,
1149 info->conversions[i].property,
1152 GDK_PROP_MODE_REPLACE,
1159 /* If we have some INCR's, we need to send the rest of the data in
1162 if (info->num_incrs > 0)
1164 /* FIXME: this could be dangerous if window doesn't still
1167 #ifdef DEBUG_SELECTION
1168 g_message ("Starting INCR...");
1171 gdk_window_set_events (info->requestor,
1172 gdk_window_get_events (info->requestor) |
1173 GDK_PROPERTY_CHANGE_MASK);
1174 current_incrs = g_list_append (current_incrs, info);
1175 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
1178 /* If it was a MULTIPLE request, set the property to indicate which
1179 conversions succeeded */
1180 if (event->target == gtk_selection_atoms[MULTIPLE])
1182 gdk_property_change (info->requestor, event->property,
1183 gdk_atom_intern ("ATOM_PAIR", FALSE), 32,
1184 GDK_PROP_MODE_REPLACE,
1185 mult_atoms, 2*info->num_conversions);
1186 g_free (mult_atoms);
1189 if (info->num_conversions == 1 &&
1190 info->conversions[0].property == GDK_NONE)
1192 /* Reject the entire conversion */
1193 gdk_selection_send_notify (event->requestor, event->selection,
1194 event->target, GDK_NONE, event->time);
1198 gdk_selection_send_notify (event->requestor, event->selection,
1199 event->target, event->property, event->time);
1202 if (info->num_incrs == 0)
1204 g_free (info->conversions);
1208 g_object_unref (widget);
1213 /*************************************************************
1214 * gtk_selection_incr_event:
1215 * Called whenever an PropertyNotify event occurs for an
1216 * GdkWindow with user_data == NULL. These will be notifications
1217 * that a window we are sending the selection to via the
1218 * INCR protocol has deleted a property and is ready for
1222 * window: the requestor window
1223 * event: the property event structure
1226 *************************************************************/
1229 gtk_selection_incr_event (GdkWindow *window,
1230 GdkEventProperty *event)
1233 GtkIncrInfo *info = NULL;
1239 if (event->state != GDK_PROPERTY_DELETE)
1242 #ifdef DEBUG_SELECTION
1243 g_message ("PropertyDelete, property %ld", event->atom);
1246 /* Now find the appropriate ongoing INCR */
1247 tmp_list = current_incrs;
1250 info = (GtkIncrInfo *)tmp_list->data;
1251 if (info->requestor == event->window)
1254 tmp_list = tmp_list->next;
1257 if (tmp_list == NULL)
1260 /* Find out which target this is for */
1261 for (i=0; i<info->num_conversions; i++)
1263 if (info->conversions[i].property == event->atom &&
1264 info->conversions[i].offset != -1)
1268 info->idle_time = 0;
1270 if (info->conversions[i].offset == -2) /* only the last 0-length
1278 num_bytes = info->conversions[i].data.length -
1279 info->conversions[i].offset;
1280 buffer = info->conversions[i].data.data +
1281 info->conversions[i].offset;
1283 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1285 num_bytes = GTK_SELECTION_MAX_SIZE;
1286 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1289 info->conversions[i].offset = -2;
1291 #ifdef DEBUG_SELECTION
1292 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1293 num_bytes, info->conversions[i].offset,
1294 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1297 bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1298 gdk_property_change (info->requestor, event->atom,
1299 info->conversions[i].data.type,
1300 info->conversions[i].data.format,
1301 GDK_PROP_MODE_REPLACE,
1303 num_bytes / bytes_per_item);
1305 if (info->conversions[i].offset == -2)
1307 g_free (info->conversions[i].data.data);
1308 info->conversions[i].data.data = NULL;
1314 info->conversions[i].offset = -1;
1320 /* Check if we're finished with all the targets */
1322 if (info->num_incrs == 0)
1324 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1325 g_list_free (tmp_list);
1326 /* Let the timeout free it */
1332 /*************************************************************
1333 * gtk_selection_incr_timeout:
1334 * Timeout callback for the sending portion of the INCR
1337 * info: Information about this incr
1339 *************************************************************/
1342 gtk_selection_incr_timeout (GtkIncrInfo *info)
1347 GDK_THREADS_ENTER ();
1349 /* Determine if retrieval has finished by checking if it still in
1350 list of pending retrievals */
1352 tmp_list = current_incrs;
1355 if (info == (GtkIncrInfo *)tmp_list->data)
1357 tmp_list = tmp_list->next;
1360 /* If retrieval is finished */
1361 if (!tmp_list || info->idle_time >= 5)
1363 if (tmp_list && info->idle_time >= 5)
1365 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1366 g_list_free (tmp_list);
1369 g_free (info->conversions);
1370 /* FIXME: we should check if requestor window is still in use,
1371 and if not, remove it? */
1375 retval = FALSE; /* remove timeout */
1381 retval = TRUE; /* timeout will happen again */
1384 GDK_THREADS_LEAVE ();
1389 /*************************************************************
1390 * gtk_selection_notify:
1391 * Handler for "selection_notify_event" signals on windows
1392 * where a retrieval is currently in process. The selection
1393 * owner has responded to our conversion request.
1395 * widget: Widget getting signal
1396 * event: Selection event structure
1397 * info: Information about this retrieval
1399 * was event handled?
1400 *************************************************************/
1403 gtk_selection_notify (GtkWidget *widget,
1404 GdkEventSelection *event)
1407 GtkRetrievalInfo *info = NULL;
1408 guchar *buffer = NULL;
1413 #ifdef DEBUG_SELECTION
1414 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1415 event->selection, event->target, event->property);
1418 tmp_list = current_retrievals;
1421 info = (GtkRetrievalInfo *)tmp_list->data;
1422 if (info->widget == widget && info->selection == event->selection)
1424 tmp_list = tmp_list->next;
1427 if (!tmp_list) /* no retrieval in progress */
1430 if (event->property != GDK_NONE)
1431 length = gdk_selection_property_get (widget->window, &buffer,
1434 length = 0; /* silence gcc */
1436 if (event->property == GDK_NONE || buffer == NULL)
1438 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1439 g_list_free (tmp_list);
1440 /* structure will be freed in timeout */
1441 gtk_selection_retrieval_report (info,
1442 GDK_NONE, 0, NULL, -1, event->time);
1447 if (type == gtk_selection_atoms[INCR])
1449 /* The remainder of the selection will come through PropertyNotify
1452 info->notify_time = event->time;
1453 info->idle_time = 0;
1454 info->offset = 0; /* Mark as OK to proceed */
1455 gdk_window_set_events (widget->window,
1456 gdk_window_get_events (widget->window)
1457 | GDK_PROPERTY_CHANGE_MASK);
1461 /* We don't delete the info structure - that will happen in timeout */
1462 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1463 g_list_free (tmp_list);
1465 info->offset = length;
1466 gtk_selection_retrieval_report (info,
1468 buffer, length, event->time);
1471 gdk_property_delete (widget->window, event->property);
1478 /*************************************************************
1479 * gtk_selection_property_notify:
1480 * Handler for "property_notify_event" signals on windows
1481 * where a retrieval is currently in process. The selection
1482 * owner has added more data.
1484 * widget: Widget getting signal
1485 * event: Property event structure
1486 * info: Information about this retrieval
1488 * was event handled?
1489 *************************************************************/
1492 gtk_selection_property_notify (GtkWidget *widget,
1493 GdkEventProperty *event)
1496 GtkRetrievalInfo *info = NULL;
1502 g_return_val_if_fail (widget != NULL, FALSE);
1503 g_return_val_if_fail (event != NULL, FALSE);
1505 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1506 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1507 (event->atom != gdk_atom_intern ("GDK_SELECTION", FALSE))) /* not the right property */
1511 #ifdef DEBUG_SELECTION
1512 g_message ("PropertyNewValue, property %ld",
1516 tmp_list = current_retrievals;
1519 info = (GtkRetrievalInfo *)tmp_list->data;
1520 if (info->widget == widget)
1522 tmp_list = tmp_list->next;
1525 if (!tmp_list) /* No retrieval in progress */
1528 if (info->offset < 0) /* We haven't got the SelectionNotify
1529 for this retrieval yet */
1532 info->idle_time = 0;
1534 length = gdk_selection_property_get (widget->window, &new_buffer,
1536 gdk_property_delete (widget->window, event->atom);
1538 /* We could do a lot better efficiency-wise by paying attention to
1539 what length was sent in the initial INCR transaction, instead of
1540 doing memory allocation at every step. But its only guaranteed to
1541 be a _lower bound_ (pretty useless!) */
1543 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1545 /* Info structure will be freed in timeout */
1546 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1547 g_list_free (tmp_list);
1548 gtk_selection_retrieval_report (info,
1550 (type == GDK_NONE) ? NULL : info->buffer,
1551 (type == GDK_NONE) ? -1 : info->offset,
1554 else /* append on newly arrived data */
1558 #ifdef DEBUG_SELECTION
1559 g_message ("Start - Adding %d bytes at offset 0",
1562 info->buffer = new_buffer;
1563 info->offset = length;
1568 #ifdef DEBUG_SELECTION
1569 g_message ("Appending %d bytes at offset %d",
1570 length,info->offset);
1572 /* We copy length+1 bytes to preserve guaranteed null termination */
1573 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1574 memcpy (info->buffer + info->offset, new_buffer, length+1);
1575 info->offset += length;
1576 g_free (new_buffer);
1583 /*************************************************************
1584 * gtk_selection_retrieval_timeout:
1585 * Timeout callback while receiving a selection.
1587 * info: Information about this retrieval
1589 *************************************************************/
1592 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1597 GDK_THREADS_ENTER ();
1599 /* Determine if retrieval has finished by checking if it still in
1600 list of pending retrievals */
1602 tmp_list = current_retrievals;
1605 if (info == (GtkRetrievalInfo *)tmp_list->data)
1607 tmp_list = tmp_list->next;
1610 /* If retrieval is finished */
1611 if (!tmp_list || info->idle_time >= 5)
1613 if (tmp_list && info->idle_time >= 5)
1615 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1616 g_list_free (tmp_list);
1617 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1620 g_free (info->buffer);
1623 retval = FALSE; /* remove timeout */
1629 retval = TRUE; /* timeout will happen again */
1632 GDK_THREADS_LEAVE ();
1637 /*************************************************************
1638 * gtk_selection_retrieval_report:
1639 * Emits a "selection_received" signal.
1641 * info: information about the retrieval that completed
1642 * buffer: buffer containing data (NULL => errror)
1643 * time: timestamp for data in buffer
1645 *************************************************************/
1648 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1649 GdkAtom type, gint format,
1650 guchar *buffer, gint length,
1653 GtkSelectionData data;
1655 data.selection = info->selection;
1656 data.target = info->target;
1658 data.format = format;
1660 data.length = length;
1663 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1664 "selection_received",
1668 /*************************************************************
1669 * gtk_selection_invoke_handler:
1670 * Finds and invokes handler for specified
1671 * widget/selection/target combination, calls
1672 * gtk_selection_default_handler if none exists.
1675 * widget: selection owner
1676 * data: selection data [INOUT]
1677 * time: time from requeset
1680 * Number of bytes written to buffer, -1 if error
1681 *************************************************************/
1684 gtk_selection_invoke_handler (GtkWidget *widget,
1685 GtkSelectionData *data,
1688 GtkTargetList *target_list;
1692 g_return_if_fail (widget != NULL);
1694 target_list = gtk_selection_target_list_get (widget, data->selection);
1696 gtk_target_list_find (target_list, data->target, &info))
1698 gtk_signal_emit_by_name (GTK_OBJECT (widget),
1704 gtk_selection_default_handler (widget, data);
1707 /*************************************************************
1708 * gtk_selection_default_handler:
1709 * Handles some default targets that exist for any widget
1710 * If it can't fit results into buffer, returns -1. This
1711 * won't happen in any conceivable case, since it would
1712 * require 1000 selection targets!
1715 * widget: selection owner
1716 * data: selection data [INOUT]
1718 *************************************************************/
1721 gtk_selection_default_handler (GtkWidget *widget,
1722 GtkSelectionData *data)
1724 if (data->target == gtk_selection_atoms[TIMESTAMP])
1726 /* Time which was used to obtain selection */
1728 GtkSelectionInfo *selection_info;
1730 tmp_list = current_selections;
1733 selection_info = (GtkSelectionInfo *)tmp_list->data;
1734 if ((selection_info->widget == widget) &&
1735 (selection_info->selection == data->selection))
1737 gulong time = selection_info->time;
1739 gtk_selection_data_set (data,
1740 GDK_SELECTION_TYPE_INTEGER,
1747 tmp_list = tmp_list->next;
1752 else if (data->target == gtk_selection_atoms[TARGETS])
1754 /* List of all targets supported for this widget/selection pair */
1758 GtkTargetList *target_list;
1759 GtkTargetPair *pair;
1761 target_list = gtk_selection_target_list_get (widget,
1763 count = g_list_length (target_list->list) + 3;
1765 data->type = GDK_SELECTION_TYPE_ATOM;
1767 data->length = count * sizeof (GdkAtom);
1769 p = g_new (GdkAtom, count);
1770 data->data = (guchar *)p;
1772 *p++ = gtk_selection_atoms[TIMESTAMP];
1773 *p++ = gtk_selection_atoms[TARGETS];
1774 *p++ = gtk_selection_atoms[MULTIPLE];
1776 tmp_list = target_list->list;
1779 pair = (GtkTargetPair *)tmp_list->data;
1780 *p++ = pair->target;
1782 tmp_list = tmp_list->next;
1793 gtk_selection_data_copy (GtkSelectionData *selection_data)
1795 GtkSelectionData *new_data;
1797 g_return_val_if_fail (selection_data != NULL, NULL);
1799 new_data = g_new (GtkSelectionData, 1);
1800 *new_data = *selection_data;
1802 if (selection_data->data)
1804 new_data->data = g_malloc (selection_data->length + 1);
1805 memcpy (new_data->data, selection_data->data, selection_data->length + 1);
1812 gtk_selection_data_free (GtkSelectionData *data)
1814 g_return_if_fail (data != NULL);
1817 g_free (data->data);
1823 gtk_selection_data_get_type (void)
1825 static GType our_type = 0;
1828 our_type = g_boxed_type_register_static ("GtkTypeSelectionData",
1829 (GBoxedCopyFunc) gtk_selection_data_copy,
1830 (GBoxedFreeFunc) gtk_selection_data_free);
1836 gtk_selection_bytes_per_item (gint format)
1841 return sizeof (char);
1844 return sizeof (short);
1847 return sizeof (long);
1850 g_assert_not_reached();