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 ... */
50 /* we need this for gdk_window_lookup() */
52 #include "gtkprivate.h"
53 #include "gtkselection.h"
54 #include "gtksignal.h"
56 /* #define DEBUG_SELECTION */
58 /* Maximum size of a sent chunk, in bytes. Also the default size of
60 #define GTK_SELECTION_MAX_SIZE 4000
70 typedef struct _GtkSelectionInfo GtkSelectionInfo;
71 typedef struct _GtkIncrConversion GtkIncrConversion;
72 typedef struct _GtkIncrInfo GtkIncrInfo;
73 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
75 struct _GtkSelectionInfo
78 GtkWidget *widget; /* widget that owns selection */
79 guint32 time; /* time used to acquire selection */
82 struct _GtkIncrConversion
84 GdkAtom target; /* Requested target */
85 GdkAtom property; /* Property to store in */
86 GtkSelectionData data; /* The data being supplied */
87 gint offset; /* Current offset in sent selection.
89 * -2 => Only the final (empty) portion
95 GtkWidget *widget; /* Selection owner */
96 GdkWindow *requestor; /* Requestor window - we create a GdkWindow
97 so we can receive events */
98 GdkAtom selection; /* Selection we're sending */
100 GtkIncrConversion *conversions; /* Information about requested conversions -
101 * With MULTIPLE requests (benighted 1980's
102 * hardware idea), there can be more than
104 gint num_conversions;
105 gint num_incrs; /* number of remaining INCR style transactions */
110 struct _GtkRetrievalInfo
113 GdkAtom selection; /* Selection being retrieved. */
114 GdkAtom target; /* Form of selection that we requested */
115 guint32 idle_time; /* Number of seconds since we last heard
116 from selection owner */
117 guchar *buffer; /* Buffer in which to accumulate results */
118 gint offset; /* Current offset in buffer, -1 indicates
120 guint32 notify_time; /* Timestamp from SelectionNotify */
123 /* Local Functions */
124 static void gtk_selection_init (void);
125 static gint gtk_selection_incr_timeout (GtkIncrInfo *info);
126 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
127 static void gtk_selection_retrieval_report (GtkRetrievalInfo *info,
128 GdkAtom type, gint format,
129 guchar *buffer, gint length,
131 static void gtk_selection_invoke_handler (GtkWidget *widget,
132 GtkSelectionData *data,
134 static void gtk_selection_default_handler (GtkWidget *widget,
135 GtkSelectionData *data);
138 static gint initialize = TRUE;
139 static GList *current_retrievals = NULL;
140 static GList *current_incrs = NULL;
141 static GList *current_selections = NULL;
143 static GdkAtom gtk_selection_atoms[LAST_ATOM];
144 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
155 gtk_target_list_new (GtkTargetEntry *targets,
158 GtkTargetList *result = g_new (GtkTargetList, 1);
160 result->ref_count = 1;
163 gtk_target_list_add_table (result, targets, ntargets);
169 gtk_target_list_ref (GtkTargetList *list)
175 gtk_target_list_unref (GtkTargetList *list)
178 if (list->ref_count == 0)
180 GList *tmp_list = list->list;
183 GtkTargetEntry *entry = tmp_list->data;
186 tmp_list = tmp_list->next;
192 gtk_target_list_add (GtkTargetList *list,
199 g_return_if_fail (list != NULL);
201 pair = g_new (GtkTargetPair, 1);
202 pair->target = target;
206 list->list = g_list_append (list->list, pair);
210 gtk_target_list_add_table (GtkTargetList *list,
211 GtkTargetEntry *targets,
216 for (i=ntargets-1; i >= 0; i--)
218 GtkTargetPair *pair = g_new (GtkTargetPair, 1);
219 pair->target = gdk_atom_intern (targets[i].target, FALSE);
220 pair->flags = targets[i].flags;
221 pair->info = targets[i].info;
223 list->list = g_list_prepend (list->list, pair);
228 gtk_target_list_remove (GtkTargetList *list,
233 g_return_if_fail (list != NULL);
235 tmp_list = list->list;
238 GtkTargetPair *pair = tmp_list->data;
240 if (pair->target == target)
244 list->list = g_list_remove (list->list, tmp_list);
245 g_list_free_1 (tmp_list);
250 tmp_list = tmp_list->next;
255 gtk_target_list_find (GtkTargetList *list,
259 GList *tmp_list = list->list;
262 GtkTargetPair *pair = tmp_list->data;
264 if (pair->target == target)
269 tmp_list = tmp_list->next;
276 /*************************************************************
277 * gtk_selection_owner_set:
278 * Claim ownership of a selection.
280 * widget: new selection owner
281 * selection: which selection
282 * time: time (use GDK_CURRENT_TIME only if necessary)
285 *************************************************************/
288 gtk_selection_owner_set (GtkWidget *widget,
293 GtkWidget *old_owner;
294 GtkSelectionInfo *selection_info;
301 if (!GTK_WIDGET_REALIZED (widget))
302 gtk_widget_realize (widget);
304 window = widget->window;
307 tmp_list = current_selections;
310 selection_info = (GtkSelectionInfo *)tmp_list->data;
312 if (selection_info->selection == selection)
315 tmp_list = tmp_list->next;
318 if (tmp_list == NULL)
319 selection_info = NULL;
321 if (selection_info->widget == widget)
324 if (gdk_selection_owner_set (window, selection, time, TRUE))
332 old_owner = selection_info->widget;
333 current_selections = g_list_remove_link (current_selections,
335 g_list_free (tmp_list);
336 g_free (selection_info);
341 if (selection_info == NULL)
343 selection_info = g_new (GtkSelectionInfo, 1);
344 selection_info->selection = selection;
345 selection_info->widget = widget;
346 selection_info->time = time;
347 current_selections = g_list_append (current_selections,
352 old_owner = selection_info->widget;
353 selection_info->widget = widget;
354 selection_info->time = time;
357 /* If another widget in the application lost the selection,
358 * send it a GDK_SELECTION_CLEAR event, unless we're setting
359 * the owner to None, in which case an event will be sent */
360 if (old_owner && (widget != NULL))
362 GdkEventSelection event;
364 event.type = GDK_SELECTION_CLEAR;
365 event.window = old_owner->window;
366 event.selection = selection;
369 gtk_widget_event (old_owner, (GdkEvent *) &event);
377 /*************************************************************
378 * gtk_selection_add_target
379 * Add specified target to list of supported targets
382 * widget: The widget for which this target applies
385 * info: guint to pass to to the selection_get signal
388 *************************************************************/
390 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
392 struct _GtkSelectionTargetList {
397 static GtkTargetList *
398 gtk_selection_target_list_get (GtkWidget *widget,
401 GtkSelectionTargetList *sellist;
405 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
410 sellist = tmp_list->data;
411 if (sellist->selection == selection)
412 return sellist->list;
413 tmp_list = tmp_list->next;
416 sellist = g_new (GtkSelectionTargetList, 1);
417 sellist->selection = selection;
418 sellist->list = gtk_target_list_new (NULL, 0);
420 lists = g_list_prepend (lists, sellist);
421 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
423 return sellist->list;
427 gtk_selection_target_list_remove (GtkWidget *widget)
429 GtkSelectionTargetList *sellist;
433 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
438 sellist = tmp_list->data;
440 gtk_target_list_unref (sellist->list);
443 tmp_list = tmp_list->next;
447 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL);
451 gtk_selection_add_target (GtkWidget *widget,
458 g_return_if_fail (widget != NULL);
460 list = gtk_selection_target_list_get (widget, selection);
461 gtk_target_list_add (list, target, 0, info);
465 gtk_selection_add_targets (GtkWidget *widget,
467 GtkTargetEntry *targets,
472 g_return_if_fail (widget != NULL);
473 g_return_if_fail (targets != NULL);
475 list = gtk_selection_target_list_get (widget, selection);
476 gtk_target_list_add_table (list, targets, ntargets);
479 /*************************************************************
480 * gtk_selection_remove_all:
481 * Removes all handlers and unsets ownership of all
482 * selections for a widget. Called when widget is being
488 *************************************************************/
491 gtk_selection_remove_all (GtkWidget *widget)
495 GtkSelectionInfo *selection_info;
497 /* Remove pending requests/incrs for this widget */
499 tmp_list = current_incrs;
502 next = tmp_list->next;
503 if (((GtkIncrInfo *)tmp_list->data)->widget == widget)
505 current_incrs = g_list_remove_link (current_incrs, tmp_list);
506 /* structure will be freed in timeout */
507 g_list_free (tmp_list);
512 tmp_list = current_retrievals;
515 next = tmp_list->next;
516 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
518 current_retrievals = g_list_remove_link (current_retrievals,
520 /* structure will be freed in timeout */
521 g_list_free (tmp_list);
526 /* Disclaim ownership of any selections */
528 tmp_list = current_selections;
531 next = tmp_list->next;
532 selection_info = (GtkSelectionInfo *)tmp_list->data;
534 if (selection_info->widget == widget)
536 gdk_selection_owner_set (NULL,
537 selection_info->selection,
538 GDK_CURRENT_TIME, FALSE);
539 current_selections = g_list_remove_link (current_selections,
541 g_list_free (tmp_list);
542 g_free (selection_info);
548 /* Remove all selection lists */
549 gtk_selection_target_list_remove (widget);
552 /*************************************************************
553 * gtk_selection_convert:
554 * Request the contents of a selection. When received,
555 * a "selection_received" signal will be generated.
558 * widget: The widget which acts as requestor
559 * selection: Which selection to get
560 * target: Form of information desired (e.g., STRING)
561 * time: Time of request (usually of triggering event)
562 * In emergency, you could use GDK_CURRENT_TIME
565 * TRUE if requested succeeded. FALSE if we could not process
566 * request. (e.g., there was already a request in process for
568 *************************************************************/
571 gtk_selection_convert (GtkWidget *widget,
576 GtkRetrievalInfo *info;
578 GdkWindow *owner_window;
580 g_return_val_if_fail (widget != NULL, FALSE);
583 gtk_selection_init ();
585 if (!GTK_WIDGET_REALIZED (widget))
586 gtk_widget_realize (widget);
588 /* Check to see if there are already any retrievals in progress for
589 this widget. If we changed GDK to use the selection for the
590 window property in which to store the retrieved information, then
591 we could support multiple retrievals for different selections.
592 This might be useful for DND. */
594 tmp_list = current_retrievals;
597 info = (GtkRetrievalInfo *)tmp_list->data;
598 if (info->widget == widget)
600 tmp_list = tmp_list->next;
603 info = g_new (GtkRetrievalInfo, 1);
605 info->widget = widget;
606 info->selection = selection;
607 info->target = target;
611 /* Check if this process has current owner. If so, call handler
612 procedure directly to avoid deadlocks with INCR. */
614 owner_window = gdk_selection_owner_get (selection);
616 if (owner_window != NULL)
618 GtkWidget *owner_widget;
619 GtkSelectionData selection_data;
621 selection_data.selection = selection;
622 selection_data.target = target;
623 selection_data.data = NULL;
624 selection_data.length = -1;
626 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
628 if (owner_widget != NULL)
630 gtk_selection_invoke_handler (owner_widget,
634 gtk_selection_retrieval_report (info,
636 selection_data.format,
638 selection_data.length,
641 g_free (selection_data.data);
648 /* Otherwise, we need to go through X */
650 current_retrievals = g_list_append (current_retrievals, info);
651 gdk_selection_convert (widget->window, selection, target, time);
652 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
657 /*************************************************************
658 * gtk_selection_data_set:
659 * Store new data into a GtkSelectionData object. Should
660 * _only_ by called from a selection handler callback.
661 * Null terminates the stored data.
663 * type: the type of selection data
664 * format: format (number of bits in a unit)
665 * data: pointer to the data (will be copied)
666 * length: length of the data
668 *************************************************************/
671 gtk_selection_data_set (GtkSelectionData *selection_data,
677 if (selection_data->data)
678 g_free (selection_data->data);
680 selection_data->type = type;
681 selection_data->format = format;
685 selection_data->data = g_new (guchar, length+1);
686 memcpy (selection_data->data, data, length);
687 selection_data->data[length] = 0;
691 g_return_if_fail (length <= 0);
694 selection_data->data = NULL;
696 selection_data->data = g_strdup("");
699 selection_data->length = length;
702 /*************************************************************
703 * gtk_selection_init:
704 * Initialize local variables
708 *************************************************************/
711 gtk_selection_init (void)
713 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
714 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
715 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
716 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
719 /*************************************************************
720 * gtk_selection_clear:
721 * Handler for "selection_clear_event"
726 *************************************************************/
729 gtk_selection_clear (GtkWidget *widget,
730 GdkEventSelection *event)
732 /* FIXME: there can be a problem if we change the selection
733 via gtk_selection_owner_set after another client claims
734 the selection, but before we get the notification event.
735 Tk filters based on serial #'s, which aren't retained by
736 GTK. Filtering based on time's will be inherently
737 somewhat unreliable. */
740 GtkSelectionInfo *selection_info;
742 tmp_list = current_selections;
745 selection_info = (GtkSelectionInfo *)tmp_list->data;
747 if ((selection_info->selection == event->selection) &&
748 (selection_info->widget == widget))
751 tmp_list = tmp_list->next;
756 if (selection_info->time > event->time)
757 return FALSE; /* return FALSE to indicate that
758 * the selection was out of date,
759 * and this clear should be ignored */
762 current_selections = g_list_remove_link (current_selections, tmp_list);
763 g_list_free (tmp_list);
764 g_free (selection_info);
772 /*************************************************************
773 * gtk_selection_request:
774 * Handler for "selection_request_event"
779 *************************************************************/
782 gtk_selection_request (GtkWidget *widget,
783 GdkEventSelection *event)
791 gtk_selection_init ();
793 /* Check if we own selection */
795 tmp_list = current_selections;
798 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
800 if ((selection_info->selection == event->selection) &&
801 (selection_info->widget == widget))
804 tmp_list = tmp_list->next;
807 if (tmp_list == NULL)
810 info = g_new(GtkIncrInfo, 1);
812 info->widget = widget;
813 info->selection = event->selection;
816 /* Create GdkWindow structure for the requestor */
818 info->requestor = gdk_window_lookup (event->requestor);
819 if (!info->requestor)
820 info->requestor = gdk_window_foreign_new (event->requestor);
822 /* Determine conversions we need to perform */
824 if (event->target == gtk_selection_atoms[MULTIPLE])
831 if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
832 0, GTK_SELECTION_MAX_SIZE, FALSE,
833 &type, &format, &length, &mult_atoms))
835 gdk_selection_send_notify (event->requestor, event->selection,
836 event->target, GDK_NONE, event->time);
842 info->num_conversions = length / (2*sizeof (GdkAtom));
843 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
845 for (i=0; i<info->num_conversions; i++)
847 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
848 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
851 else /* only a single conversion */
853 info->conversions = g_new (GtkIncrConversion, 1);
854 info->num_conversions = 1;
855 info->conversions[0].target = event->target;
856 info->conversions[0].property = event->property;
857 mult_atoms = (guchar *)info->conversions;
860 /* Loop through conversions and determine which of these are big
861 enough to require doing them via INCR */
862 for (i=0; i<info->num_conversions; i++)
864 GtkSelectionData data;
867 data.selection = event->selection;
868 data.target = info->conversions[i].target;
872 #ifdef DEBUG_SELECTION
873 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
874 event->selection, info->conversions[i].target,
875 gdk_atom_name(info->conversions[i].target),
876 event->requestor, event->property);
879 gtk_selection_invoke_handler (widget, &data, event->time);
883 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
884 info->conversions[i].property = GDK_NONE;
888 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
890 items = (data.length + data.format/8 - 1) / (data.format/8);
892 if (data.length > GTK_SELECTION_MAX_SIZE)
894 /* Sending via INCR */
896 info->conversions[i].offset = 0;
897 info->conversions[i].data = data;
900 gdk_property_change (info->requestor,
901 info->conversions[i].property,
902 gtk_selection_atoms[INCR],
904 GDK_PROP_MODE_REPLACE,
905 (guchar *)&items, 1);
909 info->conversions[i].offset = -1;
911 gdk_property_change (info->requestor,
912 info->conversions[i].property,
915 GDK_PROP_MODE_REPLACE,
922 /* If we have some INCR's, we need to send the rest of the data in
925 if (info->num_incrs > 0)
927 /* FIXME: this could be dangerous if window doesn't still
930 #ifdef DEBUG_SELECTION
931 g_message ("Starting INCR...");
934 gdk_window_set_events (info->requestor,
935 gdk_window_get_events (info->requestor) |
936 GDK_PROPERTY_CHANGE_MASK);
937 current_incrs = g_list_append (current_incrs, info);
938 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
941 /* If it was a MULTIPLE request, set the property to indicate which
942 conversions succeeded */
943 if (event->target == gtk_selection_atoms[MULTIPLE])
945 gdk_property_change (info->requestor, event->property,
946 GDK_SELECTION_TYPE_ATOM, 8*sizeof(GdkAtom),
947 GDK_PROP_MODE_REPLACE,
948 mult_atoms, 2*info->num_conversions);
952 gdk_selection_send_notify (event->requestor, event->selection, event->target,
953 event->property, event->time);
955 if (info->num_incrs == 0)
957 g_free (info->conversions);
964 /*************************************************************
965 * gtk_selection_incr_event:
966 * Called whenever an PropertyNotify event occurs for an
967 * GdkWindow with user_data == NULL. These will be notifications
968 * that a window we are sending the selection to via the
969 * INCR protocol has deleted a property and is ready for
973 * window: the requestor window
974 * event: the property event structure
977 *************************************************************/
980 gtk_selection_incr_event (GdkWindow *window,
981 GdkEventProperty *event)
990 if (event->state != GDK_PROPERTY_DELETE)
993 #ifdef DEBUG_SELECTION
994 g_message ("PropertyDelete, property %ld", event->atom);
997 /* Now find the appropriate ongoing INCR */
998 tmp_list = current_incrs;
1001 info = (GtkIncrInfo *)tmp_list->data;
1002 if (info->requestor == event->window)
1005 tmp_list = tmp_list->next;
1008 if (tmp_list == NULL)
1011 /* Find out which target this is for */
1012 for (i=0; i<info->num_conversions; i++)
1014 if (info->conversions[i].property == event->atom &&
1015 info->conversions[i].offset != -1)
1017 info->idle_time = 0;
1019 if (info->conversions[i].offset == -2) /* only the last 0-length
1027 num_bytes = info->conversions[i].data.length -
1028 info->conversions[i].offset;
1029 buffer = info->conversions[i].data.data +
1030 info->conversions[i].offset;
1032 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1034 num_bytes = GTK_SELECTION_MAX_SIZE;
1035 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1038 info->conversions[i].offset = -2;
1040 #ifdef DEBUG_SELECTION
1041 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1042 num_bytes, info->conversions[i].offset,
1043 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1045 gdk_property_change (info->requestor, event->atom,
1046 info->conversions[i].data.type,
1047 info->conversions[i].data.format,
1048 GDK_PROP_MODE_REPLACE,
1050 (num_bytes + info->conversions[i].data.format/8 - 1) /
1051 (info->conversions[i].data.format/8));
1053 if (info->conversions[i].offset == -2)
1055 g_free (info->conversions[i].data.data);
1056 info->conversions[i].data.data = NULL;
1062 info->conversions[i].offset = -1;
1068 /* Check if we're finished with all the targets */
1070 if (info->num_incrs == 0)
1072 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1073 g_list_free (tmp_list);
1074 /* Let the timeout free it */
1080 /*************************************************************
1081 * gtk_selection_incr_timeout:
1082 * Timeout callback for the sending portion of the INCR
1085 * info: Information about this incr
1087 *************************************************************/
1090 gtk_selection_incr_timeout (GtkIncrInfo *info)
1097 /* Determine if retrieval has finished by checking if it still in
1098 list of pending retrievals */
1100 tmp_list = current_incrs;
1103 if (info == (GtkIncrInfo *)tmp_list->data)
1105 tmp_list = tmp_list->next;
1108 /* If retrieval is finished */
1109 if (!tmp_list || info->idle_time >= 5)
1111 if (tmp_list && info->idle_time >= 5)
1113 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1114 g_list_free (tmp_list);
1117 g_free (info->conversions);
1118 /* FIXME: we should check if requestor window is still in use,
1119 and if not, remove it? */
1123 retval = FALSE; /* remove timeout */
1129 retval = TRUE; /* timeout will happen again */
1137 /*************************************************************
1138 * gtk_selection_notify:
1139 * Handler for "selection_notify_event" signals on windows
1140 * where a retrieval is currently in process. The selection
1141 * owner has responded to our conversion request.
1143 * widget: Widget getting signal
1144 * event: Selection event structure
1145 * info: Information about this retrieval
1147 * was event handled?
1148 *************************************************************/
1151 gtk_selection_notify (GtkWidget *widget,
1152 GdkEventSelection *event)
1155 GtkRetrievalInfo *info;
1161 #ifdef DEBUG_SELECTION
1162 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1163 event->selection, event->target, event->property);
1166 tmp_list = current_retrievals;
1169 info = (GtkRetrievalInfo *)tmp_list->data;
1170 if (info->widget == widget && info->selection == event->selection)
1172 tmp_list = tmp_list->next;
1175 if (!tmp_list) /* no retrieval in progress */
1178 if (event->property == GDK_NONE)
1180 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1181 g_list_free (tmp_list);
1182 /* structure will be freed in timeout */
1183 gtk_selection_retrieval_report (info,
1184 GDK_NONE, 0, NULL, -1, event->time);
1189 length = gdk_selection_property_get (widget->window, &buffer,
1192 if (type == gtk_selection_atoms[INCR])
1194 /* The remainder of the selection will come through PropertyNotify
1197 info->notify_time = event->time;
1198 info->idle_time = 0;
1199 info->offset = 0; /* Mark as OK to proceed */
1200 gdk_window_set_events (widget->window,
1201 gdk_window_get_events (widget->window)
1202 | GDK_PROPERTY_CHANGE_MASK);
1206 /* We don't delete the info structure - that will happen in timeout */
1207 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1208 g_list_free (tmp_list);
1210 info->offset = length;
1211 gtk_selection_retrieval_report (info,
1213 buffer, length, event->time);
1216 gdk_property_delete (widget->window, event->property);
1223 /*************************************************************
1224 * gtk_selection_property_notify:
1225 * Handler for "property_notify_event" signals on windows
1226 * where a retrieval is currently in process. The selection
1227 * owner has added more data.
1229 * widget: Widget getting signal
1230 * event: Property event structure
1231 * info: Information about this retrieval
1233 * was event handled?
1234 *************************************************************/
1237 gtk_selection_property_notify (GtkWidget *widget,
1238 GdkEventProperty *event)
1241 GtkRetrievalInfo *info;
1247 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1248 (event->atom != gdk_selection_property)) /* not the right property */
1251 #ifdef DEBUG_SELECTION
1252 g_message ("PropertyNewValue, property %ld",
1256 tmp_list = current_retrievals;
1259 info = (GtkRetrievalInfo *)tmp_list->data;
1260 if (info->widget == widget)
1262 tmp_list = tmp_list->next;
1265 if (!tmp_list) /* No retrieval in progress */
1268 if (info->offset < 0) /* We haven't got the SelectionNotify
1269 for this retrieval yet */
1272 info->idle_time = 0;
1274 length = gdk_selection_property_get (widget->window, &new_buffer,
1276 gdk_property_delete (widget->window, event->atom);
1278 /* We could do a lot better efficiency-wise by paying attention to
1279 what length was sent in the initial INCR transaction, instead of
1280 doing memory allocation at every step. But its only guaranteed to
1281 be a _lower bound_ (pretty useless!) */
1283 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1285 /* Info structure will be freed in timeout */
1286 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1287 g_list_free (tmp_list);
1288 gtk_selection_retrieval_report (info,
1290 (type == GDK_NONE) ? NULL : info->buffer,
1291 (type == GDK_NONE) ? -1 : info->offset,
1294 else /* append on newly arrived data */
1298 #ifdef DEBUG_SELECTION
1299 g_message ("Start - Adding %d bytes at offset 0",
1302 info->buffer = new_buffer;
1303 info->offset = length;
1308 #ifdef DEBUG_SELECTION
1309 g_message ("Appending %d bytes at offset %d",
1310 length,info->offset);
1312 /* We copy length+1 bytes to preserve guaranteed null termination */
1313 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1314 memcpy (info->buffer + info->offset, new_buffer, length+1);
1315 info->offset += length;
1316 g_free (new_buffer);
1323 /*************************************************************
1324 * gtk_selection_retrieval_timeout:
1325 * Timeout callback while receiving a selection.
1327 * info: Information about this retrieval
1329 *************************************************************/
1332 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1339 /* Determine if retrieval has finished by checking if it still in
1340 list of pending retrievals */
1342 tmp_list = current_retrievals;
1345 if (info == (GtkRetrievalInfo *)tmp_list->data)
1347 tmp_list = tmp_list->next;
1350 /* If retrieval is finished */
1351 if (!tmp_list || info->idle_time >= 5)
1353 if (tmp_list && info->idle_time >= 5)
1355 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1356 g_list_free (tmp_list);
1357 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1360 g_free (info->buffer);
1363 retval = FALSE; /* remove timeout */
1369 retval = TRUE; /* timeout will happen again */
1377 /*************************************************************
1378 * gtk_selection_retrieval_report:
1379 * Emits a "selection_received" signal.
1381 * info: information about the retrieval that completed
1382 * buffer: buffer containing data (NULL => errror)
1383 * time: timestamp for data in buffer
1385 *************************************************************/
1388 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1389 GdkAtom type, gint format,
1390 guchar *buffer, gint length,
1393 GtkSelectionData data;
1395 data.selection = info->selection;
1396 data.target = info->target;
1398 data.format = format;
1400 data.length = length;
1403 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1404 "selection_received",
1408 /*************************************************************
1409 * gtk_selection_invoke_handler:
1410 * Finds and invokes handler for specified
1411 * widget/selection/target combination, calls
1412 * gtk_selection_default_handler if none exists.
1415 * widget: selection owner
1416 * data: selection data [INOUT]
1417 * time: time from requeset
1420 * Number of bytes written to buffer, -1 if error
1421 *************************************************************/
1424 gtk_selection_invoke_handler (GtkWidget *widget,
1425 GtkSelectionData *data,
1428 GtkTargetList *target_list;
1432 g_return_if_fail (widget != NULL);
1434 target_list = gtk_selection_target_list_get (widget, data->selection);
1436 gtk_target_list_find (target_list, data->target, &info))
1438 gtk_signal_emit_by_name (GTK_OBJECT (widget),
1444 gtk_selection_default_handler (widget, data);
1447 /*************************************************************
1448 * gtk_selection_default_handler:
1449 * Handles some default targets that exist for any widget
1450 * If it can't fit results into buffer, returns -1. This
1451 * won't happen in any conceivable case, since it would
1452 * require 1000 selection targets!
1455 * widget: selection owner
1456 * data: selection data [INOUT]
1458 *************************************************************/
1461 gtk_selection_default_handler (GtkWidget *widget,
1462 GtkSelectionData *data)
1464 if (data->target == gtk_selection_atoms[TIMESTAMP])
1466 /* Time which was used to obtain selection */
1468 GtkSelectionInfo *selection_info;
1470 tmp_list = current_selections;
1473 selection_info = (GtkSelectionInfo *)tmp_list->data;
1474 if ((selection_info->widget == widget) &&
1475 (selection_info->selection == data->selection))
1477 gtk_selection_data_set (data,
1478 GDK_SELECTION_TYPE_INTEGER,
1480 (guchar *)&selection_info->time,
1485 tmp_list = tmp_list->next;
1490 else if (data->target == gtk_selection_atoms[TARGETS])
1492 /* List of all targets supported for this widget/selection pair */
1496 GtkTargetList *target_list;
1497 GtkTargetPair *pair;
1500 target_list = gtk_selection_target_list_get (widget,
1502 tmp_list = target_list->list;
1506 tmp_list = tmp_list->next;
1509 data->type = GDK_SELECTION_TYPE_ATOM;
1510 data->format = 8*sizeof (GdkAtom);
1511 data->length = count*sizeof (GdkAtom);
1513 p = g_new (GdkAtom, count);
1514 data->data = (guchar *)p;
1516 *p++ = gtk_selection_atoms[TIMESTAMP];
1517 *p++ = gtk_selection_atoms[TARGETS];
1518 *p++ = gtk_selection_atoms[MULTIPLE];
1520 tmp_list = target_list->list;
1523 pair = (GtkTargetPair *)tmp_list->data;
1524 *p++ = pair->target;
1526 tmp_list = tmp_list->next;
1537 gtk_selection_data_copy (GtkSelectionData *data)
1539 GtkSelectionData *new_data;
1541 g_return_val_if_fail (data != NULL, NULL);
1543 new_data = g_new (GtkSelectionData, 1);
1550 gtk_selection_data_free (GtkSelectionData *data)
1552 g_return_if_fail (data != NULL);