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;
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
119 guint32 notify_time; /* Timestamp from SelectionNotify */
122 /* Local Functions */
123 static void gtk_selection_init (void);
124 static gint gtk_selection_incr_timeout (GtkIncrInfo *info);
125 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
126 static void gtk_selection_retrieval_report (GtkRetrievalInfo *info,
127 GdkAtom type, gint format,
128 guchar *buffer, gint length,
130 static void gtk_selection_invoke_handler (GtkWidget *widget,
131 GtkSelectionData *data,
133 static void gtk_selection_default_handler (GtkWidget *widget,
134 GtkSelectionData *data);
137 static gint initialize = TRUE;
138 static GList *current_retrievals = NULL;
139 static GList *current_incrs = NULL;
140 static GList *current_selections = NULL;
142 static GdkAtom gtk_selection_atoms[LAST_ATOM];
143 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
154 gtk_target_list_new (const GtkTargetEntry *targets,
157 GtkTargetList *result = g_new (GtkTargetList, 1);
159 result->ref_count = 1;
162 gtk_target_list_add_table (result, targets, ntargets);
168 gtk_target_list_ref (GtkTargetList *list)
174 gtk_target_list_unref (GtkTargetList *list)
177 if (list->ref_count == 0)
179 GList *tmp_list = list->list;
182 GtkTargetEntry *entry = tmp_list->data;
185 tmp_list = tmp_list->next;
191 gtk_target_list_add (GtkTargetList *list,
198 g_return_if_fail (list != NULL);
200 pair = g_new (GtkTargetPair, 1);
201 pair->target = target;
205 list->list = g_list_append (list->list, pair);
209 gtk_target_list_add_table (GtkTargetList *list,
210 const GtkTargetEntry *targets,
215 for (i=ntargets-1; i >= 0; i--)
217 GtkTargetPair *pair = g_new (GtkTargetPair, 1);
218 pair->target = gdk_atom_intern (targets[i].target, FALSE);
219 pair->flags = targets[i].flags;
220 pair->info = targets[i].info;
222 list->list = g_list_prepend (list->list, pair);
227 gtk_target_list_remove (GtkTargetList *list,
232 g_return_if_fail (list != NULL);
234 tmp_list = list->list;
237 GtkTargetPair *pair = tmp_list->data;
239 if (pair->target == target)
243 list->list = g_list_remove (list->list, tmp_list);
244 g_list_free_1 (tmp_list);
249 tmp_list = tmp_list->next;
254 gtk_target_list_find (GtkTargetList *list,
258 GList *tmp_list = list->list;
261 GtkTargetPair *pair = tmp_list->data;
263 if (pair->target == target)
268 tmp_list = tmp_list->next;
275 /*************************************************************
276 * gtk_selection_owner_set:
277 * Claim ownership of a selection.
279 * widget: new selection owner
280 * selection: which selection
281 * time: time (use GDK_CURRENT_TIME only if necessary)
284 *************************************************************/
287 gtk_selection_owner_set (GtkWidget *widget,
292 GtkWidget *old_owner;
293 GtkSelectionInfo *selection_info;
300 if (!GTK_WIDGET_REALIZED (widget))
301 gtk_widget_realize (widget);
303 window = widget->window;
306 tmp_list = current_selections;
309 selection_info = (GtkSelectionInfo *)tmp_list->data;
311 if (selection_info->selection == selection)
314 tmp_list = tmp_list->next;
317 if (tmp_list == NULL)
318 selection_info = NULL;
320 if (selection_info->widget == widget)
323 if (gdk_selection_owner_set (window, selection, time, TRUE))
331 old_owner = selection_info->widget;
332 current_selections = g_list_remove_link (current_selections,
334 g_list_free (tmp_list);
335 g_free (selection_info);
340 if (selection_info == NULL)
342 selection_info = g_new (GtkSelectionInfo, 1);
343 selection_info->selection = selection;
344 selection_info->widget = widget;
345 selection_info->time = time;
346 current_selections = g_list_append (current_selections,
351 old_owner = selection_info->widget;
352 selection_info->widget = widget;
353 selection_info->time = time;
356 /* If another widget in the application lost the selection,
357 * send it a GDK_SELECTION_CLEAR event, unless we're setting
358 * the owner to None, in which case an event will be sent */
359 if (old_owner && (widget != NULL))
361 GdkEventSelection event;
363 event.type = GDK_SELECTION_CLEAR;
364 event.window = old_owner->window;
365 event.selection = selection;
368 gtk_widget_event (old_owner, (GdkEvent *) &event);
376 /*************************************************************
377 * gtk_selection_add_target
378 * Add specified target to list of supported targets
381 * widget: The widget for which this target applies
384 * info: guint to pass to to the selection_get signal
387 *************************************************************/
389 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
391 struct _GtkSelectionTargetList {
396 static GtkTargetList *
397 gtk_selection_target_list_get (GtkWidget *widget,
400 GtkSelectionTargetList *sellist;
404 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
409 sellist = tmp_list->data;
410 if (sellist->selection == selection)
411 return sellist->list;
412 tmp_list = tmp_list->next;
415 sellist = g_new (GtkSelectionTargetList, 1);
416 sellist->selection = selection;
417 sellist->list = gtk_target_list_new (NULL, 0);
419 lists = g_list_prepend (lists, sellist);
420 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
422 return sellist->list;
426 gtk_selection_target_list_remove (GtkWidget *widget)
428 GtkSelectionTargetList *sellist;
432 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
437 sellist = tmp_list->data;
439 gtk_target_list_unref (sellist->list);
442 tmp_list = tmp_list->next;
446 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL);
450 gtk_selection_add_target (GtkWidget *widget,
457 g_return_if_fail (widget != NULL);
459 list = gtk_selection_target_list_get (widget, selection);
460 gtk_target_list_add (list, target, 0, info);
464 gtk_selection_add_targets (GtkWidget *widget,
466 const GtkTargetEntry *targets,
471 g_return_if_fail (widget != NULL);
472 g_return_if_fail (targets != NULL);
474 list = gtk_selection_target_list_get (widget, selection);
475 gtk_target_list_add_table (list, targets, ntargets);
478 /*************************************************************
479 * gtk_selection_remove_all:
480 * Removes all handlers and unsets ownership of all
481 * selections for a widget. Called when widget is being
487 *************************************************************/
490 gtk_selection_remove_all (GtkWidget *widget)
494 GtkSelectionInfo *selection_info;
496 /* Remove pending requests/incrs for this widget */
498 tmp_list = current_incrs;
501 next = tmp_list->next;
502 if (((GtkIncrInfo *)tmp_list->data)->widget == widget)
504 current_incrs = g_list_remove_link (current_incrs, tmp_list);
505 /* structure will be freed in timeout */
506 g_list_free (tmp_list);
511 tmp_list = current_retrievals;
514 next = tmp_list->next;
515 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
517 current_retrievals = g_list_remove_link (current_retrievals,
519 /* structure will be freed in timeout */
520 g_list_free (tmp_list);
525 /* Disclaim ownership of any selections */
527 tmp_list = current_selections;
530 next = tmp_list->next;
531 selection_info = (GtkSelectionInfo *)tmp_list->data;
533 if (selection_info->widget == widget)
535 gdk_selection_owner_set (NULL,
536 selection_info->selection,
537 GDK_CURRENT_TIME, FALSE);
538 current_selections = g_list_remove_link (current_selections,
540 g_list_free (tmp_list);
541 g_free (selection_info);
547 /* Remove all selection lists */
548 gtk_selection_target_list_remove (widget);
551 /*************************************************************
552 * gtk_selection_convert:
553 * Request the contents of a selection. When received,
554 * a "selection_received" signal will be generated.
557 * widget: The widget which acts as requestor
558 * selection: Which selection to get
559 * target: Form of information desired (e.g., STRING)
560 * time: Time of request (usually of triggering event)
561 * In emergency, you could use GDK_CURRENT_TIME
564 * TRUE if requested succeeded. FALSE if we could not process
565 * request. (e.g., there was already a request in process for
567 *************************************************************/
570 gtk_selection_convert (GtkWidget *widget,
575 GtkRetrievalInfo *info;
577 GdkWindow *owner_window;
579 g_return_val_if_fail (widget != NULL, FALSE);
582 gtk_selection_init ();
584 if (!GTK_WIDGET_REALIZED (widget))
585 gtk_widget_realize (widget);
587 /* Check to see if there are already any retrievals in progress for
588 this widget. If we changed GDK to use the selection for the
589 window property in which to store the retrieved information, then
590 we could support multiple retrievals for different selections.
591 This might be useful for DND. */
593 tmp_list = current_retrievals;
596 info = (GtkRetrievalInfo *)tmp_list->data;
597 if (info->widget == widget)
599 tmp_list = tmp_list->next;
602 info = g_new (GtkRetrievalInfo, 1);
604 info->widget = widget;
605 info->selection = selection;
606 info->target = target;
610 /* Check if this process has current owner. If so, call handler
611 procedure directly to avoid deadlocks with INCR. */
613 owner_window = gdk_selection_owner_get (selection);
615 if (owner_window != NULL)
617 GtkWidget *owner_widget;
618 GtkSelectionData selection_data;
620 selection_data.selection = selection;
621 selection_data.target = target;
622 selection_data.data = NULL;
623 selection_data.length = -1;
625 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
627 if (owner_widget != NULL)
629 gtk_selection_invoke_handler (owner_widget,
633 gtk_selection_retrieval_report (info,
635 selection_data.format,
637 selection_data.length,
640 g_free (selection_data.data);
647 /* Otherwise, we need to go through X */
649 current_retrievals = g_list_append (current_retrievals, info);
650 gdk_selection_convert (widget->window, selection, target, time);
651 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
656 /*************************************************************
657 * gtk_selection_data_set:
658 * Store new data into a GtkSelectionData object. Should
659 * _only_ by called from a selection handler callback.
660 * Null terminates the stored data.
662 * type: the type of selection data
663 * format: format (number of bits in a unit)
664 * data: pointer to the data (will be copied)
665 * length: length of the data
667 *************************************************************/
670 gtk_selection_data_set (GtkSelectionData *selection_data,
676 if (selection_data->data)
677 g_free (selection_data->data);
679 selection_data->type = type;
680 selection_data->format = format;
684 selection_data->data = g_new (guchar, length+1);
685 memcpy (selection_data->data, data, length);
686 selection_data->data[length] = 0;
690 g_return_if_fail (length <= 0);
693 selection_data->data = NULL;
695 selection_data->data = g_strdup("");
698 selection_data->length = length;
701 /*************************************************************
702 * gtk_selection_init:
703 * Initialize local variables
707 *************************************************************/
710 gtk_selection_init (void)
712 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
713 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
714 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
715 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
718 /*************************************************************
719 * gtk_selection_clear:
720 * Handler for "selection_clear_event"
725 *************************************************************/
728 gtk_selection_clear (GtkWidget *widget,
729 GdkEventSelection *event)
731 /* FIXME: there can be a problem if we change the selection
732 via gtk_selection_owner_set after another client claims
733 the selection, but before we get the notification event.
734 Tk filters based on serial #'s, which aren't retained by
735 GTK. Filtering based on time's will be inherently
736 somewhat unreliable. */
739 GtkSelectionInfo *selection_info;
741 tmp_list = current_selections;
744 selection_info = (GtkSelectionInfo *)tmp_list->data;
746 if ((selection_info->selection == event->selection) &&
747 (selection_info->widget == widget))
750 tmp_list = tmp_list->next;
755 if (selection_info->time > event->time)
756 return FALSE; /* return FALSE to indicate that
757 * the selection was out of date,
758 * and this clear should be ignored */
761 current_selections = g_list_remove_link (current_selections, tmp_list);
762 g_list_free (tmp_list);
763 g_free (selection_info);
771 /*************************************************************
772 * gtk_selection_request:
773 * Handler for "selection_request_event"
778 *************************************************************/
781 gtk_selection_request (GtkWidget *widget,
782 GdkEventSelection *event)
790 gtk_selection_init ();
792 /* Check if we own selection */
794 tmp_list = current_selections;
797 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
799 if ((selection_info->selection == event->selection) &&
800 (selection_info->widget == widget))
803 tmp_list = tmp_list->next;
806 if (tmp_list == NULL)
809 info = g_new(GtkIncrInfo, 1);
811 info->widget = widget;
812 info->selection = event->selection;
815 /* Create GdkWindow structure for the requestor */
817 info->requestor = gdk_window_lookup (event->requestor);
818 if (!info->requestor)
819 info->requestor = gdk_window_foreign_new (event->requestor);
821 /* Determine conversions we need to perform */
823 if (event->target == gtk_selection_atoms[MULTIPLE])
830 if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
831 0, GTK_SELECTION_MAX_SIZE, FALSE,
832 &type, &format, &length, &mult_atoms))
834 gdk_selection_send_notify (event->requestor, event->selection,
835 event->target, GDK_NONE, event->time);
841 info->num_conversions = length / (2*sizeof (GdkAtom));
842 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
844 for (i=0; i<info->num_conversions; i++)
846 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
847 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
850 else /* only a single conversion */
852 info->conversions = g_new (GtkIncrConversion, 1);
853 info->num_conversions = 1;
854 info->conversions[0].target = event->target;
855 info->conversions[0].property = event->property;
856 mult_atoms = (guchar *)info->conversions;
859 /* Loop through conversions and determine which of these are big
860 enough to require doing them via INCR */
861 for (i=0; i<info->num_conversions; i++)
863 GtkSelectionData data;
866 data.selection = event->selection;
867 data.target = info->conversions[i].target;
871 #ifdef DEBUG_SELECTION
872 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
873 event->selection, info->conversions[i].target,
874 gdk_atom_name(info->conversions[i].target),
875 event->requestor, event->property);
878 gtk_selection_invoke_handler (widget, &data, event->time);
882 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
883 info->conversions[i].property = GDK_NONE;
887 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
889 items = (data.length + data.format/8 - 1) / (data.format/8);
891 if (data.length > GTK_SELECTION_MAX_SIZE)
893 /* Sending via INCR */
895 info->conversions[i].offset = 0;
896 info->conversions[i].data = data;
899 gdk_property_change (info->requestor,
900 info->conversions[i].property,
901 gtk_selection_atoms[INCR],
903 GDK_PROP_MODE_REPLACE,
904 (guchar *)&items, 1);
908 info->conversions[i].offset = -1;
910 gdk_property_change (info->requestor,
911 info->conversions[i].property,
914 GDK_PROP_MODE_REPLACE,
921 /* If we have some INCR's, we need to send the rest of the data in
924 if (info->num_incrs > 0)
926 /* FIXME: this could be dangerous if window doesn't still
929 #ifdef DEBUG_SELECTION
930 g_message ("Starting INCR...");
933 gdk_window_set_events (info->requestor,
934 gdk_window_get_events (info->requestor) |
935 GDK_PROPERTY_CHANGE_MASK);
936 current_incrs = g_list_append (current_incrs, info);
937 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
940 /* If it was a MULTIPLE request, set the property to indicate which
941 conversions succeeded */
942 if (event->target == gtk_selection_atoms[MULTIPLE])
944 gdk_property_change (info->requestor, event->property,
945 GDK_SELECTION_TYPE_ATOM, 8*sizeof(GdkAtom),
946 GDK_PROP_MODE_REPLACE,
947 mult_atoms, 2*info->num_conversions);
951 gdk_selection_send_notify (event->requestor, event->selection, event->target,
952 event->property, event->time);
954 if (info->num_incrs == 0)
956 g_free (info->conversions);
963 /*************************************************************
964 * gtk_selection_incr_event:
965 * Called whenever an PropertyNotify event occurs for an
966 * GdkWindow with user_data == NULL. These will be notifications
967 * that a window we are sending the selection to via the
968 * INCR protocol has deleted a property and is ready for
972 * window: the requestor window
973 * event: the property event structure
976 *************************************************************/
979 gtk_selection_incr_event (GdkWindow *window,
980 GdkEventProperty *event)
989 if (event->state != GDK_PROPERTY_DELETE)
992 #ifdef DEBUG_SELECTION
993 g_message ("PropertyDelete, property %ld", event->atom);
996 /* Now find the appropriate ongoing INCR */
997 tmp_list = current_incrs;
1000 info = (GtkIncrInfo *)tmp_list->data;
1001 if (info->requestor == event->window)
1004 tmp_list = tmp_list->next;
1007 if (tmp_list == NULL)
1010 /* Find out which target this is for */
1011 for (i=0; i<info->num_conversions; i++)
1013 if (info->conversions[i].property == event->atom &&
1014 info->conversions[i].offset != -1)
1016 info->idle_time = 0;
1018 if (info->conversions[i].offset == -2) /* only the last 0-length
1026 num_bytes = info->conversions[i].data.length -
1027 info->conversions[i].offset;
1028 buffer = info->conversions[i].data.data +
1029 info->conversions[i].offset;
1031 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1033 num_bytes = GTK_SELECTION_MAX_SIZE;
1034 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1037 info->conversions[i].offset = -2;
1039 #ifdef DEBUG_SELECTION
1040 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1041 num_bytes, info->conversions[i].offset,
1042 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1044 gdk_property_change (info->requestor, event->atom,
1045 info->conversions[i].data.type,
1046 info->conversions[i].data.format,
1047 GDK_PROP_MODE_REPLACE,
1049 (num_bytes + info->conversions[i].data.format/8 - 1) /
1050 (info->conversions[i].data.format/8));
1052 if (info->conversions[i].offset == -2)
1054 g_free (info->conversions[i].data.data);
1055 info->conversions[i].data.data = NULL;
1061 info->conversions[i].offset = -1;
1067 /* Check if we're finished with all the targets */
1069 if (info->num_incrs == 0)
1071 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1072 g_list_free (tmp_list);
1073 /* Let the timeout free it */
1079 /*************************************************************
1080 * gtk_selection_incr_timeout:
1081 * Timeout callback for the sending portion of the INCR
1084 * info: Information about this incr
1086 *************************************************************/
1089 gtk_selection_incr_timeout (GtkIncrInfo *info)
1094 GDK_THREADS_ENTER ();
1096 /* Determine if retrieval has finished by checking if it still in
1097 list of pending retrievals */
1099 tmp_list = current_incrs;
1102 if (info == (GtkIncrInfo *)tmp_list->data)
1104 tmp_list = tmp_list->next;
1107 /* If retrieval is finished */
1108 if (!tmp_list || info->idle_time >= 5)
1110 if (tmp_list && info->idle_time >= 5)
1112 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1113 g_list_free (tmp_list);
1116 g_free (info->conversions);
1117 /* FIXME: we should check if requestor window is still in use,
1118 and if not, remove it? */
1122 retval = FALSE; /* remove timeout */
1128 retval = TRUE; /* timeout will happen again */
1131 GDK_THREADS_LEAVE ();
1136 /*************************************************************
1137 * gtk_selection_notify:
1138 * Handler for "selection_notify_event" signals on windows
1139 * where a retrieval is currently in process. The selection
1140 * owner has responded to our conversion request.
1142 * widget: Widget getting signal
1143 * event: Selection event structure
1144 * info: Information about this retrieval
1146 * was event handled?
1147 *************************************************************/
1150 gtk_selection_notify (GtkWidget *widget,
1151 GdkEventSelection *event)
1154 GtkRetrievalInfo *info;
1160 #ifdef DEBUG_SELECTION
1161 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1162 event->selection, event->target, event->property);
1165 tmp_list = current_retrievals;
1168 info = (GtkRetrievalInfo *)tmp_list->data;
1169 if (info->widget == widget && info->selection == event->selection)
1171 tmp_list = tmp_list->next;
1174 if (!tmp_list) /* no retrieval in progress */
1177 if (event->property == GDK_NONE)
1179 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1180 g_list_free (tmp_list);
1181 /* structure will be freed in timeout */
1182 gtk_selection_retrieval_report (info,
1183 GDK_NONE, 0, NULL, -1, event->time);
1188 length = gdk_selection_property_get (widget->window, &buffer,
1191 if (type == gtk_selection_atoms[INCR])
1193 /* The remainder of the selection will come through PropertyNotify
1196 info->notify_time = event->time;
1197 info->idle_time = 0;
1198 info->offset = 0; /* Mark as OK to proceed */
1199 gdk_window_set_events (widget->window,
1200 gdk_window_get_events (widget->window)
1201 | GDK_PROPERTY_CHANGE_MASK);
1205 /* We don't delete the info structure - that will happen in timeout */
1206 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1207 g_list_free (tmp_list);
1209 info->offset = length;
1210 gtk_selection_retrieval_report (info,
1212 buffer, length, event->time);
1215 gdk_property_delete (widget->window, event->property);
1222 /*************************************************************
1223 * gtk_selection_property_notify:
1224 * Handler for "property_notify_event" signals on windows
1225 * where a retrieval is currently in process. The selection
1226 * owner has added more data.
1228 * widget: Widget getting signal
1229 * event: Property event structure
1230 * info: Information about this retrieval
1232 * was event handled?
1233 *************************************************************/
1236 gtk_selection_property_notify (GtkWidget *widget,
1237 GdkEventProperty *event)
1240 GtkRetrievalInfo *info;
1246 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1247 (event->atom != gdk_selection_property)) /* not the right property */
1250 #ifdef DEBUG_SELECTION
1251 g_message ("PropertyNewValue, property %ld",
1255 tmp_list = current_retrievals;
1258 info = (GtkRetrievalInfo *)tmp_list->data;
1259 if (info->widget == widget)
1261 tmp_list = tmp_list->next;
1264 if (!tmp_list) /* No retrieval in progress */
1267 if (info->offset < 0) /* We haven't got the SelectionNotify
1268 for this retrieval yet */
1271 info->idle_time = 0;
1273 length = gdk_selection_property_get (widget->window, &new_buffer,
1275 gdk_property_delete (widget->window, event->atom);
1277 /* We could do a lot better efficiency-wise by paying attention to
1278 what length was sent in the initial INCR transaction, instead of
1279 doing memory allocation at every step. But its only guaranteed to
1280 be a _lower bound_ (pretty useless!) */
1282 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1284 /* Info structure will be freed in timeout */
1285 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1286 g_list_free (tmp_list);
1287 gtk_selection_retrieval_report (info,
1289 (type == GDK_NONE) ? NULL : info->buffer,
1290 (type == GDK_NONE) ? -1 : info->offset,
1293 else /* append on newly arrived data */
1297 #ifdef DEBUG_SELECTION
1298 g_message ("Start - Adding %d bytes at offset 0",
1301 info->buffer = new_buffer;
1302 info->offset = length;
1307 #ifdef DEBUG_SELECTION
1308 g_message ("Appending %d bytes at offset %d",
1309 length,info->offset);
1311 /* We copy length+1 bytes to preserve guaranteed null termination */
1312 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1313 memcpy (info->buffer + info->offset, new_buffer, length+1);
1314 info->offset += length;
1315 g_free (new_buffer);
1322 /*************************************************************
1323 * gtk_selection_retrieval_timeout:
1324 * Timeout callback while receiving a selection.
1326 * info: Information about this retrieval
1328 *************************************************************/
1331 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1336 GDK_THREADS_ENTER ();
1338 /* Determine if retrieval has finished by checking if it still in
1339 list of pending retrievals */
1341 tmp_list = current_retrievals;
1344 if (info == (GtkRetrievalInfo *)tmp_list->data)
1346 tmp_list = tmp_list->next;
1349 /* If retrieval is finished */
1350 if (!tmp_list || info->idle_time >= 5)
1352 if (tmp_list && info->idle_time >= 5)
1354 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1355 g_list_free (tmp_list);
1356 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1359 g_free (info->buffer);
1362 retval = FALSE; /* remove timeout */
1368 retval = TRUE; /* timeout will happen again */
1371 GDK_THREADS_LEAVE ();
1376 /*************************************************************
1377 * gtk_selection_retrieval_report:
1378 * Emits a "selection_received" signal.
1380 * info: information about the retrieval that completed
1381 * buffer: buffer containing data (NULL => errror)
1382 * time: timestamp for data in buffer
1384 *************************************************************/
1387 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1388 GdkAtom type, gint format,
1389 guchar *buffer, gint length,
1392 GtkSelectionData data;
1394 data.selection = info->selection;
1395 data.target = info->target;
1397 data.format = format;
1399 data.length = length;
1402 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1403 "selection_received",
1407 /*************************************************************
1408 * gtk_selection_invoke_handler:
1409 * Finds and invokes handler for specified
1410 * widget/selection/target combination, calls
1411 * gtk_selection_default_handler if none exists.
1414 * widget: selection owner
1415 * data: selection data [INOUT]
1416 * time: time from requeset
1419 * Number of bytes written to buffer, -1 if error
1420 *************************************************************/
1423 gtk_selection_invoke_handler (GtkWidget *widget,
1424 GtkSelectionData *data,
1427 GtkTargetList *target_list;
1431 g_return_if_fail (widget != NULL);
1433 target_list = gtk_selection_target_list_get (widget, data->selection);
1435 gtk_target_list_find (target_list, data->target, &info))
1437 gtk_signal_emit_by_name (GTK_OBJECT (widget),
1443 gtk_selection_default_handler (widget, data);
1446 /*************************************************************
1447 * gtk_selection_default_handler:
1448 * Handles some default targets that exist for any widget
1449 * If it can't fit results into buffer, returns -1. This
1450 * won't happen in any conceivable case, since it would
1451 * require 1000 selection targets!
1454 * widget: selection owner
1455 * data: selection data [INOUT]
1457 *************************************************************/
1460 gtk_selection_default_handler (GtkWidget *widget,
1461 GtkSelectionData *data)
1463 if (data->target == gtk_selection_atoms[TIMESTAMP])
1465 /* Time which was used to obtain selection */
1467 GtkSelectionInfo *selection_info;
1469 tmp_list = current_selections;
1472 selection_info = (GtkSelectionInfo *)tmp_list->data;
1473 if ((selection_info->widget == widget) &&
1474 (selection_info->selection == data->selection))
1476 gtk_selection_data_set (data,
1477 GDK_SELECTION_TYPE_INTEGER,
1479 (guchar *)&selection_info->time,
1484 tmp_list = tmp_list->next;
1489 else if (data->target == gtk_selection_atoms[TARGETS])
1491 /* List of all targets supported for this widget/selection pair */
1495 GtkTargetList *target_list;
1496 GtkTargetPair *pair;
1499 target_list = gtk_selection_target_list_get (widget,
1501 tmp_list = target_list->list;
1505 tmp_list = tmp_list->next;
1508 data->type = GDK_SELECTION_TYPE_ATOM;
1509 data->format = 8*sizeof (GdkAtom);
1510 data->length = count*sizeof (GdkAtom);
1512 p = g_new (GdkAtom, count);
1513 data->data = (guchar *)p;
1515 *p++ = gtk_selection_atoms[TIMESTAMP];
1516 *p++ = gtk_selection_atoms[TARGETS];
1517 *p++ = gtk_selection_atoms[MULTIPLE];
1519 tmp_list = target_list->list;
1522 pair = (GtkTargetPair *)tmp_list->data;
1523 *p++ = pair->target;
1525 tmp_list = tmp_list->next;
1536 gtk_selection_data_copy (GtkSelectionData *data)
1538 GtkSelectionData *new_data;
1540 g_return_val_if_fail (data != NULL, NULL);
1542 new_data = g_new (GtkSelectionData, 1);
1549 gtk_selection_data_free (GtkSelectionData *data)
1551 g_return_if_fail (data != NULL);