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 Library 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 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library 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-1999. 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/.
57 /* we need this for gdk_window_lookup() */
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 GtkWidget *widget; /* Selection owner */
106 GdkWindow *requestor; /* Requestor window - we create a GdkWindow
107 so we can receive events */
108 GdkAtom selection; /* Selection we're sending */
110 GtkIncrConversion *conversions; /* Information about requested conversions -
111 * With MULTIPLE requests (benighted 1980's
112 * hardware idea), there can be more than
114 gint num_conversions;
115 gint num_incrs; /* number of remaining INCR style transactions */
120 struct _GtkRetrievalInfo
123 GdkAtom selection; /* Selection being retrieved. */
124 GdkAtom target; /* Form of selection that we requested */
125 guint32 idle_time; /* Number of seconds since we last heard
126 from selection owner */
127 guchar *buffer; /* Buffer in which to accumulate results */
128 gint offset; /* Current offset in buffer, -1 indicates
130 guint32 notify_time; /* Timestamp from SelectionNotify */
133 /* Local Functions */
134 static void gtk_selection_init (void);
135 static gint gtk_selection_incr_timeout (GtkIncrInfo *info);
136 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
137 static void gtk_selection_retrieval_report (GtkRetrievalInfo *info,
138 GdkAtom type, gint format,
139 guchar *buffer, gint length,
141 static void gtk_selection_invoke_handler (GtkWidget *widget,
142 GtkSelectionData *data,
144 static void gtk_selection_default_handler (GtkWidget *widget,
145 GtkSelectionData *data);
148 static gint initialize = TRUE;
149 static GList *current_retrievals = NULL;
150 static GList *current_incrs = NULL;
151 static GList *current_selections = NULL;
153 static GdkAtom gtk_selection_atoms[LAST_ATOM];
154 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
165 gtk_target_list_new (const GtkTargetEntry *targets,
168 GtkTargetList *result = g_new (GtkTargetList, 1);
170 result->ref_count = 1;
173 gtk_target_list_add_table (result, targets, ntargets);
179 gtk_target_list_ref (GtkTargetList *list)
185 gtk_target_list_unref (GtkTargetList *list)
188 if (list->ref_count == 0)
190 GList *tmp_list = list->list;
193 GtkTargetPair *pair = tmp_list->data;
196 tmp_list = tmp_list->next;
199 g_list_free (list->list);
205 gtk_target_list_add (GtkTargetList *list,
212 g_return_if_fail (list != NULL);
214 pair = g_new (GtkTargetPair, 1);
215 pair->target = target;
219 list->list = g_list_append (list->list, pair);
223 gtk_target_list_add_table (GtkTargetList *list,
224 const GtkTargetEntry *targets,
229 for (i=ntargets-1; i >= 0; i--)
231 GtkTargetPair *pair = g_new (GtkTargetPair, 1);
232 pair->target = gdk_atom_intern (targets[i].target, FALSE);
233 pair->flags = targets[i].flags;
234 pair->info = targets[i].info;
236 list->list = g_list_prepend (list->list, pair);
241 gtk_target_list_remove (GtkTargetList *list,
246 g_return_if_fail (list != NULL);
248 tmp_list = list->list;
251 GtkTargetPair *pair = tmp_list->data;
253 if (pair->target == target)
257 list->list = g_list_remove (list->list, tmp_list);
258 g_list_free_1 (tmp_list);
263 tmp_list = tmp_list->next;
268 gtk_target_list_find (GtkTargetList *list,
272 GList *tmp_list = list->list;
275 GtkTargetPair *pair = tmp_list->data;
277 if (pair->target == target)
282 tmp_list = tmp_list->next;
289 /*************************************************************
290 * gtk_selection_owner_set:
291 * Claim ownership of a selection.
293 * widget: new selection owner
294 * selection: which selection
295 * time: time (use GDK_CURRENT_TIME only if necessary)
298 *************************************************************/
301 gtk_selection_owner_set (GtkWidget *widget,
306 GtkWidget *old_owner;
307 GtkSelectionInfo *selection_info = NULL;
314 if (!GTK_WIDGET_REALIZED (widget))
315 gtk_widget_realize (widget);
317 window = widget->window;
320 tmp_list = current_selections;
323 selection_info = (GtkSelectionInfo *)tmp_list->data;
325 if (selection_info->selection == selection)
328 tmp_list = tmp_list->next;
331 if (tmp_list == NULL)
332 selection_info = NULL;
334 if (selection_info->widget == widget)
337 if (gdk_selection_owner_set (window, selection, time, TRUE))
345 old_owner = selection_info->widget;
346 current_selections = g_list_remove_link (current_selections,
348 g_list_free (tmp_list);
349 g_free (selection_info);
354 if (selection_info == NULL)
356 selection_info = g_new (GtkSelectionInfo, 1);
357 selection_info->selection = selection;
358 selection_info->widget = widget;
359 selection_info->time = time;
360 current_selections = g_list_append (current_selections,
365 old_owner = selection_info->widget;
366 selection_info->widget = widget;
367 selection_info->time = time;
370 /* If another widget in the application lost the selection,
371 * send it a GDK_SELECTION_CLEAR event, unless we're setting
372 * the owner to None, in which case an event will be sent */
373 if (old_owner && (widget != NULL))
375 GdkEventSelection event;
377 event.type = GDK_SELECTION_CLEAR;
378 event.window = old_owner->window;
379 event.selection = selection;
382 gtk_widget_event (old_owner, (GdkEvent *) &event);
390 /*************************************************************
391 * gtk_selection_add_target
392 * Add specified target to list of supported targets
395 * widget: The widget for which this target applies
398 * info: guint to pass to to the selection_get signal
401 *************************************************************/
403 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
405 struct _GtkSelectionTargetList {
410 static GtkTargetList *
411 gtk_selection_target_list_get (GtkWidget *widget,
414 GtkSelectionTargetList *sellist;
418 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
423 sellist = tmp_list->data;
424 if (sellist->selection == selection)
425 return sellist->list;
426 tmp_list = tmp_list->next;
429 sellist = g_new (GtkSelectionTargetList, 1);
430 sellist->selection = selection;
431 sellist->list = gtk_target_list_new (NULL, 0);
433 lists = g_list_prepend (lists, sellist);
434 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
436 return sellist->list;
440 gtk_selection_target_list_remove (GtkWidget *widget)
442 GtkSelectionTargetList *sellist;
446 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
451 sellist = tmp_list->data;
453 gtk_target_list_unref (sellist->list);
456 tmp_list = tmp_list->next;
460 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL);
464 gtk_selection_add_target (GtkWidget *widget,
471 g_return_if_fail (widget != NULL);
473 list = gtk_selection_target_list_get (widget, selection);
474 gtk_target_list_add (list, target, 0, info);
478 gtk_selection_add_targets (GtkWidget *widget,
480 const GtkTargetEntry *targets,
485 g_return_if_fail (widget != NULL);
486 g_return_if_fail (targets != NULL);
488 list = gtk_selection_target_list_get (widget, selection);
489 gtk_target_list_add_table (list, targets, ntargets);
492 /*************************************************************
493 * gtk_selection_remove_all:
494 * Removes all handlers and unsets ownership of all
495 * selections for a widget. Called when widget is being
501 *************************************************************/
504 gtk_selection_remove_all (GtkWidget *widget)
508 GtkSelectionInfo *selection_info;
510 /* Remove pending requests/incrs for this widget */
512 tmp_list = current_incrs;
515 next = tmp_list->next;
516 if (((GtkIncrInfo *)tmp_list->data)->widget == widget)
518 current_incrs = g_list_remove_link (current_incrs, tmp_list);
519 /* structure will be freed in timeout */
520 g_list_free (tmp_list);
525 tmp_list = current_retrievals;
528 next = tmp_list->next;
529 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
531 current_retrievals = g_list_remove_link (current_retrievals,
533 /* structure will be freed in timeout */
534 g_list_free (tmp_list);
539 /* Disclaim ownership of any selections */
541 tmp_list = current_selections;
544 next = tmp_list->next;
545 selection_info = (GtkSelectionInfo *)tmp_list->data;
547 if (selection_info->widget == widget)
549 gdk_selection_owner_set (NULL,
550 selection_info->selection,
551 GDK_CURRENT_TIME, FALSE);
552 current_selections = g_list_remove_link (current_selections,
554 g_list_free (tmp_list);
555 g_free (selection_info);
561 /* Remove all selection lists */
562 gtk_selection_target_list_remove (widget);
565 /*************************************************************
566 * gtk_selection_convert:
567 * Request the contents of a selection. When received,
568 * a "selection_received" signal will be generated.
571 * widget: The widget which acts as requestor
572 * selection: Which selection to get
573 * target: Form of information desired (e.g., STRING)
574 * time: Time of request (usually of triggering event)
575 * In emergency, you could use GDK_CURRENT_TIME
578 * TRUE if requested succeeded. FALSE if we could not process
579 * request. (e.g., there was already a request in process for
581 *************************************************************/
584 gtk_selection_convert (GtkWidget *widget,
589 GtkRetrievalInfo *info;
591 GdkWindow *owner_window;
593 g_return_val_if_fail (widget != NULL, FALSE);
596 gtk_selection_init ();
598 if (!GTK_WIDGET_REALIZED (widget))
599 gtk_widget_realize (widget);
601 /* Check to see if there are already any retrievals in progress for
602 this widget. If we changed GDK to use the selection for the
603 window property in which to store the retrieved information, then
604 we could support multiple retrievals for different selections.
605 This might be useful for DND. */
607 tmp_list = current_retrievals;
610 info = (GtkRetrievalInfo *)tmp_list->data;
611 if (info->widget == widget)
613 tmp_list = tmp_list->next;
616 info = g_new (GtkRetrievalInfo, 1);
618 info->widget = widget;
619 info->selection = selection;
620 info->target = target;
624 /* Check if this process has current owner. If so, call handler
625 procedure directly to avoid deadlocks with INCR. */
627 owner_window = gdk_selection_owner_get (selection);
629 if (owner_window != NULL)
631 GtkWidget *owner_widget;
632 GtkSelectionData selection_data;
634 selection_data.selection = selection;
635 selection_data.target = target;
636 selection_data.data = NULL;
637 selection_data.length = -1;
639 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
641 if (owner_widget != NULL)
643 gtk_selection_invoke_handler (owner_widget,
647 gtk_selection_retrieval_report (info,
649 selection_data.format,
651 selection_data.length,
654 g_free (selection_data.data);
661 /* Otherwise, we need to go through X */
663 current_retrievals = g_list_append (current_retrievals, info);
664 gdk_selection_convert (widget->window, selection, target, time);
665 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
670 /*************************************************************
671 * gtk_selection_data_set:
672 * Store new data into a GtkSelectionData object. Should
673 * _only_ by called from a selection handler callback.
674 * Null terminates the stored data.
676 * type: the type of selection data
677 * format: format (number of bits in a unit)
678 * data: pointer to the data (will be copied)
679 * length: length of the data
681 *************************************************************/
684 gtk_selection_data_set (GtkSelectionData *selection_data,
690 if (selection_data->data)
691 g_free (selection_data->data);
693 selection_data->type = type;
694 selection_data->format = format;
698 selection_data->data = g_new (guchar, length+1);
699 memcpy (selection_data->data, data, length);
700 selection_data->data[length] = 0;
704 g_return_if_fail (length <= 0);
707 selection_data->data = NULL;
709 selection_data->data = g_strdup("");
712 selection_data->length = length;
715 /*************************************************************
716 * gtk_selection_init:
717 * Initialize local variables
721 *************************************************************/
724 gtk_selection_init (void)
726 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
727 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
728 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
729 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
732 /*************************************************************
733 * gtk_selection_clear:
734 * Handler for "selection_clear_event"
739 *************************************************************/
742 gtk_selection_clear (GtkWidget *widget,
743 GdkEventSelection *event)
745 /* FIXME: there can be a problem if we change the selection
746 via gtk_selection_owner_set after another client claims
747 the selection, but before we get the notification event.
748 Tk filters based on serial #'s, which aren't retained by
749 GTK. Filtering based on time's will be inherently
750 somewhat unreliable. */
753 GtkSelectionInfo *selection_info = NULL;
755 tmp_list = current_selections;
758 selection_info = (GtkSelectionInfo *)tmp_list->data;
760 if ((selection_info->selection == event->selection) &&
761 (selection_info->widget == widget))
764 tmp_list = tmp_list->next;
769 if (selection_info->time > event->time)
770 return FALSE; /* return FALSE to indicate that
771 * the selection was out of date,
772 * and this clear should be ignored */
775 current_selections = g_list_remove_link (current_selections, tmp_list);
776 g_list_free (tmp_list);
777 g_free (selection_info);
785 /*************************************************************
786 * gtk_selection_request:
787 * Handler for "selection_request_event"
792 *************************************************************/
795 gtk_selection_request (GtkWidget *widget,
796 GdkEventSelection *event)
804 gtk_selection_init ();
806 /* Check if we own selection */
808 tmp_list = current_selections;
811 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
813 if ((selection_info->selection == event->selection) &&
814 (selection_info->widget == widget))
817 tmp_list = tmp_list->next;
820 if (tmp_list == NULL)
823 info = g_new(GtkIncrInfo, 1);
825 info->widget = widget;
826 info->selection = event->selection;
829 /* Create GdkWindow structure for the requestor */
831 info->requestor = gdk_window_lookup (event->requestor);
832 if (!info->requestor)
833 info->requestor = gdk_window_foreign_new (event->requestor);
835 /* Determine conversions we need to perform */
837 if (event->target == gtk_selection_atoms[MULTIPLE])
844 if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
845 0, GTK_SELECTION_MAX_SIZE, FALSE,
846 &type, &format, &length, &mult_atoms))
848 gdk_selection_send_notify (event->requestor, event->selection,
849 event->target, GDK_NONE, event->time);
855 info->num_conversions = length / (2*sizeof (GdkAtom));
856 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
858 for (i=0; i<info->num_conversions; i++)
860 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
861 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
864 else /* only a single conversion */
866 info->conversions = g_new (GtkIncrConversion, 1);
867 info->num_conversions = 1;
868 info->conversions[0].target = event->target;
869 info->conversions[0].property = event->property;
870 mult_atoms = (guchar *)info->conversions;
873 /* Loop through conversions and determine which of these are big
874 enough to require doing them via INCR */
875 for (i=0; i<info->num_conversions; i++)
877 GtkSelectionData data;
880 data.selection = event->selection;
881 data.target = info->conversions[i].target;
885 #ifdef DEBUG_SELECTION
886 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
887 event->selection, info->conversions[i].target,
888 gdk_atom_name(info->conversions[i].target),
889 event->requestor, event->property);
892 gtk_selection_invoke_handler (widget, &data, event->time);
896 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
897 info->conversions[i].property = GDK_NONE;
901 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
903 items = (data.length + data.format/8 - 1) / (data.format/8);
905 if (data.length > GTK_SELECTION_MAX_SIZE)
907 /* Sending via INCR */
909 info->conversions[i].offset = 0;
910 info->conversions[i].data = data;
913 gdk_property_change (info->requestor,
914 info->conversions[i].property,
915 gtk_selection_atoms[INCR],
917 GDK_PROP_MODE_REPLACE,
918 (guchar *)&items, 1);
922 info->conversions[i].offset = -1;
924 gdk_property_change (info->requestor,
925 info->conversions[i].property,
928 GDK_PROP_MODE_REPLACE,
935 /* If we have some INCR's, we need to send the rest of the data in
938 if (info->num_incrs > 0)
940 /* FIXME: this could be dangerous if window doesn't still
943 #ifdef DEBUG_SELECTION
944 g_message ("Starting INCR...");
947 gdk_window_set_events (info->requestor,
948 gdk_window_get_events (info->requestor) |
949 GDK_PROPERTY_CHANGE_MASK);
950 current_incrs = g_list_append (current_incrs, info);
951 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
954 /* If it was a MULTIPLE request, set the property to indicate which
955 conversions succeeded */
956 if (event->target == gtk_selection_atoms[MULTIPLE])
958 gdk_property_change (info->requestor, event->property,
959 GDK_SELECTION_TYPE_ATOM, 8*sizeof(GdkAtom),
960 GDK_PROP_MODE_REPLACE,
961 mult_atoms, 2*info->num_conversions);
965 gdk_selection_send_notify (event->requestor, event->selection, event->target,
966 event->property, event->time);
968 if (info->num_incrs == 0)
970 g_free (info->conversions);
977 /*************************************************************
978 * gtk_selection_incr_event:
979 * Called whenever an PropertyNotify event occurs for an
980 * GdkWindow with user_data == NULL. These will be notifications
981 * that a window we are sending the selection to via the
982 * INCR protocol has deleted a property and is ready for
986 * window: the requestor window
987 * event: the property event structure
990 *************************************************************/
993 gtk_selection_incr_event (GdkWindow *window,
994 GdkEventProperty *event)
997 GtkIncrInfo *info = NULL;
1003 if (event->state != GDK_PROPERTY_DELETE)
1006 #ifdef DEBUG_SELECTION
1007 g_message ("PropertyDelete, property %ld", event->atom);
1010 /* Now find the appropriate ongoing INCR */
1011 tmp_list = current_incrs;
1014 info = (GtkIncrInfo *)tmp_list->data;
1015 if (info->requestor == event->window)
1018 tmp_list = tmp_list->next;
1021 if (tmp_list == NULL)
1024 /* Find out which target this is for */
1025 for (i=0; i<info->num_conversions; i++)
1027 if (info->conversions[i].property == event->atom &&
1028 info->conversions[i].offset != -1)
1030 info->idle_time = 0;
1032 if (info->conversions[i].offset == -2) /* only the last 0-length
1040 num_bytes = info->conversions[i].data.length -
1041 info->conversions[i].offset;
1042 buffer = info->conversions[i].data.data +
1043 info->conversions[i].offset;
1045 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1047 num_bytes = GTK_SELECTION_MAX_SIZE;
1048 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1051 info->conversions[i].offset = -2;
1053 #ifdef DEBUG_SELECTION
1054 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1055 num_bytes, info->conversions[i].offset,
1056 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1058 gdk_property_change (info->requestor, event->atom,
1059 info->conversions[i].data.type,
1060 info->conversions[i].data.format,
1061 GDK_PROP_MODE_REPLACE,
1063 (num_bytes + info->conversions[i].data.format/8 - 1) /
1064 (info->conversions[i].data.format/8));
1066 if (info->conversions[i].offset == -2)
1068 g_free (info->conversions[i].data.data);
1069 info->conversions[i].data.data = NULL;
1075 info->conversions[i].offset = -1;
1081 /* Check if we're finished with all the targets */
1083 if (info->num_incrs == 0)
1085 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1086 g_list_free (tmp_list);
1087 /* Let the timeout free it */
1093 /*************************************************************
1094 * gtk_selection_incr_timeout:
1095 * Timeout callback for the sending portion of the INCR
1098 * info: Information about this incr
1100 *************************************************************/
1103 gtk_selection_incr_timeout (GtkIncrInfo *info)
1108 GDK_THREADS_ENTER ();
1110 /* Determine if retrieval has finished by checking if it still in
1111 list of pending retrievals */
1113 tmp_list = current_incrs;
1116 if (info == (GtkIncrInfo *)tmp_list->data)
1118 tmp_list = tmp_list->next;
1121 /* If retrieval is finished */
1122 if (!tmp_list || info->idle_time >= 5)
1124 if (tmp_list && info->idle_time >= 5)
1126 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1127 g_list_free (tmp_list);
1130 g_free (info->conversions);
1131 /* FIXME: we should check if requestor window is still in use,
1132 and if not, remove it? */
1136 retval = FALSE; /* remove timeout */
1142 retval = TRUE; /* timeout will happen again */
1145 GDK_THREADS_LEAVE ();
1150 /*************************************************************
1151 * gtk_selection_notify:
1152 * Handler for "selection_notify_event" signals on windows
1153 * where a retrieval is currently in process. The selection
1154 * owner has responded to our conversion request.
1156 * widget: Widget getting signal
1157 * event: Selection event structure
1158 * info: Information about this retrieval
1160 * was event handled?
1161 *************************************************************/
1164 gtk_selection_notify (GtkWidget *widget,
1165 GdkEventSelection *event)
1168 GtkRetrievalInfo *info = NULL;
1174 #ifdef DEBUG_SELECTION
1175 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1176 event->selection, event->target, event->property);
1179 tmp_list = current_retrievals;
1182 info = (GtkRetrievalInfo *)tmp_list->data;
1183 if (info->widget == widget && info->selection == event->selection)
1185 tmp_list = tmp_list->next;
1188 if (!tmp_list) /* no retrieval in progress */
1191 if (event->property == GDK_NONE)
1193 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1194 g_list_free (tmp_list);
1195 /* structure will be freed in timeout */
1196 gtk_selection_retrieval_report (info,
1197 GDK_NONE, 0, NULL, -1, event->time);
1202 length = gdk_selection_property_get (widget->window, &buffer,
1205 if (type == gtk_selection_atoms[INCR])
1207 /* The remainder of the selection will come through PropertyNotify
1210 info->notify_time = event->time;
1211 info->idle_time = 0;
1212 info->offset = 0; /* Mark as OK to proceed */
1213 gdk_window_set_events (widget->window,
1214 gdk_window_get_events (widget->window)
1215 | GDK_PROPERTY_CHANGE_MASK);
1219 /* We don't delete the info structure - that will happen in timeout */
1220 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1221 g_list_free (tmp_list);
1223 info->offset = length;
1224 gtk_selection_retrieval_report (info,
1226 buffer, length, event->time);
1229 gdk_property_delete (widget->window, event->property);
1236 /*************************************************************
1237 * gtk_selection_property_notify:
1238 * Handler for "property_notify_event" signals on windows
1239 * where a retrieval is currently in process. The selection
1240 * owner has added more data.
1242 * widget: Widget getting signal
1243 * event: Property event structure
1244 * info: Information about this retrieval
1246 * was event handled?
1247 *************************************************************/
1250 gtk_selection_property_notify (GtkWidget *widget,
1251 GdkEventProperty *event)
1254 GtkRetrievalInfo *info = NULL;
1260 g_return_val_if_fail (widget != NULL, FALSE);
1261 g_return_val_if_fail (event != NULL, FALSE);
1263 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1264 (event->atom != gdk_selection_property)) /* not the right property */
1267 #ifdef DEBUG_SELECTION
1268 g_message ("PropertyNewValue, property %ld",
1272 tmp_list = current_retrievals;
1275 info = (GtkRetrievalInfo *)tmp_list->data;
1276 if (info->widget == widget)
1278 tmp_list = tmp_list->next;
1281 if (!tmp_list) /* No retrieval in progress */
1284 if (info->offset < 0) /* We haven't got the SelectionNotify
1285 for this retrieval yet */
1288 info->idle_time = 0;
1290 length = gdk_selection_property_get (widget->window, &new_buffer,
1292 gdk_property_delete (widget->window, event->atom);
1294 /* We could do a lot better efficiency-wise by paying attention to
1295 what length was sent in the initial INCR transaction, instead of
1296 doing memory allocation at every step. But its only guaranteed to
1297 be a _lower bound_ (pretty useless!) */
1299 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1301 /* Info structure will be freed in timeout */
1302 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1303 g_list_free (tmp_list);
1304 gtk_selection_retrieval_report (info,
1306 (type == GDK_NONE) ? NULL : info->buffer,
1307 (type == GDK_NONE) ? -1 : info->offset,
1310 else /* append on newly arrived data */
1314 #ifdef DEBUG_SELECTION
1315 g_message ("Start - Adding %d bytes at offset 0",
1318 info->buffer = new_buffer;
1319 info->offset = length;
1324 #ifdef DEBUG_SELECTION
1325 g_message ("Appending %d bytes at offset %d",
1326 length,info->offset);
1328 /* We copy length+1 bytes to preserve guaranteed null termination */
1329 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1330 memcpy (info->buffer + info->offset, new_buffer, length+1);
1331 info->offset += length;
1332 g_free (new_buffer);
1339 /*************************************************************
1340 * gtk_selection_retrieval_timeout:
1341 * Timeout callback while receiving a selection.
1343 * info: Information about this retrieval
1345 *************************************************************/
1348 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1353 GDK_THREADS_ENTER ();
1355 /* Determine if retrieval has finished by checking if it still in
1356 list of pending retrievals */
1358 tmp_list = current_retrievals;
1361 if (info == (GtkRetrievalInfo *)tmp_list->data)
1363 tmp_list = tmp_list->next;
1366 /* If retrieval is finished */
1367 if (!tmp_list || info->idle_time >= 5)
1369 if (tmp_list && info->idle_time >= 5)
1371 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1372 g_list_free (tmp_list);
1373 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1376 g_free (info->buffer);
1379 retval = FALSE; /* remove timeout */
1385 retval = TRUE; /* timeout will happen again */
1388 GDK_THREADS_LEAVE ();
1393 /*************************************************************
1394 * gtk_selection_retrieval_report:
1395 * Emits a "selection_received" signal.
1397 * info: information about the retrieval that completed
1398 * buffer: buffer containing data (NULL => errror)
1399 * time: timestamp for data in buffer
1401 *************************************************************/
1404 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1405 GdkAtom type, gint format,
1406 guchar *buffer, gint length,
1409 GtkSelectionData data;
1411 data.selection = info->selection;
1412 data.target = info->target;
1414 data.format = format;
1416 data.length = length;
1419 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1420 "selection_received",
1424 /*************************************************************
1425 * gtk_selection_invoke_handler:
1426 * Finds and invokes handler for specified
1427 * widget/selection/target combination, calls
1428 * gtk_selection_default_handler if none exists.
1431 * widget: selection owner
1432 * data: selection data [INOUT]
1433 * time: time from requeset
1436 * Number of bytes written to buffer, -1 if error
1437 *************************************************************/
1440 gtk_selection_invoke_handler (GtkWidget *widget,
1441 GtkSelectionData *data,
1444 GtkTargetList *target_list;
1448 g_return_if_fail (widget != NULL);
1450 target_list = gtk_selection_target_list_get (widget, data->selection);
1452 gtk_target_list_find (target_list, data->target, &info))
1454 gtk_signal_emit_by_name (GTK_OBJECT (widget),
1460 gtk_selection_default_handler (widget, data);
1463 /*************************************************************
1464 * gtk_selection_default_handler:
1465 * Handles some default targets that exist for any widget
1466 * If it can't fit results into buffer, returns -1. This
1467 * won't happen in any conceivable case, since it would
1468 * require 1000 selection targets!
1471 * widget: selection owner
1472 * data: selection data [INOUT]
1474 *************************************************************/
1477 gtk_selection_default_handler (GtkWidget *widget,
1478 GtkSelectionData *data)
1480 if (data->target == gtk_selection_atoms[TIMESTAMP])
1482 /* Time which was used to obtain selection */
1484 GtkSelectionInfo *selection_info;
1486 tmp_list = current_selections;
1489 selection_info = (GtkSelectionInfo *)tmp_list->data;
1490 if ((selection_info->widget == widget) &&
1491 (selection_info->selection == data->selection))
1493 gtk_selection_data_set (data,
1494 GDK_SELECTION_TYPE_INTEGER,
1496 (guchar *)&selection_info->time,
1501 tmp_list = tmp_list->next;
1506 else if (data->target == gtk_selection_atoms[TARGETS])
1508 /* List of all targets supported for this widget/selection pair */
1512 GtkTargetList *target_list;
1513 GtkTargetPair *pair;
1515 target_list = gtk_selection_target_list_get (widget,
1517 count = g_list_length (target_list->list) + 3;
1519 data->type = GDK_SELECTION_TYPE_ATOM;
1520 data->format = 8*sizeof (GdkAtom);
1521 data->length = count * sizeof (GdkAtom);
1523 p = g_new (GdkAtom, count);
1524 data->data = (guchar *)p;
1526 *p++ = gtk_selection_atoms[TIMESTAMP];
1527 *p++ = gtk_selection_atoms[TARGETS];
1528 *p++ = gtk_selection_atoms[MULTIPLE];
1530 tmp_list = target_list->list;
1533 pair = (GtkTargetPair *)tmp_list->data;
1534 *p++ = pair->target;
1536 tmp_list = tmp_list->next;
1547 gtk_selection_data_copy (GtkSelectionData *data)
1549 GtkSelectionData *new_data;
1551 g_return_val_if_fail (data != NULL, NULL);
1553 new_data = g_new (GtkSelectionData, 1);
1560 gtk_selection_data_free (GtkSelectionData *data)
1562 g_return_if_fail (data != NULL);