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 "gtkselection.h"
53 #include "gtksignal.h"
55 /* #define DEBUG_SELECTION */
57 /* Maximum size of a sent chunk, in bytes. Also the default size of
59 #define GTK_SELECTION_MAX_SIZE 4000
69 typedef struct _GtkSelectionInfo GtkSelectionInfo;
70 typedef struct _GtkIncrConversion GtkIncrConversion;
71 typedef struct _GtkIncrInfo GtkIncrInfo;
72 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
73 typedef struct _GtkSelectionHandler GtkSelectionHandler;
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
122 struct _GtkSelectionHandler
124 GdkAtom selection; /* selection thats handled */
125 GdkAtom target; /* target thats handled */
126 GtkSelectionFunction function; /* callback function */
127 GtkCallbackMarshal marshal; /* Marshalling function */
128 gpointer data; /* callback data */
129 GtkDestroyNotify destroy; /* called when callback is removed */
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);
139 static void gtk_selection_invoke_handler (GtkWidget *widget,
140 GtkSelectionData *data);
141 static void gtk_selection_default_handler (GtkWidget *widget,
142 GtkSelectionData *data);
145 static gint initialize = TRUE;
146 static GList *current_retrievals = NULL;
147 static GList *current_incrs = NULL;
148 static GList *current_selections = NULL;
150 static GdkAtom gtk_selection_atoms[LAST_ATOM];
151 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
153 /*************************************************************
154 * gtk_selection_owner_set:
155 * Claim ownership of a selection.
157 * widget: new selection owner
158 * selection: which selection
159 * time: time (use GDK_CURRENT_TIME only if necessary)
162 *************************************************************/
165 gtk_selection_owner_set (GtkWidget *widget,
170 GtkWidget *old_owner;
171 GtkSelectionInfo *selection_info;
178 if (!GTK_WIDGET_REALIZED (widget))
179 gtk_widget_realize (widget);
181 window = widget->window;
184 tmp_list = current_selections;
187 selection_info = (GtkSelectionInfo *)tmp_list->data;
189 if (selection_info->selection == selection)
192 tmp_list = tmp_list->next;
195 if (tmp_list == NULL)
196 selection_info = NULL;
198 if (selection_info->widget == widget)
201 if (gdk_selection_owner_set (window, selection, time, TRUE))
209 old_owner = selection_info->widget;
210 current_selections = g_list_remove_link (current_selections,
212 g_list_free (tmp_list);
213 g_free (selection_info);
218 if (selection_info == NULL)
220 selection_info = g_new (GtkSelectionInfo, 1);
221 selection_info->selection = selection;
222 selection_info->widget = widget;
223 selection_info->time = time;
224 current_selections = g_list_append (current_selections,
229 old_owner = selection_info->widget;
230 selection_info->widget = widget;
231 selection_info->time = time;
234 /* If another widget in the application lost the selection,
235 * send it a GDK_SELECTION_CLEAR event, unless we're setting
236 * the owner to None, in which case an event will be sent */
237 if (old_owner && (widget != NULL))
239 GdkEventSelection event;
241 event.type = GDK_SELECTION_CLEAR;
242 event.window = old_owner->window;
243 event.selection = selection;
246 gtk_widget_event (old_owner, (GdkEvent *) &event);
254 /*************************************************************
255 * gtk_selection_add_handler_full:
256 * Add a handler for a specified selection/target pair
259 * widget: The widget the handler applies to
262 * format: Format in which this handler will return data
263 * function: Callback function (can be NULL)
264 * marshal: Callback marshal function
265 * data: User data for callback
266 * destroy: Called when handler removed
269 *************************************************************/
272 gtk_selection_add_handler (GtkWidget *widget,
275 GtkSelectionFunction function,
278 gtk_selection_add_handler_full (widget, selection, target, function,
283 gtk_selection_add_handler_full (GtkWidget *widget,
286 GtkSelectionFunction function,
287 GtkCallbackMarshal marshal,
289 GtkDestroyNotify destroy)
291 GList *selection_handlers;
293 GtkSelectionHandler *handler;
295 g_return_if_fail (widget != NULL);
297 gtk_selection_init ();
299 selection_handlers = gtk_object_get_data (GTK_OBJECT (widget),
300 gtk_selection_handler_key);
302 /* Reuse old handler structure, if present */
303 tmp_list = selection_handlers;
306 handler = (GtkSelectionHandler *)tmp_list->data;
307 if ((handler->selection == selection) && (handler->target == target))
309 if (handler->destroy)
310 (*handler->destroy)(handler->data);
313 handler->function = function;
314 handler->marshal = marshal;
315 handler->data = data;
316 handler->destroy = destroy;
320 selection_handlers = g_list_remove_link (selection_handlers,
322 g_list_free (tmp_list);
327 tmp_list = tmp_list->next;
330 if (tmp_list == NULL && function)
332 handler = g_new (GtkSelectionHandler, 1);
333 handler->selection = selection;
334 handler->target = target;
335 handler->function = function;
336 handler->marshal = marshal;
337 handler->data = data;
338 handler->destroy = destroy;
339 selection_handlers = g_list_append (selection_handlers, handler);
342 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key,
346 /*************************************************************
347 * gtk_selection_remove_all:
348 * Removes all handlers and unsets ownership of all
349 * selections for a widget. Called when widget is being
355 *************************************************************/
358 gtk_selection_remove_all (GtkWidget *widget)
362 GtkSelectionInfo *selection_info;
363 GList *selection_handlers;
364 GtkSelectionHandler *handler;
366 /* Remove pending requests/incrs for this widget */
368 tmp_list = current_incrs;
371 next = tmp_list->next;
372 if (((GtkIncrInfo *)tmp_list->data)->widget == widget)
374 current_incrs = g_list_remove_link (current_incrs, tmp_list);
375 /* structure will be freed in timeout */
376 g_list_free (tmp_list);
381 tmp_list = current_retrievals;
384 next = tmp_list->next;
385 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
387 current_retrievals = g_list_remove_link (current_retrievals,
389 /* structure will be freed in timeout */
390 g_list_free (tmp_list);
395 /* Disclaim ownership of any selections */
397 tmp_list = current_selections;
400 next = tmp_list->next;
401 selection_info = (GtkSelectionInfo *)tmp_list->data;
403 if (selection_info->widget == widget)
405 gdk_selection_owner_set (NULL,
406 selection_info->selection,
407 GDK_CURRENT_TIME, FALSE);
408 current_selections = g_list_remove_link (current_selections,
410 g_list_free (tmp_list);
411 g_free (selection_info);
417 /* Now remove all handlers */
419 selection_handlers = gtk_object_get_data (GTK_OBJECT (widget),
420 gtk_selection_handler_key);
421 gtk_object_remove_data (GTK_OBJECT (widget), gtk_selection_handler_key);
423 tmp_list = selection_handlers;
426 next = tmp_list->next;
427 handler = (GtkSelectionHandler *)tmp_list->data;
429 if (handler->destroy)
430 (*handler->destroy)(handler->data);
437 g_list_free (selection_handlers);
440 /*************************************************************
441 * gtk_selection_convert:
442 * Request the contents of a selection. When received,
443 * a "selection_received" signal will be generated.
446 * widget: The widget which acts as requestor
447 * selection: Which selection to get
448 * target: Form of information desired (e.g., STRING)
449 * time: Time of request (usually of triggering event)
450 * In emergency, you could use GDK_CURRENT_TIME
453 * TRUE if requested succeeded. FALSE if we could not process
454 * request. (e.g., there was already a request in process for
456 *************************************************************/
459 gtk_selection_convert (GtkWidget *widget,
464 GtkRetrievalInfo *info;
466 GdkWindow *owner_window;
468 g_return_val_if_fail (widget != NULL, FALSE);
471 gtk_selection_init ();
473 if (!GTK_WIDGET_REALIZED (widget))
474 gtk_widget_realize (widget);
476 /* Check to see if there are already any retrievals in progress for
477 this widget. If we changed GDK to use the selection for the
478 window property in which to store the retrieved information, then
479 we could support multiple retrievals for different selections.
480 This might be useful for DND. */
482 tmp_list = current_retrievals;
485 info = (GtkRetrievalInfo *)tmp_list->data;
486 if (info->widget == widget)
488 tmp_list = tmp_list->next;
491 info = g_new (GtkRetrievalInfo, 1);
493 info->widget = widget;
494 info->selection = selection;
495 info->target = target;
499 /* Check if this process has current owner. If so, call handler
500 procedure directly to avoid deadlocks with INCR. */
502 owner_window = gdk_selection_owner_get (selection);
504 if (owner_window != NULL)
506 GtkWidget *owner_widget;
507 GtkSelectionData selection_data;
509 selection_data.selection = selection;
510 selection_data.target = target;
511 selection_data.data = NULL;
512 selection_data.length = -1;
514 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
516 if (owner_widget != NULL)
518 gtk_selection_invoke_handler (owner_widget,
521 gtk_selection_retrieval_report (info,
523 selection_data.format,
525 selection_data.length);
527 g_free (selection_data.data);
534 /* Otherwise, we need to go through X */
536 current_retrievals = g_list_append (current_retrievals, info);
537 gdk_selection_convert (widget->window, selection, target, time);
538 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
543 /*************************************************************
544 * gtk_selection_data_set:
545 * Store new data into a GtkSelectionData object. Should
546 * _only_ by called from a selection handler callback.
547 * Null terminates the stored data.
549 * type: the type of selection data
550 * format: format (number of bits in a unit)
551 * data: pointer to the data (will be copied)
552 * length: length of the data
554 *************************************************************/
557 gtk_selection_data_set (GtkSelectionData *selection_data,
563 if (selection_data->data)
564 g_free (selection_data->data);
566 selection_data->type = type;
567 selection_data->format = format;
571 selection_data->data = g_new (guchar, length+1);
572 memcpy (selection_data->data, data, length);
573 selection_data->data[length] = 0;
576 selection_data->data = NULL;
578 selection_data->length = length;
581 /*************************************************************
582 * gtk_selection_init:
583 * Initialize local variables
587 *************************************************************/
590 gtk_selection_init (void)
592 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
593 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
594 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
595 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
598 /*************************************************************
599 * gtk_selection_clear:
600 * Handler for "selection_clear_event"
605 *************************************************************/
608 gtk_selection_clear (GtkWidget *widget,
609 GdkEventSelection *event)
611 /* FIXME: there can be a problem if we change the selection
612 via gtk_selection_owner_set after another client claims
613 the selection, but before we get the notification event.
614 Tk filters based on serial #'s, which aren't retained by
615 GTK. Filtering based on time's will be inherently
616 somewhat unreliable. */
619 GtkSelectionInfo *selection_info;
621 tmp_list = current_selections;
624 selection_info = (GtkSelectionInfo *)tmp_list->data;
626 if ((selection_info->selection == event->selection) &&
627 (selection_info->widget == widget))
630 tmp_list = tmp_list->next;
635 if (selection_info->time > event->time)
636 return FALSE; /* return FALSE to indicate that
637 * the selection was out of date,
638 * and this clear should be ignored */
641 current_selections = g_list_remove_link (current_selections, tmp_list);
642 g_list_free (tmp_list);
643 g_free (selection_info);
651 /*************************************************************
652 * gtk_selection_request:
653 * Handler for "selection_request_event"
658 *************************************************************/
661 gtk_selection_request (GtkWidget *widget,
662 GdkEventSelection *event)
669 /* Check if we own selection */
671 tmp_list = current_selections;
674 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
676 if ((selection_info->selection == event->selection) &&
677 (selection_info->widget == widget))
680 tmp_list = tmp_list->next;
683 if (tmp_list == NULL)
686 info = g_new(GtkIncrInfo, 1);
688 info->widget = widget;
689 info->selection = event->selection;
692 /* Create GdkWindow structure for the requestor */
694 info->requestor = gdk_window_lookup (event->requestor);
695 if (!info->requestor)
696 info->requestor = gdk_window_foreign_new (event->requestor);
698 /* Determine conversions we need to perform */
700 if (event->target == gtk_selection_atoms[MULTIPLE])
707 if (!gdk_property_get (info->requestor, event->property, GDK_SELECTION_TYPE_ATOM,
708 0, GTK_SELECTION_MAX_SIZE, FALSE,
709 &type, &format, &length, &mult_atoms) ||
710 type != GDK_SELECTION_TYPE_ATOM || format != 8*sizeof(GdkAtom))
712 gdk_selection_send_notify (event->requestor, event->selection,
713 event->target, GDK_NONE, event->time);
719 info->num_conversions = length / (2*sizeof (GdkAtom));
720 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
722 for (i=0; i<info->num_conversions; i++)
724 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
725 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
728 else /* only a single conversion */
730 info->conversions = g_new (GtkIncrConversion, 1);
731 info->num_conversions = 1;
732 info->conversions[0].target = event->target;
733 info->conversions[0].property = event->property;
734 mult_atoms = (guchar *)info->conversions;
737 /* Loop through conversions and determine which of these are big
738 enough to require doing them via INCR */
739 for (i=0; i<info->num_conversions; i++)
741 GtkSelectionData data;
744 data.selection = event->selection;
745 data.target = info->conversions[i].target;
749 #ifdef DEBUG_SELECTION
750 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
751 event->selection, info->conversions[i].target,
752 gdk_atom_name(info->conversions[i].target),
753 event->requestor, event->property);
756 gtk_selection_invoke_handler (widget, &data);
760 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
761 info->conversions[i].property = GDK_NONE;
765 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
767 items = (data.length + data.format/8 - 1) / (data.format/8);
769 if (data.length > GTK_SELECTION_MAX_SIZE)
771 /* Sending via INCR */
773 info->conversions[i].offset = 0;
774 info->conversions[i].data = data;
777 gdk_property_change (info->requestor,
778 info->conversions[i].property,
779 gtk_selection_atoms[INCR],
781 GDK_PROP_MODE_REPLACE,
782 (guchar *)&items, 1);
786 info->conversions[i].offset = -1;
788 gdk_property_change (info->requestor,
789 info->conversions[i].property,
792 GDK_PROP_MODE_REPLACE,
799 /* If we have some INCR's, we need to send the rest of the data in
802 if (info->num_incrs > 0)
804 /* FIXME: this could be dangerous if window doesn't still
807 #ifdef DEBUG_SELECTION
808 g_message ("Starting INCR...");
811 gdk_window_set_events (info->requestor,
812 gdk_window_get_events (info->requestor) |
813 GDK_PROPERTY_CHANGE_MASK);
814 current_incrs = g_list_append (current_incrs, info);
815 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
818 /* If it was a MULTIPLE request, set the property to indicate which
819 conversions succeeded */
820 if (event->target == gtk_selection_atoms[MULTIPLE])
822 gdk_property_change (info->requestor, event->property,
823 GDK_SELECTION_TYPE_ATOM, 8*sizeof(GdkAtom),
824 GDK_PROP_MODE_REPLACE,
825 mult_atoms, info->num_conversions);
829 gdk_selection_send_notify (event->requestor, event->selection, event->target,
830 event->property, event->time);
832 if (info->num_incrs == 0)
834 g_free (info->conversions);
841 /*************************************************************
842 * gtk_selection_incr_event:
843 * Called whenever an PropertyNotify event occurs for an
844 * GdkWindow with user_data == NULL. These will be notifications
845 * that a window we are sending the selection to via the
846 * INCR protocol has deleted a property and is ready for
850 * window: the requestor window
851 * event: the property event structure
854 *************************************************************/
857 gtk_selection_incr_event (GdkWindow *window,
858 GdkEventProperty *event)
867 if (event->state != GDK_PROPERTY_DELETE)
870 #ifdef DEBUG_SELECTION
871 g_message ("PropertyDelete, property %ld", event->atom);
874 /* Now find the appropriate ongoing INCR */
875 tmp_list = current_incrs;
878 info = (GtkIncrInfo *)tmp_list->data;
879 if (info->requestor == event->window)
882 tmp_list = tmp_list->next;
885 if (tmp_list == NULL)
888 /* Find out which target this is for */
889 for (i=0; i<info->num_conversions; i++)
891 if (info->conversions[i].property == event->atom &&
892 info->conversions[i].offset != -1)
896 if (info->conversions[i].offset == -2) /* only the last 0-length
904 num_bytes = info->conversions[i].data.length -
905 info->conversions[i].offset;
906 buffer = info->conversions[i].data.data +
907 info->conversions[i].offset;
909 if (num_bytes > GTK_SELECTION_MAX_SIZE)
911 num_bytes = GTK_SELECTION_MAX_SIZE;
912 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
915 info->conversions[i].offset = -2;
917 #ifdef DEBUG_SELECTION
918 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
919 num_bytes, info->conversions[i].offset,
920 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
922 gdk_property_change (info->requestor, event->atom,
923 info->conversions[i].data.type,
924 info->conversions[i].data.format,
925 GDK_PROP_MODE_REPLACE,
927 (num_bytes + info->conversions[i].data.format/8 - 1) /
928 (info->conversions[i].data.format/8));
930 if (info->conversions[i].offset == -2)
932 g_free (info->conversions[i].data.data);
933 info->conversions[i].data.data = NULL;
939 info->conversions[i].offset = -1;
945 /* Check if we're finished with all the targets */
947 if (info->num_incrs == 0)
949 current_incrs = g_list_remove_link (current_incrs, tmp_list);
950 g_list_free (tmp_list);
951 /* Let the timeout free it */
957 /*************************************************************
958 * gtk_selection_incr_timeout:
959 * Timeout callback for the sending portion of the INCR
962 * info: Information about this incr
964 *************************************************************/
967 gtk_selection_incr_timeout (GtkIncrInfo *info)
971 /* Determine if retrieval has finished by checking if it still in
972 list of pending retrievals */
974 tmp_list = current_incrs;
977 if (info == (GtkIncrInfo *)tmp_list->data)
979 tmp_list = tmp_list->next;
982 /* If retrieval is finished */
983 if (!tmp_list || info->idle_time >= 5)
985 if (tmp_list && info->idle_time >= 5)
987 current_incrs = g_list_remove_link (current_incrs, tmp_list);
988 g_list_free (tmp_list);
991 g_free (info->conversions);
992 /* FIXME: we should check if requestor window is still in use,
993 and if not, remove it? */
997 return FALSE; /* remove timeout */
1003 return TRUE; /* timeout will happen again */
1007 /*************************************************************
1008 * gtk_selection_notify:
1009 * Handler for "selection_notify_event" signals on windows
1010 * where a retrieval is currently in process. The selection
1011 * owner has responded to our conversion request.
1013 * widget: Widget getting signal
1014 * event: Selection event structure
1015 * info: Information about this retrieval
1017 * was event handled?
1018 *************************************************************/
1021 gtk_selection_notify (GtkWidget *widget,
1022 GdkEventSelection *event)
1025 GtkRetrievalInfo *info;
1031 #ifdef DEBUG_SELECTION
1032 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1033 event->selection, event->target, event->property);
1036 tmp_list = current_retrievals;
1039 info = (GtkRetrievalInfo *)tmp_list->data;
1040 if (info->widget == widget && info->selection == event->selection)
1042 tmp_list = tmp_list->next;
1045 if (!tmp_list) /* no retrieval in progress */
1048 if (event->property == GDK_NONE)
1050 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1051 g_list_free (tmp_list);
1052 /* structure will be freed in timeout */
1053 gtk_selection_retrieval_report (info,
1054 GDK_NONE, 0, NULL, -1);
1059 length = gdk_selection_property_get (widget->window, &buffer,
1062 if (type == gtk_selection_atoms[INCR])
1064 /* The remainder of the selection will come through PropertyNotify
1067 info->idle_time = 0;
1068 info->offset = 0; /* Mark as OK to proceed */
1069 gdk_window_set_events (widget->window,
1070 gdk_window_get_events (widget->window)
1071 | GDK_PROPERTY_CHANGE_MASK);
1075 /* We don't delete the info structure - that will happen in timeout */
1076 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1077 g_list_free (tmp_list);
1079 info->offset = length;
1080 gtk_selection_retrieval_report (info,
1085 gdk_property_delete (widget->window, event->property);
1092 /*************************************************************
1093 * gtk_selection_property_notify:
1094 * Handler for "property_notify_event" signals on windows
1095 * where a retrieval is currently in process. The selection
1096 * owner has added more data.
1098 * widget: Widget getting signal
1099 * event: Property event structure
1100 * info: Information about this retrieval
1102 * was event handled?
1103 *************************************************************/
1106 gtk_selection_property_notify (GtkWidget *widget,
1107 GdkEventProperty *event)
1110 GtkRetrievalInfo *info;
1116 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1117 (event->atom != gdk_selection_property)) /* not the right property */
1120 #ifdef DEBUG_SELECTION
1121 g_message ("PropertyNewValue, property %ld",
1125 tmp_list = current_retrievals;
1128 info = (GtkRetrievalInfo *)tmp_list->data;
1129 if (info->widget == widget)
1131 tmp_list = tmp_list->next;
1134 if (!tmp_list) /* No retrieval in progress */
1137 if (info->offset < 0) /* We haven't got the SelectionNotify
1138 for this retrieval yet */
1141 info->idle_time = 0;
1143 length = gdk_selection_property_get (widget->window, &new_buffer,
1145 gdk_property_delete (widget->window, event->atom);
1147 /* We could do a lot better efficiency-wise by paying attention to
1148 what length was sent in the initial INCR transaction, instead of
1149 doing memory allocation at every step. But its only guaranteed to
1150 be a _lower bound_ (pretty useless!) */
1152 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1154 /* Info structure will be freed in timeout */
1155 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1156 g_list_free (tmp_list);
1157 gtk_selection_retrieval_report (info,
1159 (type == GDK_NONE) ? NULL : info->buffer,
1160 (type == GDK_NONE) ? -1 : info->offset);
1162 else /* append on newly arrived data */
1166 #ifdef DEBUG_SELECTION
1167 g_message ("Start - Adding %d bytes at offset 0",
1170 info->buffer = new_buffer;
1171 info->offset = length;
1176 #ifdef DEBUG_SELECTION
1177 g_message ("Appending %d bytes at offset %d",
1178 length,info->offset);
1180 /* We copy length+1 bytes to preserve guaranteed null termination */
1181 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1182 memcpy (info->buffer + info->offset, new_buffer, length+1);
1183 info->offset += length;
1184 g_free (new_buffer);
1191 /*************************************************************
1192 * gtk_selection_retrieval_timeout:
1193 * Timeout callback while receiving a selection.
1195 * info: Information about this retrieval
1197 *************************************************************/
1200 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1204 /* Determine if retrieval has finished by checking if it still in
1205 list of pending retrievals */
1207 tmp_list = current_retrievals;
1210 if (info == (GtkRetrievalInfo *)tmp_list->data)
1212 tmp_list = tmp_list->next;
1215 /* If retrieval is finished */
1216 if (!tmp_list || info->idle_time >= 5)
1218 if (tmp_list && info->idle_time >= 5)
1220 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1221 g_list_free (tmp_list);
1222 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1);
1225 g_free (info->buffer);
1228 return FALSE; /* remove timeout */
1234 return TRUE; /* timeout will happen again */
1239 /*************************************************************
1240 * gtk_selection_retrieval_report:
1241 * Emits a "selection_received" signal.
1243 * info: information about the retrieval that completed
1244 * buffer: buffer containing data (NULL => errror)
1246 *************************************************************/
1249 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1250 GdkAtom type, gint format,
1251 guchar *buffer, gint length)
1253 GtkSelectionData data;
1255 data.selection = info->selection;
1256 data.target = info->target;
1258 data.format = format;
1260 data.length = length;
1263 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1264 "selection_received", &data);
1267 /*************************************************************
1268 * gtk_selection_invoke_handler:
1269 * Finds and invokes handler for specified
1270 * widget/selection/target combination, calls
1271 * gtk_selection_default_handler if none exists.
1274 * widget: selection owner
1275 * data: selection data [INOUT]
1278 * Number of bytes written to buffer, -1 if error
1279 *************************************************************/
1282 gtk_selection_invoke_handler (GtkWidget *widget,
1283 GtkSelectionData *data)
1286 GtkSelectionHandler *handler;
1288 g_return_if_fail (widget != NULL);
1290 tmp_list = gtk_object_get_data (GTK_OBJECT (widget),
1291 gtk_selection_handler_key);
1295 handler = (GtkSelectionHandler *)tmp_list->data;
1296 if ((handler->selection == data->selection) &&
1297 (handler->target == data->target))
1299 tmp_list = tmp_list->next;
1302 if (tmp_list == NULL)
1303 gtk_selection_default_handler (widget, data);
1306 if (handler->marshal)
1309 args[0].type = GTK_TYPE_BOXED;
1310 args[0].name = NULL;
1311 GTK_VALUE_BOXED(args[0]) = data;
1312 args[1].type = GTK_TYPE_NONE;
1313 args[1].name = NULL;
1315 handler->marshal (GTK_OBJECT(widget), handler->data, 1, args);
1318 if (handler->function)
1319 handler->function (widget, data, handler->data);
1323 /*************************************************************
1324 * gtk_selection_default_handler:
1325 * Handles some default targets that exist for any widget
1326 * If it can't fit results into buffer, returns -1. This
1327 * won't happen in any conceivable case, since it would
1328 * require 1000 selection targets!
1331 * widget: selection owner
1332 * data: selection data [INOUT]
1334 *************************************************************/
1337 gtk_selection_default_handler (GtkWidget *widget,
1338 GtkSelectionData *data)
1340 if (data->target == gtk_selection_atoms[TIMESTAMP])
1342 /* Time which was used to obtain selection */
1344 GtkSelectionInfo *selection_info;
1346 tmp_list = current_selections;
1349 selection_info = (GtkSelectionInfo *)tmp_list->data;
1350 if ((selection_info->widget == widget) &&
1351 (selection_info->selection == data->selection))
1353 gtk_selection_data_set (data,
1354 GDK_SELECTION_TYPE_INTEGER,
1356 (guchar *)&selection_info->time,
1361 tmp_list = tmp_list->next;
1366 else if (data->target == gtk_selection_atoms[TARGETS])
1368 /* List of all targets supported for this widget/selection pair */
1372 GtkSelectionHandler *handler;
1375 tmp_list = gtk_object_get_data (GTK_OBJECT(widget),
1376 gtk_selection_handler_key);
1379 handler = (GtkSelectionHandler *)tmp_list->data;
1381 if (handler->selection == data->selection)
1384 tmp_list = tmp_list->next;
1387 data->type = GDK_SELECTION_TYPE_ATOM;
1388 data->format = 8*sizeof (GdkAtom);
1389 data->length = count*sizeof (GdkAtom);
1391 p = g_new (GdkAtom, count);
1392 data->data = (guchar *)p;
1394 *p++ = gtk_selection_atoms[TIMESTAMP];
1395 *p++ = gtk_selection_atoms[TARGETS];
1396 *p++ = gtk_selection_atoms[MULTIPLE];
1398 tmp_list = gtk_object_get_data (GTK_OBJECT(widget),
1399 gtk_selection_handler_key);
1402 handler = (GtkSelectionHandler *)tmp_list->data;
1404 if (handler->selection == data->selection)
1405 *p++ = handler->target;
1407 tmp_list = tmp_list->next;
1418 gtk_selection_data_copy (GtkSelectionData *data)
1420 GtkSelectionData *new_data;
1422 g_return_val_if_fail (data != NULL, NULL);
1424 new_data = g_new (GtkSelectionData, 1);
1431 gtk_selection_data_free (GtkSelectionData *data)
1433 g_return_if_fail (data != NULL);