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 Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* This file implements most of the work of the ICCM selection protocol.
20 * The code was written after an intensive study of the equivalent part
21 * of John Ousterhout's Tk toolkit, and does many things in much the
24 * The one thing in the ICCM that isn't fully supported here (or in Tk)
25 * is side effects targets. For these to be handled properly, MULTIPLE
26 * targets need to be done in the order specified. This cannot be
27 * guaranteed with the way we do things, since if we are doing INCR
28 * transfers, the order will depend on the timing of the requestor.
30 * By Owen Taylor <owt1@cornell.edu> 8/16/97
33 /* Terminology note: when not otherwise specified, the term "incr" below
34 * refers to the _sending_ part of the INCR protocol. The receiving
35 * portion is referred to just as "retrieval". (Terminology borrowed
36 * from Tk, because there is no good opposite to "retrieval" in English.
37 * "send" can't be made into a noun gracefully and we're already using
38 * "emission" for something else ....)
41 /* The MOTIF entry widget seems to ask for the TARGETS target, then
42 (regardless of the reply) ask for the TEXT target. It's slightly
43 possible though that it somehow thinks we are responding negatively
44 to the TARGETS request, though I don't really think so ... */
49 /* we need this for gdk_window_lookup() */
51 #include "gtkselection.h"
52 #include "gtksignal.h"
54 /* #define DEBUG_SELECTION */
56 /* Maximum size of a sent chunk, in bytes. Also the default size of
58 #define GTK_SELECTION_MAX_SIZE 4000
68 typedef struct _GtkSelectionInfo GtkSelectionInfo;
69 typedef struct _GtkIncrConversion GtkIncrConversion;
70 typedef struct _GtkIncrInfo GtkIncrInfo;
71 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
72 typedef struct _GtkSelectionHandler GtkSelectionHandler;
74 struct _GtkSelectionInfo
77 GtkWidget *widget; /* widget that owns selection */
78 guint32 time; /* time used to acquire selection */
81 struct _GtkIncrConversion
83 GdkAtom target; /* Requested target */
84 GdkAtom property; /* Property to store in */
85 GtkSelectionData data; /* The data being supplied */
86 gint offset; /* Current offset in sent selection.
88 * -2 => Only the final (empty) portion
94 GtkWidget *widget; /* Selection owner */
95 GdkWindow *requestor; /* Requestor window - we create a GdkWindow
96 so we can receive events */
97 GdkAtom selection; /* Selection we're sending */
99 GtkIncrConversion *conversions; /* Information about requested conversions -
100 * With MULTIPLE requests (benighted 1980's
101 * hardware idea), there can be more than
103 gint num_conversions;
104 gint num_incrs; /* number of remaining INCR style transactions */
109 struct _GtkRetrievalInfo
112 GdkAtom selection; /* Selection being retrieved. */
113 GdkAtom target; /* Form of selection that we requested */
114 guint32 idle_time; /* Number of seconds since we last heard
115 from selection owner */
116 guchar *buffer; /* Buffer in which to accumulate results */
117 gint offset; /* Current offset in buffer, -1 indicates
121 struct _GtkSelectionHandler
123 GdkAtom selection; /* selection thats handled */
124 GdkAtom target; /* target thats handled */
125 GtkSelectionFunction function; /* callback function */
126 GtkRemoveFunction remove_func; /* called when callback is removed */
127 gpointer data; /* callback data */
130 /* Local Functions */
131 static void gtk_selection_init (void);
132 static gint gtk_selection_incr_timeout (GtkIncrInfo *info);
133 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
134 static void gtk_selection_retrieval_report (GtkRetrievalInfo *info,
135 GdkAtom type, gint format,
136 guchar *buffer, gint length);
137 static GtkSelectionHandler *gtk_selection_find_handler (GtkWidget *widget,
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";
152 /*************************************************************
153 * gtk_selection_owner_set:
154 * Claim ownership of a selection.
156 * widget: new selection owner
157 * selection: which selection
158 * time: time (use GDK_CURRENT_TIME only if necessary)
161 *************************************************************/
164 gtk_selection_owner_set (GtkWidget *widget,
169 GtkWidget *old_owner;
170 GtkSelectionInfo *selection_info;
177 if (!GTK_WIDGET_REALIZED (widget))
178 gtk_widget_realize (widget);
180 window = widget->window;
183 tmp_list = current_selections;
186 selection_info = (GtkSelectionInfo *)tmp_list->data;
188 if (selection_info->selection == selection)
191 tmp_list = tmp_list->next;
194 if (tmp_list == NULL)
195 selection_info = NULL;
197 if (selection_info->widget == widget)
200 if (gdk_selection_owner_set (window, selection, time, TRUE))
208 old_owner = selection_info->widget;
209 current_selections = g_list_remove_link (current_selections,
211 g_list_free (tmp_list);
212 g_free (selection_info);
217 if (selection_info == NULL)
219 selection_info = g_new (GtkSelectionInfo, 1);
220 selection_info->selection = selection;
221 selection_info->widget = widget;
222 selection_info->time = time;
223 current_selections = g_list_append (current_selections,
228 old_owner = selection_info->widget;
229 selection_info->widget = widget;
230 selection_info->time = time;
233 /* If another widget in the application lost the selection,
234 * send it a GDK_SELECTION_CLEAR event, unless we're setting
235 * the owner to None, in which case an event will be sent */
236 if (old_owner && (widget != NULL))
238 GdkEventSelection event;
240 event.type = GDK_SELECTION_CLEAR;
241 event.window = old_owner->window;
242 event.selection = selection;
245 gtk_widget_event (old_owner, (GdkEvent *) &event);
253 /*************************************************************
254 * gtk_selection_add_handler:
255 * Add a handler for a specified selection/target pair
258 * widget: The widget the handler applies to
261 * format: Format in which this handler will return data
262 * function: Callback function (can be NULL)
263 * data: User data for callback
266 *************************************************************/
269 gtk_selection_add_handler (GtkWidget *widget,
272 GtkSelectionFunction function,
273 GtkRemoveFunction remove_func,
276 GList *selection_handlers;
278 GtkSelectionHandler *handler;
280 g_return_if_fail (widget != NULL);
282 gtk_selection_init ();
284 selection_handlers = gtk_object_get_data (GTK_OBJECT (widget),
285 gtk_selection_handler_key);
287 /* Reuse old handler structure, if present */
288 tmp_list = selection_handlers;
291 handler = (GtkSelectionHandler *)tmp_list->data;
292 if ((handler->selection == selection) && (handler->target == target))
294 if (handler->remove_func)
295 (*handler->remove_func)(handler->data);
298 handler->function = function;
299 handler->remove_func = remove_func;
300 handler->data = data;
304 selection_handlers = g_list_remove_link (selection_handlers,
306 g_list_free (tmp_list);
311 tmp_list = tmp_list->next;
314 if (tmp_list == NULL && function)
316 handler = g_new (GtkSelectionHandler, 1);
317 handler->selection = selection;
318 handler->target = target;
319 handler->function = function;
320 handler->remove_func = remove_func;
321 handler->data = data;
322 selection_handlers = g_list_append (selection_handlers, handler);
325 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key,
329 /*************************************************************
330 * gtk_selection_remove_all:
331 * Removes all handlers and unsets ownership of all
332 * selections for a widget. Called when widget is being
338 *************************************************************/
341 gtk_selection_remove_all (GtkWidget *widget)
345 GtkSelectionInfo *selection_info;
346 GList *selection_handlers;
347 GtkSelectionHandler *handler;
349 /* Remove pending requests/incrs for this widget */
351 tmp_list = current_incrs;
354 next = tmp_list->next;
355 if (((GtkIncrInfo *)tmp_list->data)->widget == widget)
357 current_incrs = g_list_remove_link (current_incrs, tmp_list);
358 /* structure will be freed in timeout */
359 g_list_free (tmp_list);
364 tmp_list = current_retrievals;
367 next = tmp_list->next;
368 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
370 current_retrievals = g_list_remove_link (current_retrievals,
372 /* structure will be freed in timeout */
373 g_list_free (tmp_list);
378 /* Disclaim ownership of any selections */
380 tmp_list = current_selections;
383 next = tmp_list->next;
384 selection_info = (GtkSelectionInfo *)tmp_list->data;
386 if (selection_info->widget == widget)
388 gdk_selection_owner_set (NULL,
389 selection_info->selection,
390 GDK_CURRENT_TIME, FALSE);
391 current_selections = g_list_remove_link (current_selections,
393 g_list_free (tmp_list);
394 g_free (selection_info);
400 /* Now remove all handlers */
402 selection_handlers = gtk_object_get_data (GTK_OBJECT (widget),
403 gtk_selection_handler_key);
404 gtk_object_remove_data (GTK_OBJECT (widget), gtk_selection_handler_key);
406 tmp_list = selection_handlers;
409 next = tmp_list->next;
410 handler = (GtkSelectionHandler *)tmp_list->data;
412 if (handler->remove_func)
413 (*handler->remove_func)(handler->data);
420 g_list_free (selection_handlers);
423 /*************************************************************
424 * gtk_selection_convert:
425 * Request the contents of a selection. When received,
426 * a "selection_received" signal will be generated.
429 * widget: The widget which acts as requestor
430 * selection: Which selection to get
431 * target: Form of information desired (e.g., STRING)
432 * time: Time of request (usually of triggering event)
433 * In emergency, you could use GDK_CURRENT_TIME
436 * TRUE if requested succeeded. FALSE if we could not process
437 * request. (e.g., there was already a request in process for
439 *************************************************************/
442 gtk_selection_convert (GtkWidget *widget,
447 GtkRetrievalInfo *info;
449 GdkWindow *owner_window;
451 g_return_val_if_fail (widget != NULL, FALSE);
454 gtk_selection_init ();
456 if (!GTK_WIDGET_REALIZED (widget))
457 gtk_widget_realize (widget);
459 /* Check to see if there are already any retrievals in progress for
460 this widget. If we changed GDK to use the selection for the
461 window property in which to store the retrieved information, then
462 we could support multiple retrievals for different selections.
463 This might be useful for DND. */
465 tmp_list = current_retrievals;
468 info = (GtkRetrievalInfo *)tmp_list->data;
469 if (info->widget == widget)
471 tmp_list = tmp_list->next;
474 info = g_new (GtkRetrievalInfo, 1);
476 info->widget = widget;
477 info->selection = selection;
478 info->target = target;
482 /* Check if this process has current owner. If so, call handler
483 procedure directly to avoid deadlocks with INCR. */
485 owner_window = gdk_selection_owner_get (selection);
487 if (owner_window != NULL)
489 GtkWidget *owner_widget;
490 GtkSelectionHandler *handler;
491 GtkSelectionData selection_data;
493 selection_data.selection = selection;
494 selection_data.target = target;
495 selection_data.data = NULL;
496 selection_data.length = -1;
498 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
500 if (owner_widget != NULL)
502 handler = gtk_selection_find_handler (owner_widget, selection, target);
504 (* handler->function)(owner_widget,
507 else /* try the default handler */
508 gtk_selection_default_handler (owner_widget,
511 gtk_selection_retrieval_report (info,
513 selection_data.format,
515 selection_data.length);
517 g_free (selection_data.data);
524 /* Otherwise, we need to go through X */
526 current_retrievals = g_list_append (current_retrievals, info);
527 gdk_selection_convert (widget->window, selection, target, time);
528 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
533 /*************************************************************
534 * gtk_selection_data_set:
535 * Store new data into a GtkSelectionData object. Should
536 * _only_ by called from a selection handler callback.
537 * Null terminates the stored data.
539 * type: the type of selection data
540 * format: format (number of bits in a unit)
541 * data: pointer to the data (will be copied)
542 * length: length of the data
544 *************************************************************/
547 gtk_selection_data_set (GtkSelectionData *selection_data,
553 if (selection_data->data)
554 g_free (selection_data->data);
556 selection_data->type = type;
557 selection_data->format = format;
561 selection_data->data = g_new (guchar, length+1);
562 memcpy (selection_data->data, data, length);
563 selection_data->data[length] = 0;
566 selection_data->data = NULL;
568 selection_data->length = length;
571 /*************************************************************
572 * gtk_selection_init:
573 * Initialize local variables
577 *************************************************************/
580 gtk_selection_init (void)
582 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
583 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
584 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
585 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
588 /*************************************************************
589 * gtk_selection_clear:
590 * Handler for "selection_clear_event"
595 *************************************************************/
598 gtk_selection_clear (GtkWidget *widget,
599 GdkEventSelection *event)
601 /* FIXME: there can be a problem if we change the selection
602 via gtk_selection_owner_set after another client claims
603 the selection, but before we get the notification event.
604 Tk filters based on serial #'s, which aren't retained by
605 GTK. Filtering based on time's will be inherently
606 somewhat unreliable. */
609 GtkSelectionInfo *selection_info;
611 tmp_list = current_selections;
614 selection_info = (GtkSelectionInfo *)tmp_list->data;
616 if ((selection_info->selection == event->selection) &&
617 (selection_info->widget == widget))
620 tmp_list = tmp_list->next;
623 if (tmp_list == NULL || selection_info->time > event->time)
626 current_selections = g_list_remove_link (current_selections, tmp_list);
627 g_list_free (tmp_list);
628 g_free (selection_info);
634 /*************************************************************
635 * gtk_selection_request:
636 * Handler for "selection_request_event"
641 *************************************************************/
644 gtk_selection_request (GtkWidget *widget,
645 GdkEventSelection *event)
648 GtkSelectionHandler *handler;
653 /* Check if we own selection */
655 tmp_list = current_selections;
658 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
660 if ((selection_info->selection == event->selection) &&
661 (selection_info->widget == widget))
664 tmp_list = tmp_list->next;
667 if (tmp_list == NULL)
670 info = g_new(GtkIncrInfo, 1);
672 info->widget = widget;
673 info->selection = event->selection;
676 /* Create GdkWindow structure for the requestor */
678 info->requestor = gdk_window_lookup (event->requestor);
679 if (!info->requestor)
680 info->requestor = gdk_window_foreign_new (event->requestor);
682 /* Determine conversions we need to perform */
684 if (event->target == gtk_selection_atoms[MULTIPLE])
691 if (!gdk_property_get (info->requestor, event->property, GDK_SELECTION_TYPE_ATOM,
692 0, GTK_SELECTION_MAX_SIZE, FALSE,
693 &type, &format, &length, &mult_atoms) ||
694 type != GDK_SELECTION_TYPE_ATOM || format != 8*sizeof(GdkAtom))
696 gdk_selection_send_notify (event->requestor, event->selection,
697 event->target, GDK_NONE, event->time);
703 info->num_conversions = length / (2*sizeof (GdkAtom));
704 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
706 for (i=0; i<info->num_conversions; i++)
708 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
709 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
712 else /* only a single conversion */
714 info->conversions = g_new (GtkIncrConversion, 1);
715 info->num_conversions = 1;
716 info->conversions[0].target = event->target;
717 info->conversions[0].property = event->property;
718 mult_atoms = (guchar *)info->conversions;
721 /* Loop through conversions and determine which of these are big
722 enough to require doing them via INCR */
723 for (i=0; i<info->num_conversions; i++)
725 GtkSelectionData data;
728 data.selection = event->selection;
729 data.target = info->conversions[i].target;
733 #ifdef DEBUG_SELECTION
734 g_print("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)\n",
735 event->selection, info->conversions[i].target,
736 gdk_atom_name(info->conversions[i].target),
737 event->requestor, event->property);
740 handler = gtk_selection_find_handler (widget, event->selection,
741 info->conversions[i].target);
743 (* handler->function)(widget, &data, handler->data);
745 gtk_selection_default_handler (widget, &data);
749 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
750 info->conversions[i].property = GDK_NONE;
754 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
756 items = (data.length + data.format/8 - 1) / (data.format/8);
758 if (data.length > GTK_SELECTION_MAX_SIZE)
760 /* Sending via INCR */
762 info->conversions[i].offset = 0;
763 info->conversions[i].data = data;
766 gdk_property_change (info->requestor,
767 info->conversions[i].property,
768 gtk_selection_atoms[INCR],
770 GDK_PROP_MODE_REPLACE,
771 (guchar *)&items, 1);
775 info->conversions[i].offset = -1;
777 gdk_property_change (info->requestor,
778 info->conversions[i].property,
781 GDK_PROP_MODE_REPLACE,
788 /* If we have some INCR's, we need to send the rest of the data in
791 if (info->num_incrs > 0)
793 /* FIXME: this could be dangerous if window doesn't still
796 #ifdef DEBUG_SELECTION
797 g_print("Starting INCR...\n");
800 gdk_window_set_events (info->requestor,
801 gdk_window_get_events (info->requestor) |
802 GDK_PROPERTY_CHANGE_MASK);
803 current_incrs = g_list_append (current_incrs, info);
804 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
807 /* If it was a MULTIPLE request, set the property to indicate which
808 conversions succeeded */
809 if (event->target == gtk_selection_atoms[MULTIPLE])
811 gdk_property_change (info->requestor, event->property,
812 GDK_SELECTION_TYPE_ATOM, 8*sizeof(GdkAtom),
813 GDK_PROP_MODE_REPLACE,
814 mult_atoms, info->num_conversions);
818 gdk_selection_send_notify (event->requestor, event->selection, event->target,
819 event->property, event->time);
821 if (info->num_incrs == 0)
823 g_free (info->conversions);
830 /*************************************************************
831 * gtk_selection_incr_event:
832 * Called whenever an PropertyNotify event occurs for an
833 * GdkWindow with user_data == NULL. These will be notifications
834 * that a window we are sending the selection to via the
835 * INCR protocol has deleted a property and is ready for
839 * window: the requestor window
840 * event: the property event structure
843 *************************************************************/
846 gtk_selection_incr_event (GdkWindow *window,
847 GdkEventProperty *event)
856 if (event->state != GDK_PROPERTY_DELETE)
859 #ifdef DEBUG_SELECTION
860 g_print("PropertyDelete, property %ld\n", event->atom);
863 /* Now find the appropriate ongoing INCR */
864 tmp_list = current_incrs;
867 info = (GtkIncrInfo *)tmp_list->data;
868 if (info->requestor == event->window)
871 tmp_list = tmp_list->next;
874 if (tmp_list == NULL)
877 /* Find out which target this is for */
878 for (i=0; i<info->num_conversions; i++)
880 if (info->conversions[i].property == event->atom &&
881 info->conversions[i].offset != -1)
885 if (info->conversions[i].offset == -2) /* only the last 0-length
893 num_bytes = info->conversions[i].data.length -
894 info->conversions[i].offset;
895 buffer = info->conversions[i].data.data +
896 info->conversions[i].offset;
898 if (num_bytes > GTK_SELECTION_MAX_SIZE)
900 num_bytes = GTK_SELECTION_MAX_SIZE;
901 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
904 info->conversions[i].offset = -2;
906 #ifdef DEBUG_SELECTION
907 g_print("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld\n",
908 num_bytes, info->conversions[i].offset,
909 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
911 gdk_property_change (info->requestor, event->atom,
912 info->conversions[i].data.type,
913 info->conversions[i].data.format,
914 GDK_PROP_MODE_REPLACE,
916 (num_bytes + info->conversions[i].data.format/8 - 1) /
917 (info->conversions[i].data.format/8));
919 if (info->conversions[i].offset == -2)
921 g_free (info->conversions[i].data.data);
922 info->conversions[i].data.data = NULL;
928 info->conversions[i].offset = -1;
934 /* Check if we're finished with all the targets */
936 if (info->num_incrs == 0)
938 current_incrs = g_list_remove_link (current_incrs, tmp_list);
939 g_list_free (tmp_list);
940 /* Let the timeout free it */
946 /*************************************************************
947 * gtk_selection_incr_timeout:
948 * Timeout callback for the sending portion of the INCR
951 * info: Information about this incr
953 *************************************************************/
956 gtk_selection_incr_timeout (GtkIncrInfo *info)
960 /* Determine if retrieval has finished by checking if it still in
961 list of pending retrievals */
963 tmp_list = current_incrs;
966 if (info == (GtkIncrInfo *)tmp_list->data)
968 tmp_list = tmp_list->next;
971 /* If retrieval is finished */
972 if (!tmp_list || info->idle_time >= 5)
974 if (tmp_list && info->idle_time >= 5)
976 current_incrs = g_list_remove_link (current_incrs, tmp_list);
977 g_list_free (tmp_list);
980 g_free (info->conversions);
981 /* FIXME: we should check if requestor window is still in use,
982 and if not, remove it? */
986 return FALSE; /* remove timeout */
992 return TRUE; /* timeout will happen again */
996 /*************************************************************
997 * gtk_selection_notify:
998 * Handler for "selection_notify_event" signals on windows
999 * where a retrieval is currently in process. The selection
1000 * owner has responded to our conversion request.
1002 * widget: Widget getting signal
1003 * event: Selection event structure
1004 * info: Information about this retrieval
1006 * was event handled?
1007 *************************************************************/
1010 gtk_selection_notify (GtkWidget *widget,
1011 GdkEventSelection *event)
1014 GtkRetrievalInfo *info;
1020 #ifdef DEBUG_SELECTION
1021 g_print("Initial receipt of selection %ld, target %ld (property = %ld)\n",
1022 event->selection, event->target, event->property);
1025 tmp_list = current_retrievals;
1028 info = (GtkRetrievalInfo *)tmp_list->data;
1029 if (info->widget == widget && info->selection == event->selection)
1031 tmp_list = tmp_list->next;
1034 if (!tmp_list) /* no retrieval in progress */
1037 if (event->property == GDK_NONE)
1039 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1040 g_list_free (tmp_list);
1041 /* structure will be freed in timeout */
1042 gtk_selection_retrieval_report (info,
1043 GDK_NONE, 0, NULL, -1);
1048 length = gdk_selection_property_get (widget->window, &buffer,
1051 if (type == gtk_selection_atoms[INCR])
1053 /* The remainder of the selection will come through PropertyNotify
1056 info->idle_time = 0;
1057 info->offset = 0; /* Mark as OK to proceed */
1058 gdk_window_set_events (widget->window,
1059 gdk_window_get_events (widget->window)
1060 | GDK_PROPERTY_CHANGE_MASK);
1064 /* We don't delete the info structure - that will happen in timeout */
1065 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1066 g_list_free (tmp_list);
1068 info->offset = length;
1069 gtk_selection_retrieval_report (info,
1074 gdk_property_delete (widget->window, event->property);
1081 /*************************************************************
1082 * gtk_selection_property_notify:
1083 * Handler for "property_notify_event" signals on windows
1084 * where a retrieval is currently in process. The selection
1085 * owner has added more data.
1087 * widget: Widget getting signal
1088 * event: Property event structure
1089 * info: Information about this retrieval
1091 * was event handled?
1092 *************************************************************/
1095 gtk_selection_property_notify (GtkWidget *widget,
1096 GdkEventProperty *event)
1099 GtkRetrievalInfo *info;
1105 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1106 (event->atom != gdk_selection_property)) /* not the right property */
1109 #ifdef DEBUG_SELECTION
1110 g_print("PropertyNewValue, property %ld\n",
1114 tmp_list = current_retrievals;
1117 info = (GtkRetrievalInfo *)tmp_list->data;
1118 if (info->widget == widget)
1120 tmp_list = tmp_list->next;
1123 if (!tmp_list) /* No retrieval in progress */
1126 if (info->offset < 0) /* We haven't got the SelectionNotify
1127 for this retrieval yet */
1130 info->idle_time = 0;
1132 length = gdk_selection_property_get (widget->window, &new_buffer,
1134 gdk_property_delete (widget->window, event->atom);
1136 /* We could do a lot better efficiency-wise by paying attention to
1137 what length was sent in the initial INCR transaction, instead of
1138 doing memory allocation at every step. But its only guaranteed to
1139 be a _lower bound_ (pretty useless!) */
1141 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1143 /* Info structure will be freed in timeout */
1144 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1145 g_list_free (tmp_list);
1146 gtk_selection_retrieval_report (info,
1148 (type == GDK_NONE) ? NULL : info->buffer,
1149 (type == GDK_NONE) ? -1 : info->offset);
1151 else /* append on newly arrived data */
1155 #ifdef DEBUG_SELECTION
1156 g_print("Start - Adding %d bytes at offset 0\n",
1159 info->buffer = new_buffer;
1160 info->offset = length;
1165 #ifdef DEBUG_SELECTION
1166 g_print("Appending %d bytes at offset %d\n",
1167 length,info->offset);
1169 /* We copy length+1 bytes to preserve guaranteed null termination */
1170 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1171 memcpy (info->buffer + info->offset, new_buffer, length+1);
1172 info->offset += length;
1173 g_free (new_buffer);
1180 /*************************************************************
1181 * gtk_selection_retrieval_timeout:
1182 * Timeout callback while receiving a selection.
1184 * info: Information about this retrieval
1186 *************************************************************/
1189 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1193 /* Determine if retrieval has finished by checking if it still in
1194 list of pending retrievals */
1196 tmp_list = current_retrievals;
1199 if (info == (GtkRetrievalInfo *)tmp_list->data)
1201 tmp_list = tmp_list->next;
1204 /* If retrieval is finished */
1205 if (!tmp_list || info->idle_time >= 5)
1207 if (tmp_list && info->idle_time >= 5)
1209 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1210 g_list_free (tmp_list);
1211 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1);
1214 g_free (info->buffer);
1217 return FALSE; /* remove timeout */
1223 return TRUE; /* timeout will happen again */
1228 /*************************************************************
1229 * gtk_selection_retrieval_report:
1230 * Emits a "selection_received" signal.
1232 * info: information about the retrieval that completed
1233 * buffer: buffer containing data (NULL => errror)
1235 *************************************************************/
1238 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1239 GdkAtom type, gint format,
1240 guchar *buffer, gint length)
1242 GtkSelectionData data;
1244 data.selection = info->selection;
1245 data.target = info->target;
1247 data.format = format;
1249 data.length = length;
1252 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1253 "selection_received", &data);
1256 /*************************************************************
1257 * gtk_selection_find_handler:
1258 * Find handler for specified widget/selection/target
1264 *************************************************************/
1266 static GtkSelectionHandler *
1267 gtk_selection_find_handler (GtkWidget *widget,
1272 GtkSelectionHandler *handler;
1274 g_return_val_if_fail (widget != NULL, FALSE);
1276 tmp_list = gtk_object_get_data (GTK_OBJECT (widget),
1277 gtk_selection_handler_key);
1281 handler = (GtkSelectionHandler *)tmp_list->data;
1282 if ((handler->selection == selection) && (handler->target == target))
1284 tmp_list = tmp_list->next;
1291 /*************************************************************
1292 * gtk_selection_default_handler:
1293 * Handles some default targets that exist for any widget
1294 * If it can't fit results into buffer, returns -1. This
1295 * won't happen in any conceivable case, since it would
1296 * require 1000 selection targets!
1299 * widget: selection owner
1300 * selection: selection requested
1301 * target: target requested
1302 * buffer: buffer to write results into
1303 * length: size of buffer
1305 * format: length of type's units in bits
1308 * Number of bytes written to buffer, -1 if error
1309 *************************************************************/
1312 gtk_selection_default_handler (GtkWidget *widget,
1313 GtkSelectionData *data)
1315 if (data->target == gtk_selection_atoms[TIMESTAMP])
1317 /* Time which was used to obtain selection */
1319 GtkSelectionInfo *selection_info;
1321 tmp_list = current_selections;
1324 selection_info = (GtkSelectionInfo *)tmp_list->data;
1325 if ((selection_info->widget == widget) &&
1326 (selection_info->selection == data->selection))
1328 gtk_selection_data_set (data,
1329 GDK_SELECTION_TYPE_INTEGER,
1331 (guchar *)&selection_info->time,
1336 tmp_list = tmp_list->next;
1341 else if (data->target == gtk_selection_atoms[TARGETS])
1343 /* List of all targets supported for this widget/selection pair */
1347 GtkSelectionHandler *handler;
1350 tmp_list = gtk_object_get_data (GTK_OBJECT(widget),
1351 gtk_selection_handler_key);
1354 handler = (GtkSelectionHandler *)tmp_list->data;
1356 if (handler->selection == data->selection)
1359 tmp_list = tmp_list->next;
1362 data->type = GDK_SELECTION_TYPE_ATOM;
1363 data->format = 8*sizeof (GdkAtom);
1364 data->length = count*sizeof (GdkAtom);
1366 p = g_new (GdkAtom, count);
1367 data->data = (guchar *)p;
1369 *p++ = gtk_selection_atoms[TIMESTAMP];
1370 *p++ = gtk_selection_atoms[TARGETS];
1371 *p++ = gtk_selection_atoms[MULTIPLE];
1373 tmp_list = gtk_object_get_data (GTK_OBJECT(widget),
1374 gtk_selection_handler_key);
1377 handler = (GtkSelectionHandler *)tmp_list->data;
1379 if (handler->selection == data->selection)
1380 *p++ = handler->target;
1382 tmp_list = tmp_list->next;