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 #define GTK_SELECTION_MAX_SIZE 4000
76 typedef struct _GtkSelectionInfo GtkSelectionInfo;
77 typedef struct _GtkIncrConversion GtkIncrConversion;
78 typedef struct _GtkIncrInfo GtkIncrInfo;
79 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
81 struct _GtkSelectionInfo
84 GtkWidget *widget; /* widget that owns selection */
85 guint32 time; /* time used to acquire selection */
88 struct _GtkIncrConversion
90 GdkAtom target; /* Requested target */
91 GdkAtom property; /* Property to store in */
92 GtkSelectionData data; /* The data being supplied */
93 gint offset; /* Current offset in sent selection.
95 * -2 => Only the final (empty) portion
101 GtkWidget *widget; /* Selection owner */
102 GdkWindow *requestor; /* Requestor window - we create a GdkWindow
103 so we can receive events */
104 GdkAtom selection; /* Selection we're sending */
106 GtkIncrConversion *conversions; /* Information about requested conversions -
107 * With MULTIPLE requests (benighted 1980's
108 * hardware idea), there can be more than
110 gint num_conversions;
111 gint num_incrs; /* number of remaining INCR style transactions */
116 struct _GtkRetrievalInfo
119 GdkAtom selection; /* Selection being retrieved. */
120 GdkAtom target; /* Form of selection that we requested */
121 guint32 idle_time; /* Number of seconds since we last heard
122 from selection owner */
123 guchar *buffer; /* Buffer in which to accumulate results */
124 gint offset; /* Current offset in buffer, -1 indicates
126 guint32 notify_time; /* Timestamp from SelectionNotify */
129 /* Local Functions */
130 static void gtk_selection_init (void);
131 static gint gtk_selection_incr_timeout (GtkIncrInfo *info);
132 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
133 static void gtk_selection_retrieval_report (GtkRetrievalInfo *info,
134 GdkAtom type, gint format,
135 guchar *buffer, gint length,
137 static void gtk_selection_invoke_handler (GtkWidget *widget,
138 GtkSelectionData *data,
140 static void gtk_selection_default_handler (GtkWidget *widget,
141 GtkSelectionData *data);
144 static gint initialize = TRUE;
145 static GList *current_retrievals = NULL;
146 static GList *current_incrs = NULL;
147 static GList *current_selections = NULL;
149 static GdkAtom gtk_selection_atoms[LAST_ATOM];
150 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
161 gtk_target_list_new (const GtkTargetEntry *targets,
164 GtkTargetList *result = g_new (GtkTargetList, 1);
166 result->ref_count = 1;
169 gtk_target_list_add_table (result, targets, ntargets);
175 gtk_target_list_ref (GtkTargetList *list)
181 gtk_target_list_unref (GtkTargetList *list)
184 if (list->ref_count == 0)
186 GList *tmp_list = list->list;
189 GtkTargetPair *pair = tmp_list->data;
192 tmp_list = tmp_list->next;
195 g_list_free (list->list);
201 gtk_target_list_add (GtkTargetList *list,
208 g_return_if_fail (list != NULL);
210 pair = g_new (GtkTargetPair, 1);
211 pair->target = target;
215 list->list = g_list_append (list->list, pair);
219 gtk_target_list_add_table (GtkTargetList *list,
220 const GtkTargetEntry *targets,
225 for (i=ntargets-1; i >= 0; i--)
227 GtkTargetPair *pair = g_new (GtkTargetPair, 1);
228 pair->target = gdk_atom_intern (targets[i].target, FALSE);
229 pair->flags = targets[i].flags;
230 pair->info = targets[i].info;
232 list->list = g_list_prepend (list->list, pair);
237 gtk_target_list_remove (GtkTargetList *list,
242 g_return_if_fail (list != NULL);
244 tmp_list = list->list;
247 GtkTargetPair *pair = tmp_list->data;
249 if (pair->target == target)
253 list->list = g_list_remove (list->list, tmp_list);
254 g_list_free_1 (tmp_list);
259 tmp_list = tmp_list->next;
264 gtk_target_list_find (GtkTargetList *list,
268 GList *tmp_list = list->list;
271 GtkTargetPair *pair = tmp_list->data;
273 if (pair->target == target)
278 tmp_list = tmp_list->next;
285 /*************************************************************
286 * gtk_selection_owner_set:
287 * Claim ownership of a selection.
289 * widget: new selection owner
290 * selection: which selection
291 * time: time (use GDK_CURRENT_TIME only if necessary)
294 *************************************************************/
297 gtk_selection_owner_set (GtkWidget *widget,
302 GtkWidget *old_owner;
303 GtkSelectionInfo *selection_info = NULL;
310 if (!GTK_WIDGET_REALIZED (widget))
311 gtk_widget_realize (widget);
313 window = widget->window;
316 tmp_list = current_selections;
319 selection_info = (GtkSelectionInfo *)tmp_list->data;
321 if (selection_info->selection == selection)
324 tmp_list = tmp_list->next;
327 if (tmp_list == NULL)
328 selection_info = NULL;
330 if (selection_info->widget == widget)
333 if (gdk_selection_owner_set (window, selection, time, TRUE))
341 old_owner = selection_info->widget;
342 current_selections = g_list_remove_link (current_selections,
344 g_list_free (tmp_list);
345 g_free (selection_info);
350 if (selection_info == NULL)
352 selection_info = g_new (GtkSelectionInfo, 1);
353 selection_info->selection = selection;
354 selection_info->widget = widget;
355 selection_info->time = time;
356 current_selections = g_list_append (current_selections,
361 old_owner = selection_info->widget;
362 selection_info->widget = widget;
363 selection_info->time = time;
366 /* If another widget in the application lost the selection,
367 * send it a GDK_SELECTION_CLEAR event, unless we're setting
368 * the owner to None, in which case an event will be sent */
369 if (old_owner && (widget != NULL))
371 GdkEventSelection event;
373 event.type = GDK_SELECTION_CLEAR;
374 event.window = old_owner->window;
375 event.selection = selection;
378 gtk_widget_event (old_owner, (GdkEvent *) &event);
386 /*************************************************************
387 * gtk_selection_add_target
388 * Add specified target to list of supported targets
391 * widget: The widget for which this target applies
394 * info: guint to pass to to the selection_get signal
397 *************************************************************/
399 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
401 struct _GtkSelectionTargetList {
406 static GtkTargetList *
407 gtk_selection_target_list_get (GtkWidget *widget,
410 GtkSelectionTargetList *sellist;
414 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
419 sellist = tmp_list->data;
420 if (sellist->selection == selection)
421 return sellist->list;
422 tmp_list = tmp_list->next;
425 sellist = g_new (GtkSelectionTargetList, 1);
426 sellist->selection = selection;
427 sellist->list = gtk_target_list_new (NULL, 0);
429 lists = g_list_prepend (lists, sellist);
430 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
432 return sellist->list;
436 gtk_selection_target_list_remove (GtkWidget *widget)
438 GtkSelectionTargetList *sellist;
442 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
447 sellist = tmp_list->data;
449 gtk_target_list_unref (sellist->list);
452 tmp_list = tmp_list->next;
456 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL);
460 gtk_selection_add_target (GtkWidget *widget,
467 g_return_if_fail (widget != NULL);
469 list = gtk_selection_target_list_get (widget, selection);
470 gtk_target_list_add (list, target, 0, info);
474 gtk_selection_add_targets (GtkWidget *widget,
476 const GtkTargetEntry *targets,
481 g_return_if_fail (widget != NULL);
482 g_return_if_fail (targets != NULL);
484 list = gtk_selection_target_list_get (widget, selection);
485 gtk_target_list_add_table (list, targets, ntargets);
488 /*************************************************************
489 * gtk_selection_remove_all:
490 * Removes all handlers and unsets ownership of all
491 * selections for a widget. Called when widget is being
497 *************************************************************/
500 gtk_selection_remove_all (GtkWidget *widget)
504 GtkSelectionInfo *selection_info;
506 /* Remove pending requests/incrs for this widget */
508 tmp_list = current_incrs;
511 next = tmp_list->next;
512 if (((GtkIncrInfo *)tmp_list->data)->widget == widget)
514 current_incrs = g_list_remove_link (current_incrs, tmp_list);
515 /* structure will be freed in timeout */
516 g_list_free (tmp_list);
521 tmp_list = current_retrievals;
524 next = tmp_list->next;
525 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
527 current_retrievals = g_list_remove_link (current_retrievals,
529 /* structure will be freed in timeout */
530 g_list_free (tmp_list);
535 /* Disclaim ownership of any selections */
537 tmp_list = current_selections;
540 next = tmp_list->next;
541 selection_info = (GtkSelectionInfo *)tmp_list->data;
543 if (selection_info->widget == widget)
545 gdk_selection_owner_set (NULL,
546 selection_info->selection,
547 GDK_CURRENT_TIME, FALSE);
548 current_selections = g_list_remove_link (current_selections,
550 g_list_free (tmp_list);
551 g_free (selection_info);
557 /* Remove all selection lists */
558 gtk_selection_target_list_remove (widget);
561 /*************************************************************
562 * gtk_selection_convert:
563 * Request the contents of a selection. When received,
564 * a "selection_received" signal will be generated.
567 * widget: The widget which acts as requestor
568 * selection: Which selection to get
569 * target: Form of information desired (e.g., STRING)
570 * time: Time of request (usually of triggering event)
571 * In emergency, you could use GDK_CURRENT_TIME
574 * TRUE if requested succeeded. FALSE if we could not process
575 * request. (e.g., there was already a request in process for
577 *************************************************************/
580 gtk_selection_convert (GtkWidget *widget,
585 GtkRetrievalInfo *info;
587 GdkWindow *owner_window;
589 g_return_val_if_fail (widget != NULL, FALSE);
592 gtk_selection_init ();
594 if (!GTK_WIDGET_REALIZED (widget))
595 gtk_widget_realize (widget);
597 /* Check to see if there are already any retrievals in progress for
598 this widget. If we changed GDK to use the selection for the
599 window property in which to store the retrieved information, then
600 we could support multiple retrievals for different selections.
601 This might be useful for DND. */
603 tmp_list = current_retrievals;
606 info = (GtkRetrievalInfo *)tmp_list->data;
607 if (info->widget == widget)
609 tmp_list = tmp_list->next;
612 info = g_new (GtkRetrievalInfo, 1);
614 info->widget = widget;
615 info->selection = selection;
616 info->target = target;
620 /* Check if this process has current owner. If so, call handler
621 procedure directly to avoid deadlocks with INCR. */
623 owner_window = gdk_selection_owner_get (selection);
625 if (owner_window != NULL)
627 GtkWidget *owner_widget;
628 GtkSelectionData selection_data;
630 selection_data.selection = selection;
631 selection_data.target = target;
632 selection_data.data = NULL;
633 selection_data.length = -1;
635 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
637 if (owner_widget != NULL)
639 gtk_selection_invoke_handler (owner_widget,
643 gtk_selection_retrieval_report (info,
645 selection_data.format,
647 selection_data.length,
650 g_free (selection_data.data);
657 /* Otherwise, we need to go through X */
659 current_retrievals = g_list_append (current_retrievals, info);
660 gdk_selection_convert (widget->window, selection, target, time);
661 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
666 /*************************************************************
667 * gtk_selection_data_set:
668 * Store new data into a GtkSelectionData object. Should
669 * _only_ by called from a selection handler callback.
670 * Null terminates the stored data.
672 * type: the type of selection data
673 * format: format (number of bits in a unit)
674 * data: pointer to the data (will be copied)
675 * length: length of the data
677 *************************************************************/
680 gtk_selection_data_set (GtkSelectionData *selection_data,
686 if (selection_data->data)
687 g_free (selection_data->data);
689 selection_data->type = type;
690 selection_data->format = format;
694 selection_data->data = g_new (guchar, length+1);
695 memcpy (selection_data->data, data, length);
696 selection_data->data[length] = 0;
700 g_return_if_fail (length <= 0);
703 selection_data->data = NULL;
705 selection_data->data = g_strdup("");
708 selection_data->length = length;
711 /*************************************************************
712 * gtk_selection_init:
713 * Initialize local variables
717 *************************************************************/
720 gtk_selection_init (void)
722 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
723 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
724 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
725 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
728 /*************************************************************
729 * gtk_selection_clear:
730 * Handler for "selection_clear_event"
735 *************************************************************/
738 gtk_selection_clear (GtkWidget *widget,
739 GdkEventSelection *event)
741 /* FIXME: there can be a problem if we change the selection
742 via gtk_selection_owner_set after another client claims
743 the selection, but before we get the notification event.
744 Tk filters based on serial #'s, which aren't retained by
745 GTK. Filtering based on time's will be inherently
746 somewhat unreliable. */
749 GtkSelectionInfo *selection_info = NULL;
751 tmp_list = current_selections;
754 selection_info = (GtkSelectionInfo *)tmp_list->data;
756 if ((selection_info->selection == event->selection) &&
757 (selection_info->widget == widget))
760 tmp_list = tmp_list->next;
765 if (selection_info->time > event->time)
766 return FALSE; /* return FALSE to indicate that
767 * the selection was out of date,
768 * and this clear should be ignored */
771 current_selections = g_list_remove_link (current_selections, tmp_list);
772 g_list_free (tmp_list);
773 g_free (selection_info);
781 /*************************************************************
782 * gtk_selection_request:
783 * Handler for "selection_request_event"
788 *************************************************************/
791 gtk_selection_request (GtkWidget *widget,
792 GdkEventSelection *event)
800 gtk_selection_init ();
802 /* Check if we own selection */
804 tmp_list = current_selections;
807 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
809 if ((selection_info->selection == event->selection) &&
810 (selection_info->widget == widget))
813 tmp_list = tmp_list->next;
816 if (tmp_list == NULL)
819 info = g_new(GtkIncrInfo, 1);
821 info->widget = widget;
822 info->selection = event->selection;
825 /* Create GdkWindow structure for the requestor */
827 info->requestor = gdk_window_lookup (event->requestor);
828 if (!info->requestor)
829 info->requestor = gdk_window_foreign_new (event->requestor);
831 /* Determine conversions we need to perform */
833 if (event->target == gtk_selection_atoms[MULTIPLE])
840 if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
841 0, GTK_SELECTION_MAX_SIZE, FALSE,
842 &type, &format, &length, &mult_atoms))
844 gdk_selection_send_notify (event->requestor, event->selection,
845 event->target, GDK_NONE, event->time);
851 info->num_conversions = length / (2*sizeof (GdkAtom));
852 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
854 for (i=0; i<info->num_conversions; i++)
856 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
857 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
860 else /* only a single conversion */
862 info->conversions = g_new (GtkIncrConversion, 1);
863 info->num_conversions = 1;
864 info->conversions[0].target = event->target;
865 info->conversions[0].property = event->property;
866 mult_atoms = (guchar *)info->conversions;
869 /* Loop through conversions and determine which of these are big
870 enough to require doing them via INCR */
871 for (i=0; i<info->num_conversions; i++)
873 GtkSelectionData data;
876 data.selection = event->selection;
877 data.target = info->conversions[i].target;
881 #ifdef DEBUG_SELECTION
882 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
883 event->selection, info->conversions[i].target,
884 gdk_atom_name(info->conversions[i].target),
885 event->requestor, event->property);
888 gtk_selection_invoke_handler (widget, &data, event->time);
892 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
893 info->conversions[i].property = GDK_NONE;
897 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
899 items = (data.length + data.format/8 - 1) / (data.format/8);
901 if (data.length > GTK_SELECTION_MAX_SIZE)
903 /* Sending via INCR */
905 info->conversions[i].offset = 0;
906 info->conversions[i].data = data;
909 gdk_property_change (info->requestor,
910 info->conversions[i].property,
911 gtk_selection_atoms[INCR],
913 GDK_PROP_MODE_REPLACE,
914 (guchar *)&items, 1);
918 info->conversions[i].offset = -1;
920 gdk_property_change (info->requestor,
921 info->conversions[i].property,
924 GDK_PROP_MODE_REPLACE,
931 /* If we have some INCR's, we need to send the rest of the data in
934 if (info->num_incrs > 0)
936 /* FIXME: this could be dangerous if window doesn't still
939 #ifdef DEBUG_SELECTION
940 g_message ("Starting INCR...");
943 gdk_window_set_events (info->requestor,
944 gdk_window_get_events (info->requestor) |
945 GDK_PROPERTY_CHANGE_MASK);
946 current_incrs = g_list_append (current_incrs, info);
947 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
950 /* If it was a MULTIPLE request, set the property to indicate which
951 conversions succeeded */
952 if (event->target == gtk_selection_atoms[MULTIPLE])
954 gdk_property_change (info->requestor, event->property,
955 GDK_SELECTION_TYPE_ATOM, 8*sizeof(GdkAtom),
956 GDK_PROP_MODE_REPLACE,
957 mult_atoms, 2*info->num_conversions);
961 gdk_selection_send_notify (event->requestor, event->selection, event->target,
962 event->property, event->time);
964 if (info->num_incrs == 0)
966 g_free (info->conversions);
973 /*************************************************************
974 * gtk_selection_incr_event:
975 * Called whenever an PropertyNotify event occurs for an
976 * GdkWindow with user_data == NULL. These will be notifications
977 * that a window we are sending the selection to via the
978 * INCR protocol has deleted a property and is ready for
982 * window: the requestor window
983 * event: the property event structure
986 *************************************************************/
989 gtk_selection_incr_event (GdkWindow *window,
990 GdkEventProperty *event)
993 GtkIncrInfo *info = NULL;
999 if (event->state != GDK_PROPERTY_DELETE)
1002 #ifdef DEBUG_SELECTION
1003 g_message ("PropertyDelete, property %ld", event->atom);
1006 /* Now find the appropriate ongoing INCR */
1007 tmp_list = current_incrs;
1010 info = (GtkIncrInfo *)tmp_list->data;
1011 if (info->requestor == event->window)
1014 tmp_list = tmp_list->next;
1017 if (tmp_list == NULL)
1020 /* Find out which target this is for */
1021 for (i=0; i<info->num_conversions; i++)
1023 if (info->conversions[i].property == event->atom &&
1024 info->conversions[i].offset != -1)
1026 info->idle_time = 0;
1028 if (info->conversions[i].offset == -2) /* only the last 0-length
1036 num_bytes = info->conversions[i].data.length -
1037 info->conversions[i].offset;
1038 buffer = info->conversions[i].data.data +
1039 info->conversions[i].offset;
1041 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1043 num_bytes = GTK_SELECTION_MAX_SIZE;
1044 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1047 info->conversions[i].offset = -2;
1049 #ifdef DEBUG_SELECTION
1050 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1051 num_bytes, info->conversions[i].offset,
1052 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1054 gdk_property_change (info->requestor, event->atom,
1055 info->conversions[i].data.type,
1056 info->conversions[i].data.format,
1057 GDK_PROP_MODE_REPLACE,
1059 (num_bytes + info->conversions[i].data.format/8 - 1) /
1060 (info->conversions[i].data.format/8));
1062 if (info->conversions[i].offset == -2)
1064 g_free (info->conversions[i].data.data);
1065 info->conversions[i].data.data = NULL;
1071 info->conversions[i].offset = -1;
1077 /* Check if we're finished with all the targets */
1079 if (info->num_incrs == 0)
1081 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1082 g_list_free (tmp_list);
1083 /* Let the timeout free it */
1089 /*************************************************************
1090 * gtk_selection_incr_timeout:
1091 * Timeout callback for the sending portion of the INCR
1094 * info: Information about this incr
1096 *************************************************************/
1099 gtk_selection_incr_timeout (GtkIncrInfo *info)
1104 GDK_THREADS_ENTER ();
1106 /* Determine if retrieval has finished by checking if it still in
1107 list of pending retrievals */
1109 tmp_list = current_incrs;
1112 if (info == (GtkIncrInfo *)tmp_list->data)
1114 tmp_list = tmp_list->next;
1117 /* If retrieval is finished */
1118 if (!tmp_list || info->idle_time >= 5)
1120 if (tmp_list && info->idle_time >= 5)
1122 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1123 g_list_free (tmp_list);
1126 g_free (info->conversions);
1127 /* FIXME: we should check if requestor window is still in use,
1128 and if not, remove it? */
1132 retval = FALSE; /* remove timeout */
1138 retval = TRUE; /* timeout will happen again */
1141 GDK_THREADS_LEAVE ();
1146 /*************************************************************
1147 * gtk_selection_notify:
1148 * Handler for "selection_notify_event" signals on windows
1149 * where a retrieval is currently in process. The selection
1150 * owner has responded to our conversion request.
1152 * widget: Widget getting signal
1153 * event: Selection event structure
1154 * info: Information about this retrieval
1156 * was event handled?
1157 *************************************************************/
1160 gtk_selection_notify (GtkWidget *widget,
1161 GdkEventSelection *event)
1164 GtkRetrievalInfo *info = NULL;
1170 #ifdef DEBUG_SELECTION
1171 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1172 event->selection, event->target, event->property);
1175 tmp_list = current_retrievals;
1178 info = (GtkRetrievalInfo *)tmp_list->data;
1179 if (info->widget == widget && info->selection == event->selection)
1181 tmp_list = tmp_list->next;
1184 if (!tmp_list) /* no retrieval in progress */
1187 if (event->property == GDK_NONE)
1189 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1190 g_list_free (tmp_list);
1191 /* structure will be freed in timeout */
1192 gtk_selection_retrieval_report (info,
1193 GDK_NONE, 0, NULL, -1, event->time);
1198 length = gdk_selection_property_get (widget->window, &buffer,
1201 if (type == gtk_selection_atoms[INCR])
1203 /* The remainder of the selection will come through PropertyNotify
1206 info->notify_time = event->time;
1207 info->idle_time = 0;
1208 info->offset = 0; /* Mark as OK to proceed */
1209 gdk_window_set_events (widget->window,
1210 gdk_window_get_events (widget->window)
1211 | GDK_PROPERTY_CHANGE_MASK);
1215 /* We don't delete the info structure - that will happen in timeout */
1216 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1217 g_list_free (tmp_list);
1219 info->offset = length;
1220 gtk_selection_retrieval_report (info,
1222 buffer, length, event->time);
1225 gdk_property_delete (widget->window, event->property);
1232 /*************************************************************
1233 * gtk_selection_property_notify:
1234 * Handler for "property_notify_event" signals on windows
1235 * where a retrieval is currently in process. The selection
1236 * owner has added more data.
1238 * widget: Widget getting signal
1239 * event: Property event structure
1240 * info: Information about this retrieval
1242 * was event handled?
1243 *************************************************************/
1246 gtk_selection_property_notify (GtkWidget *widget,
1247 GdkEventProperty *event)
1250 GtkRetrievalInfo *info = NULL;
1256 g_return_val_if_fail (widget != NULL, FALSE);
1257 g_return_val_if_fail (event != NULL, FALSE);
1259 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1260 (event->atom != gdk_selection_property)) /* not the right property */
1263 #ifdef DEBUG_SELECTION
1264 g_message ("PropertyNewValue, property %ld",
1268 tmp_list = current_retrievals;
1271 info = (GtkRetrievalInfo *)tmp_list->data;
1272 if (info->widget == widget)
1274 tmp_list = tmp_list->next;
1277 if (!tmp_list) /* No retrieval in progress */
1280 if (info->offset < 0) /* We haven't got the SelectionNotify
1281 for this retrieval yet */
1284 info->idle_time = 0;
1286 length = gdk_selection_property_get (widget->window, &new_buffer,
1288 gdk_property_delete (widget->window, event->atom);
1290 /* We could do a lot better efficiency-wise by paying attention to
1291 what length was sent in the initial INCR transaction, instead of
1292 doing memory allocation at every step. But its only guaranteed to
1293 be a _lower bound_ (pretty useless!) */
1295 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1297 /* Info structure will be freed in timeout */
1298 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1299 g_list_free (tmp_list);
1300 gtk_selection_retrieval_report (info,
1302 (type == GDK_NONE) ? NULL : info->buffer,
1303 (type == GDK_NONE) ? -1 : info->offset,
1306 else /* append on newly arrived data */
1310 #ifdef DEBUG_SELECTION
1311 g_message ("Start - Adding %d bytes at offset 0",
1314 info->buffer = new_buffer;
1315 info->offset = length;
1320 #ifdef DEBUG_SELECTION
1321 g_message ("Appending %d bytes at offset %d",
1322 length,info->offset);
1324 /* We copy length+1 bytes to preserve guaranteed null termination */
1325 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1326 memcpy (info->buffer + info->offset, new_buffer, length+1);
1327 info->offset += length;
1328 g_free (new_buffer);
1335 /*************************************************************
1336 * gtk_selection_retrieval_timeout:
1337 * Timeout callback while receiving a selection.
1339 * info: Information about this retrieval
1341 *************************************************************/
1344 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1349 GDK_THREADS_ENTER ();
1351 /* Determine if retrieval has finished by checking if it still in
1352 list of pending retrievals */
1354 tmp_list = current_retrievals;
1357 if (info == (GtkRetrievalInfo *)tmp_list->data)
1359 tmp_list = tmp_list->next;
1362 /* If retrieval is finished */
1363 if (!tmp_list || info->idle_time >= 5)
1365 if (tmp_list && info->idle_time >= 5)
1367 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1368 g_list_free (tmp_list);
1369 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1372 g_free (info->buffer);
1375 retval = FALSE; /* remove timeout */
1381 retval = TRUE; /* timeout will happen again */
1384 GDK_THREADS_LEAVE ();
1389 /*************************************************************
1390 * gtk_selection_retrieval_report:
1391 * Emits a "selection_received" signal.
1393 * info: information about the retrieval that completed
1394 * buffer: buffer containing data (NULL => errror)
1395 * time: timestamp for data in buffer
1397 *************************************************************/
1400 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1401 GdkAtom type, gint format,
1402 guchar *buffer, gint length,
1405 GtkSelectionData data;
1407 data.selection = info->selection;
1408 data.target = info->target;
1410 data.format = format;
1412 data.length = length;
1415 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1416 "selection_received",
1420 /*************************************************************
1421 * gtk_selection_invoke_handler:
1422 * Finds and invokes handler for specified
1423 * widget/selection/target combination, calls
1424 * gtk_selection_default_handler if none exists.
1427 * widget: selection owner
1428 * data: selection data [INOUT]
1429 * time: time from requeset
1432 * Number of bytes written to buffer, -1 if error
1433 *************************************************************/
1436 gtk_selection_invoke_handler (GtkWidget *widget,
1437 GtkSelectionData *data,
1440 GtkTargetList *target_list;
1444 g_return_if_fail (widget != NULL);
1446 target_list = gtk_selection_target_list_get (widget, data->selection);
1448 gtk_target_list_find (target_list, data->target, &info))
1450 gtk_signal_emit_by_name (GTK_OBJECT (widget),
1456 gtk_selection_default_handler (widget, data);
1459 /*************************************************************
1460 * gtk_selection_default_handler:
1461 * Handles some default targets that exist for any widget
1462 * If it can't fit results into buffer, returns -1. This
1463 * won't happen in any conceivable case, since it would
1464 * require 1000 selection targets!
1467 * widget: selection owner
1468 * data: selection data [INOUT]
1470 *************************************************************/
1473 gtk_selection_default_handler (GtkWidget *widget,
1474 GtkSelectionData *data)
1476 if (data->target == gtk_selection_atoms[TIMESTAMP])
1478 /* Time which was used to obtain selection */
1480 GtkSelectionInfo *selection_info;
1482 tmp_list = current_selections;
1485 selection_info = (GtkSelectionInfo *)tmp_list->data;
1486 if ((selection_info->widget == widget) &&
1487 (selection_info->selection == data->selection))
1489 gtk_selection_data_set (data,
1490 GDK_SELECTION_TYPE_INTEGER,
1492 (guchar *)&selection_info->time,
1497 tmp_list = tmp_list->next;
1502 else if (data->target == gtk_selection_atoms[TARGETS])
1504 /* List of all targets supported for this widget/selection pair */
1508 GtkTargetList *target_list;
1509 GtkTargetPair *pair;
1511 target_list = gtk_selection_target_list_get (widget,
1513 count = g_list_length (target_list->list) + 3;
1515 data->type = GDK_SELECTION_TYPE_ATOM;
1516 data->format = 8*sizeof (GdkAtom);
1517 data->length = count * sizeof (GdkAtom);
1519 p = g_new (GdkAtom, count);
1520 data->data = (guchar *)p;
1522 *p++ = gtk_selection_atoms[TIMESTAMP];
1523 *p++ = gtk_selection_atoms[TARGETS];
1524 *p++ = gtk_selection_atoms[MULTIPLE];
1526 tmp_list = target_list->list;
1529 pair = (GtkTargetPair *)tmp_list->data;
1530 *p++ = pair->target;
1532 tmp_list = tmp_list->next;
1543 gtk_selection_data_copy (GtkSelectionData *data)
1545 GtkSelectionData *new_data;
1547 g_return_val_if_fail (data != NULL, NULL);
1549 new_data = g_new (GtkSelectionData, 1);
1556 gtk_selection_data_free (GtkSelectionData *data)
1558 g_return_if_fail (data != NULL);