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/.
56 #include "gdkx.h" /* For gdk_window_lookup() */
58 #include "gtkselection.h"
59 #include "gtksignal.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
79 typedef struct _GtkSelectionInfo GtkSelectionInfo;
80 typedef struct _GtkIncrConversion GtkIncrConversion;
81 typedef struct _GtkIncrInfo GtkIncrInfo;
82 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
84 struct _GtkSelectionInfo
87 GtkWidget *widget; /* widget that owns selection */
88 guint32 time; /* time used to acquire selection */
91 struct _GtkIncrConversion
93 GdkAtom target; /* Requested target */
94 GdkAtom property; /* Property to store in */
95 GtkSelectionData data; /* The data being supplied */
96 gint offset; /* Current offset in sent selection.
98 * -2 => Only the final (empty) portion
104 GtkWidget *widget; /* Selection owner */
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,
137 GdkAtom type, gint format,
138 guchar *buffer, gint length,
140 static void gtk_selection_invoke_handler (GtkWidget *widget,
141 GtkSelectionData *data,
143 static void gtk_selection_default_handler (GtkWidget *widget,
144 GtkSelectionData *data);
147 static gint initialize = TRUE;
148 static GList *current_retrievals = NULL;
149 static GList *current_incrs = NULL;
150 static GList *current_selections = NULL;
152 static GdkAtom gtk_selection_atoms[LAST_ATOM];
153 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
164 gtk_target_list_new (const GtkTargetEntry *targets,
167 GtkTargetList *result = g_new (GtkTargetList, 1);
169 result->ref_count = 1;
172 gtk_target_list_add_table (result, targets, ntargets);
178 gtk_target_list_ref (GtkTargetList *list)
184 gtk_target_list_unref (GtkTargetList *list)
187 if (list->ref_count == 0)
189 GList *tmp_list = list->list;
192 GtkTargetPair *pair = tmp_list->data;
195 tmp_list = tmp_list->next;
198 g_list_free (list->list);
204 gtk_target_list_add (GtkTargetList *list,
211 g_return_if_fail (list != NULL);
213 pair = g_new (GtkTargetPair, 1);
214 pair->target = target;
218 list->list = g_list_append (list->list, pair);
222 gtk_target_list_add_table (GtkTargetList *list,
223 const GtkTargetEntry *targets,
228 for (i=ntargets-1; i >= 0; i--)
230 GtkTargetPair *pair = g_new (GtkTargetPair, 1);
231 pair->target = gdk_atom_intern (targets[i].target, FALSE);
232 pair->flags = targets[i].flags;
233 pair->info = targets[i].info;
235 list->list = g_list_prepend (list->list, pair);
240 gtk_target_list_remove (GtkTargetList *list,
245 g_return_if_fail (list != NULL);
247 tmp_list = list->list;
250 GtkTargetPair *pair = tmp_list->data;
252 if (pair->target == target)
256 list->list = g_list_remove (list->list, tmp_list);
257 g_list_free_1 (tmp_list);
262 tmp_list = tmp_list->next;
267 gtk_target_list_find (GtkTargetList *list,
271 GList *tmp_list = list->list;
274 GtkTargetPair *pair = tmp_list->data;
276 if (pair->target == target)
281 tmp_list = tmp_list->next;
288 /*************************************************************
289 * gtk_selection_owner_set:
290 * Claim ownership of a selection.
292 * widget: new selection owner
293 * selection: which selection
294 * time: time (use GDK_CURRENT_TIME only if necessary)
297 *************************************************************/
300 gtk_selection_owner_set (GtkWidget *widget,
305 GtkWidget *old_owner;
306 GtkSelectionInfo *selection_info = NULL;
313 if (!GTK_WIDGET_REALIZED (widget))
314 gtk_widget_realize (widget);
316 window = widget->window;
319 tmp_list = current_selections;
322 selection_info = (GtkSelectionInfo *)tmp_list->data;
324 if (selection_info->selection == selection)
327 tmp_list = tmp_list->next;
330 if (tmp_list == NULL)
331 selection_info = NULL;
333 if (selection_info->widget == widget)
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_append (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, unless we're setting
371 * the owner to None, in which case an event will be sent */
372 if (old_owner && (widget != NULL))
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_add_target (GtkWidget *widget,
470 g_return_if_fail (widget != NULL);
472 list = gtk_selection_target_list_get (widget, selection);
473 gtk_target_list_add (list, target, 0, info);
477 gtk_selection_add_targets (GtkWidget *widget,
479 const GtkTargetEntry *targets,
484 g_return_if_fail (widget != NULL);
485 g_return_if_fail (targets != NULL);
487 list = gtk_selection_target_list_get (widget, selection);
488 gtk_target_list_add_table (list, targets, ntargets);
491 /*************************************************************
492 * gtk_selection_remove_all:
493 * Removes all handlers and unsets ownership of all
494 * selections for a widget. Called when widget is being
500 *************************************************************/
503 gtk_selection_remove_all (GtkWidget *widget)
507 GtkSelectionInfo *selection_info;
509 /* Remove pending requests/incrs for this widget */
511 tmp_list = current_incrs;
514 next = tmp_list->next;
515 if (((GtkIncrInfo *)tmp_list->data)->widget == widget)
517 current_incrs = g_list_remove_link (current_incrs, tmp_list);
518 /* structure will be freed in timeout */
519 g_list_free (tmp_list);
524 tmp_list = current_retrievals;
527 next = tmp_list->next;
528 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
530 current_retrievals = g_list_remove_link (current_retrievals,
532 /* structure will be freed in timeout */
533 g_list_free (tmp_list);
538 /* Disclaim ownership of any selections */
540 tmp_list = current_selections;
543 next = tmp_list->next;
544 selection_info = (GtkSelectionInfo *)tmp_list->data;
546 if (selection_info->widget == widget)
548 gdk_selection_owner_set (NULL,
549 selection_info->selection,
550 GDK_CURRENT_TIME, FALSE);
551 current_selections = g_list_remove_link (current_selections,
553 g_list_free (tmp_list);
554 g_free (selection_info);
560 /* Remove all selection lists */
561 gtk_selection_target_list_remove (widget);
564 /*************************************************************
565 * gtk_selection_convert:
566 * Request the contents of a selection. When received,
567 * a "selection_received" signal will be generated.
570 * widget: The widget which acts as requestor
571 * selection: Which selection to get
572 * target: Form of information desired (e.g., STRING)
573 * time: Time of request (usually of triggering event)
574 * In emergency, you could use GDK_CURRENT_TIME
577 * TRUE if requested succeeded. FALSE if we could not process
578 * request. (e.g., there was already a request in process for
580 *************************************************************/
583 gtk_selection_convert (GtkWidget *widget,
588 GtkRetrievalInfo *info;
590 GdkWindow *owner_window;
592 g_return_val_if_fail (widget != NULL, FALSE);
595 gtk_selection_init ();
597 if (!GTK_WIDGET_REALIZED (widget))
598 gtk_widget_realize (widget);
600 /* Check to see if there are already any retrievals in progress for
601 this widget. If we changed GDK to use the selection for the
602 window property in which to store the retrieved information, then
603 we could support multiple retrievals for different selections.
604 This might be useful for DND. */
606 tmp_list = current_retrievals;
609 info = (GtkRetrievalInfo *)tmp_list->data;
610 if (info->widget == widget)
612 tmp_list = tmp_list->next;
615 info = g_new (GtkRetrievalInfo, 1);
617 info->widget = widget;
618 info->selection = selection;
619 info->target = target;
623 /* Check if this process has current owner. If so, call handler
624 procedure directly to avoid deadlocks with INCR. */
626 owner_window = gdk_selection_owner_get (selection);
628 if (owner_window != NULL)
630 GtkWidget *owner_widget;
631 GtkSelectionData selection_data;
633 selection_data.selection = selection;
634 selection_data.target = target;
635 selection_data.data = NULL;
636 selection_data.length = -1;
638 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
640 if (owner_widget != NULL)
642 gtk_selection_invoke_handler (owner_widget,
646 gtk_selection_retrieval_report (info,
648 selection_data.format,
650 selection_data.length,
653 g_free (selection_data.data);
660 /* Otherwise, we need to go through X */
662 current_retrievals = g_list_append (current_retrievals, info);
663 gdk_selection_convert (widget->window, selection, target, time);
664 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
669 /*************************************************************
670 * gtk_selection_data_set:
671 * Store new data into a GtkSelectionData object. Should
672 * _only_ by called from a selection handler callback.
673 * Null terminates the stored data.
675 * type: the type of selection data
676 * format: format (number of bits in a unit)
677 * data: pointer to the data (will be copied)
678 * length: length of the data
680 *************************************************************/
683 gtk_selection_data_set (GtkSelectionData *selection_data,
689 if (selection_data->data)
690 g_free (selection_data->data);
692 selection_data->type = type;
693 selection_data->format = format;
697 selection_data->data = g_new (guchar, length+1);
698 memcpy (selection_data->data, data, length);
699 selection_data->data[length] = 0;
703 g_return_if_fail (length <= 0);
706 selection_data->data = NULL;
708 selection_data->data = g_strdup("");
711 selection_data->length = length;
714 /*************************************************************
715 * gtk_selection_init:
716 * Initialize local variables
720 *************************************************************/
723 gtk_selection_init (void)
725 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
726 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
727 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
728 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
731 /*************************************************************
732 * gtk_selection_clear:
733 * Handler for "selection_clear_event"
738 *************************************************************/
741 gtk_selection_clear (GtkWidget *widget,
742 GdkEventSelection *event)
744 /* FIXME: there can be a problem if we change the selection
745 via gtk_selection_owner_set after another client claims
746 the selection, but before we get the notification event.
747 Tk filters based on serial #'s, which aren't retained by
748 GTK. Filtering based on time's will be inherently
749 somewhat unreliable. */
752 GtkSelectionInfo *selection_info = NULL;
754 tmp_list = current_selections;
757 selection_info = (GtkSelectionInfo *)tmp_list->data;
759 if ((selection_info->selection == event->selection) &&
760 (selection_info->widget == widget))
763 tmp_list = tmp_list->next;
768 if (selection_info->time > event->time)
769 return FALSE; /* return FALSE to indicate that
770 * the selection was out of date,
771 * and this clear should be ignored */
774 current_selections = g_list_remove_link (current_selections, tmp_list);
775 g_list_free (tmp_list);
776 g_free (selection_info);
784 /*************************************************************
785 * gtk_selection_request:
786 * Handler for "selection_request_event"
791 *************************************************************/
794 gtk_selection_request (GtkWidget *widget,
795 GdkEventSelection *event)
803 gtk_selection_init ();
805 /* Check if we own selection */
807 tmp_list = current_selections;
810 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
812 if ((selection_info->selection == event->selection) &&
813 (selection_info->widget == widget))
816 tmp_list = tmp_list->next;
819 if (tmp_list == NULL)
822 info = g_new(GtkIncrInfo, 1);
824 info->widget = widget;
825 info->selection = event->selection;
828 /* Create GdkWindow structure for the requestor */
830 info->requestor = gdk_window_lookup (event->requestor);
831 if (!info->requestor)
832 info->requestor = gdk_window_foreign_new (event->requestor);
834 /* Determine conversions we need to perform */
836 if (event->target == gtk_selection_atoms[MULTIPLE])
843 if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
844 0, GTK_SELECTION_MAX_SIZE, FALSE,
845 &type, &format, &length, &mult_atoms))
847 gdk_selection_send_notify (event->requestor, event->selection,
848 event->target, GDK_NONE, event->time);
854 info->num_conversions = length / (2*sizeof (GdkAtom));
855 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
857 for (i=0; i<info->num_conversions; i++)
859 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
860 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
863 else /* only a single conversion */
865 info->conversions = g_new (GtkIncrConversion, 1);
866 info->num_conversions = 1;
867 info->conversions[0].target = event->target;
868 info->conversions[0].property = event->property;
869 mult_atoms = (guchar *)info->conversions;
872 /* Loop through conversions and determine which of these are big
873 enough to require doing them via INCR */
874 for (i=0; i<info->num_conversions; i++)
876 GtkSelectionData data;
879 data.selection = event->selection;
880 data.target = info->conversions[i].target;
884 #ifdef DEBUG_SELECTION
885 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
886 event->selection, info->conversions[i].target,
887 gdk_atom_name(info->conversions[i].target),
888 event->requestor, event->property);
891 gtk_selection_invoke_handler (widget, &data, event->time);
895 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
896 info->conversions[i].property = GDK_NONE;
900 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
902 items = (data.length + data.format/8 - 1) / (data.format/8);
904 if (data.length > GTK_SELECTION_MAX_SIZE)
906 /* Sending via INCR */
908 info->conversions[i].offset = 0;
909 info->conversions[i].data = data;
912 gdk_property_change (info->requestor,
913 info->conversions[i].property,
914 gtk_selection_atoms[INCR],
916 GDK_PROP_MODE_REPLACE,
917 (guchar *)&items, 1);
921 info->conversions[i].offset = -1;
923 gdk_property_change (info->requestor,
924 info->conversions[i].property,
927 GDK_PROP_MODE_REPLACE,
934 /* If we have some INCR's, we need to send the rest of the data in
937 if (info->num_incrs > 0)
939 /* FIXME: this could be dangerous if window doesn't still
942 #ifdef DEBUG_SELECTION
943 g_message ("Starting INCR...");
946 gdk_window_set_events (info->requestor,
947 gdk_window_get_events (info->requestor) |
948 GDK_PROPERTY_CHANGE_MASK);
949 current_incrs = g_list_append (current_incrs, info);
950 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
953 /* If it was a MULTIPLE request, set the property to indicate which
954 conversions succeeded */
955 if (event->target == gtk_selection_atoms[MULTIPLE])
957 gdk_property_change (info->requestor, event->property,
958 GDK_SELECTION_TYPE_ATOM, 8*sizeof(GdkAtom),
959 GDK_PROP_MODE_REPLACE,
960 mult_atoms, 2*info->num_conversions);
964 gdk_selection_send_notify (event->requestor, event->selection, event->target,
965 event->property, event->time);
967 if (info->num_incrs == 0)
969 g_free (info->conversions);
976 /*************************************************************
977 * gtk_selection_incr_event:
978 * Called whenever an PropertyNotify event occurs for an
979 * GdkWindow with user_data == NULL. These will be notifications
980 * that a window we are sending the selection to via the
981 * INCR protocol has deleted a property and is ready for
985 * window: the requestor window
986 * event: the property event structure
989 *************************************************************/
992 gtk_selection_incr_event (GdkWindow *window,
993 GdkEventProperty *event)
996 GtkIncrInfo *info = NULL;
1002 if (event->state != GDK_PROPERTY_DELETE)
1005 #ifdef DEBUG_SELECTION
1006 g_message ("PropertyDelete, property %ld", event->atom);
1009 /* Now find the appropriate ongoing INCR */
1010 tmp_list = current_incrs;
1013 info = (GtkIncrInfo *)tmp_list->data;
1014 if (info->requestor == event->window)
1017 tmp_list = tmp_list->next;
1020 if (tmp_list == NULL)
1023 /* Find out which target this is for */
1024 for (i=0; i<info->num_conversions; i++)
1026 if (info->conversions[i].property == event->atom &&
1027 info->conversions[i].offset != -1)
1029 info->idle_time = 0;
1031 if (info->conversions[i].offset == -2) /* only the last 0-length
1039 num_bytes = info->conversions[i].data.length -
1040 info->conversions[i].offset;
1041 buffer = info->conversions[i].data.data +
1042 info->conversions[i].offset;
1044 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1046 num_bytes = GTK_SELECTION_MAX_SIZE;
1047 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1050 info->conversions[i].offset = -2;
1052 #ifdef DEBUG_SELECTION
1053 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1054 num_bytes, info->conversions[i].offset,
1055 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1057 gdk_property_change (info->requestor, event->atom,
1058 info->conversions[i].data.type,
1059 info->conversions[i].data.format,
1060 GDK_PROP_MODE_REPLACE,
1062 (num_bytes + info->conversions[i].data.format/8 - 1) /
1063 (info->conversions[i].data.format/8));
1065 if (info->conversions[i].offset == -2)
1067 g_free (info->conversions[i].data.data);
1068 info->conversions[i].data.data = NULL;
1074 info->conversions[i].offset = -1;
1080 /* Check if we're finished with all the targets */
1082 if (info->num_incrs == 0)
1084 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1085 g_list_free (tmp_list);
1086 /* Let the timeout free it */
1092 /*************************************************************
1093 * gtk_selection_incr_timeout:
1094 * Timeout callback for the sending portion of the INCR
1097 * info: Information about this incr
1099 *************************************************************/
1102 gtk_selection_incr_timeout (GtkIncrInfo *info)
1107 GDK_THREADS_ENTER ();
1109 /* Determine if retrieval has finished by checking if it still in
1110 list of pending retrievals */
1112 tmp_list = current_incrs;
1115 if (info == (GtkIncrInfo *)tmp_list->data)
1117 tmp_list = tmp_list->next;
1120 /* If retrieval is finished */
1121 if (!tmp_list || info->idle_time >= 5)
1123 if (tmp_list && info->idle_time >= 5)
1125 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1126 g_list_free (tmp_list);
1129 g_free (info->conversions);
1130 /* FIXME: we should check if requestor window is still in use,
1131 and if not, remove it? */
1135 retval = FALSE; /* remove timeout */
1141 retval = TRUE; /* timeout will happen again */
1144 GDK_THREADS_LEAVE ();
1149 /*************************************************************
1150 * gtk_selection_notify:
1151 * Handler for "selection_notify_event" signals on windows
1152 * where a retrieval is currently in process. The selection
1153 * owner has responded to our conversion request.
1155 * widget: Widget getting signal
1156 * event: Selection event structure
1157 * info: Information about this retrieval
1159 * was event handled?
1160 *************************************************************/
1163 gtk_selection_notify (GtkWidget *widget,
1164 GdkEventSelection *event)
1167 GtkRetrievalInfo *info = NULL;
1173 #ifdef DEBUG_SELECTION
1174 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1175 event->selection, event->target, event->property);
1178 tmp_list = current_retrievals;
1181 info = (GtkRetrievalInfo *)tmp_list->data;
1182 if (info->widget == widget && info->selection == event->selection)
1184 tmp_list = tmp_list->next;
1187 if (!tmp_list) /* no retrieval in progress */
1190 if (event->property == GDK_NONE)
1192 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1193 g_list_free (tmp_list);
1194 /* structure will be freed in timeout */
1195 gtk_selection_retrieval_report (info,
1196 GDK_NONE, 0, NULL, -1, event->time);
1201 length = gdk_selection_property_get (widget->window, &buffer,
1204 if (type == gtk_selection_atoms[INCR])
1206 /* The remainder of the selection will come through PropertyNotify
1209 info->notify_time = event->time;
1210 info->idle_time = 0;
1211 info->offset = 0; /* Mark as OK to proceed */
1212 gdk_window_set_events (widget->window,
1213 gdk_window_get_events (widget->window)
1214 | GDK_PROPERTY_CHANGE_MASK);
1218 /* We don't delete the info structure - that will happen in timeout */
1219 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1220 g_list_free (tmp_list);
1222 info->offset = length;
1223 gtk_selection_retrieval_report (info,
1225 buffer, length, event->time);
1228 gdk_property_delete (widget->window, event->property);
1235 /*************************************************************
1236 * gtk_selection_property_notify:
1237 * Handler for "property_notify_event" signals on windows
1238 * where a retrieval is currently in process. The selection
1239 * owner has added more data.
1241 * widget: Widget getting signal
1242 * event: Property event structure
1243 * info: Information about this retrieval
1245 * was event handled?
1246 *************************************************************/
1249 gtk_selection_property_notify (GtkWidget *widget,
1250 GdkEventProperty *event)
1253 GtkRetrievalInfo *info = NULL;
1259 g_return_val_if_fail (widget != NULL, FALSE);
1260 g_return_val_if_fail (event != NULL, FALSE);
1262 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1263 (event->atom != gdk_selection_property)) /* not the right property */
1266 #ifdef DEBUG_SELECTION
1267 g_message ("PropertyNewValue, property %ld",
1271 tmp_list = current_retrievals;
1274 info = (GtkRetrievalInfo *)tmp_list->data;
1275 if (info->widget == widget)
1277 tmp_list = tmp_list->next;
1280 if (!tmp_list) /* No retrieval in progress */
1283 if (info->offset < 0) /* We haven't got the SelectionNotify
1284 for this retrieval yet */
1287 info->idle_time = 0;
1289 length = gdk_selection_property_get (widget->window, &new_buffer,
1291 gdk_property_delete (widget->window, event->atom);
1293 /* We could do a lot better efficiency-wise by paying attention to
1294 what length was sent in the initial INCR transaction, instead of
1295 doing memory allocation at every step. But its only guaranteed to
1296 be a _lower bound_ (pretty useless!) */
1298 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1300 /* Info structure will be freed in timeout */
1301 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1302 g_list_free (tmp_list);
1303 gtk_selection_retrieval_report (info,
1305 (type == GDK_NONE) ? NULL : info->buffer,
1306 (type == GDK_NONE) ? -1 : info->offset,
1309 else /* append on newly arrived data */
1313 #ifdef DEBUG_SELECTION
1314 g_message ("Start - Adding %d bytes at offset 0",
1317 info->buffer = new_buffer;
1318 info->offset = length;
1323 #ifdef DEBUG_SELECTION
1324 g_message ("Appending %d bytes at offset %d",
1325 length,info->offset);
1327 /* We copy length+1 bytes to preserve guaranteed null termination */
1328 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1329 memcpy (info->buffer + info->offset, new_buffer, length+1);
1330 info->offset += length;
1331 g_free (new_buffer);
1338 /*************************************************************
1339 * gtk_selection_retrieval_timeout:
1340 * Timeout callback while receiving a selection.
1342 * info: Information about this retrieval
1344 *************************************************************/
1347 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1352 GDK_THREADS_ENTER ();
1354 /* Determine if retrieval has finished by checking if it still in
1355 list of pending retrievals */
1357 tmp_list = current_retrievals;
1360 if (info == (GtkRetrievalInfo *)tmp_list->data)
1362 tmp_list = tmp_list->next;
1365 /* If retrieval is finished */
1366 if (!tmp_list || info->idle_time >= 5)
1368 if (tmp_list && info->idle_time >= 5)
1370 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1371 g_list_free (tmp_list);
1372 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1375 g_free (info->buffer);
1378 retval = FALSE; /* remove timeout */
1384 retval = TRUE; /* timeout will happen again */
1387 GDK_THREADS_LEAVE ();
1392 /*************************************************************
1393 * gtk_selection_retrieval_report:
1394 * Emits a "selection_received" signal.
1396 * info: information about the retrieval that completed
1397 * buffer: buffer containing data (NULL => errror)
1398 * time: timestamp for data in buffer
1400 *************************************************************/
1403 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1404 GdkAtom type, gint format,
1405 guchar *buffer, gint length,
1408 GtkSelectionData data;
1410 data.selection = info->selection;
1411 data.target = info->target;
1413 data.format = format;
1415 data.length = length;
1418 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1419 "selection_received",
1423 /*************************************************************
1424 * gtk_selection_invoke_handler:
1425 * Finds and invokes handler for specified
1426 * widget/selection/target combination, calls
1427 * gtk_selection_default_handler if none exists.
1430 * widget: selection owner
1431 * data: selection data [INOUT]
1432 * time: time from requeset
1435 * Number of bytes written to buffer, -1 if error
1436 *************************************************************/
1439 gtk_selection_invoke_handler (GtkWidget *widget,
1440 GtkSelectionData *data,
1443 GtkTargetList *target_list;
1447 g_return_if_fail (widget != NULL);
1449 target_list = gtk_selection_target_list_get (widget, data->selection);
1451 gtk_target_list_find (target_list, data->target, &info))
1453 gtk_signal_emit_by_name (GTK_OBJECT (widget),
1459 gtk_selection_default_handler (widget, data);
1462 /*************************************************************
1463 * gtk_selection_default_handler:
1464 * Handles some default targets that exist for any widget
1465 * If it can't fit results into buffer, returns -1. This
1466 * won't happen in any conceivable case, since it would
1467 * require 1000 selection targets!
1470 * widget: selection owner
1471 * data: selection data [INOUT]
1473 *************************************************************/
1476 gtk_selection_default_handler (GtkWidget *widget,
1477 GtkSelectionData *data)
1479 if (data->target == gtk_selection_atoms[TIMESTAMP])
1481 /* Time which was used to obtain selection */
1483 GtkSelectionInfo *selection_info;
1485 tmp_list = current_selections;
1488 selection_info = (GtkSelectionInfo *)tmp_list->data;
1489 if ((selection_info->widget == widget) &&
1490 (selection_info->selection == data->selection))
1492 gtk_selection_data_set (data,
1493 GDK_SELECTION_TYPE_INTEGER,
1495 (guchar *)&selection_info->time,
1500 tmp_list = tmp_list->next;
1505 else if (data->target == gtk_selection_atoms[TARGETS])
1507 /* List of all targets supported for this widget/selection pair */
1511 GtkTargetList *target_list;
1512 GtkTargetPair *pair;
1514 target_list = gtk_selection_target_list_get (widget,
1516 count = g_list_length (target_list->list) + 3;
1518 data->type = GDK_SELECTION_TYPE_ATOM;
1519 data->format = 8*sizeof (GdkAtom);
1520 data->length = count * sizeof (GdkAtom);
1522 p = g_new (GdkAtom, count);
1523 data->data = (guchar *)p;
1525 *p++ = gtk_selection_atoms[TIMESTAMP];
1526 *p++ = gtk_selection_atoms[TARGETS];
1527 *p++ = gtk_selection_atoms[MULTIPLE];
1529 tmp_list = target_list->list;
1532 pair = (GtkTargetPair *)tmp_list->data;
1533 *p++ = pair->target;
1535 tmp_list = tmp_list->next;
1546 gtk_selection_data_copy (GtkSelectionData *data)
1548 GtkSelectionData *new_data;
1550 g_return_val_if_fail (data != NULL, NULL);
1552 new_data = g_new (GtkSelectionData, 1);
1559 gtk_selection_data_free (GtkSelectionData *data)
1561 g_return_if_fail (data != NULL);