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 GtkTargetPair *pair = tmp_list->data;
185 tmp_list = tmp_list->next;
188 g_list_free (list->list);
194 gtk_target_list_add (GtkTargetList *list,
201 g_return_if_fail (list != NULL);
203 pair = g_new (GtkTargetPair, 1);
204 pair->target = target;
208 list->list = g_list_append (list->list, pair);
212 gtk_target_list_add_table (GtkTargetList *list,
213 const GtkTargetEntry *targets,
218 for (i=ntargets-1; i >= 0; i--)
220 GtkTargetPair *pair = g_new (GtkTargetPair, 1);
221 pair->target = gdk_atom_intern (targets[i].target, FALSE);
222 pair->flags = targets[i].flags;
223 pair->info = targets[i].info;
225 list->list = g_list_prepend (list->list, pair);
230 gtk_target_list_remove (GtkTargetList *list,
235 g_return_if_fail (list != NULL);
237 tmp_list = list->list;
240 GtkTargetPair *pair = tmp_list->data;
242 if (pair->target == target)
246 list->list = g_list_remove (list->list, tmp_list);
247 g_list_free_1 (tmp_list);
252 tmp_list = tmp_list->next;
257 gtk_target_list_find (GtkTargetList *list,
261 GList *tmp_list = list->list;
264 GtkTargetPair *pair = tmp_list->data;
266 if (pair->target == target)
271 tmp_list = tmp_list->next;
278 /*************************************************************
279 * gtk_selection_owner_set:
280 * Claim ownership of a selection.
282 * widget: new selection owner
283 * selection: which selection
284 * time: time (use GDK_CURRENT_TIME only if necessary)
287 *************************************************************/
290 gtk_selection_owner_set (GtkWidget *widget,
295 GtkWidget *old_owner;
296 GtkSelectionInfo *selection_info = NULL;
303 if (!GTK_WIDGET_REALIZED (widget))
304 gtk_widget_realize (widget);
306 window = widget->window;
309 tmp_list = current_selections;
312 selection_info = (GtkSelectionInfo *)tmp_list->data;
314 if (selection_info->selection == selection)
317 tmp_list = tmp_list->next;
320 if (tmp_list == NULL)
321 selection_info = NULL;
323 if (selection_info->widget == widget)
326 if (gdk_selection_owner_set (window, selection, time, TRUE))
334 old_owner = selection_info->widget;
335 current_selections = g_list_remove_link (current_selections,
337 g_list_free (tmp_list);
338 g_free (selection_info);
343 if (selection_info == NULL)
345 selection_info = g_new (GtkSelectionInfo, 1);
346 selection_info->selection = selection;
347 selection_info->widget = widget;
348 selection_info->time = time;
349 current_selections = g_list_append (current_selections,
354 old_owner = selection_info->widget;
355 selection_info->widget = widget;
356 selection_info->time = time;
359 /* If another widget in the application lost the selection,
360 * send it a GDK_SELECTION_CLEAR event, unless we're setting
361 * the owner to None, in which case an event will be sent */
362 if (old_owner && (widget != NULL))
364 GdkEventSelection event;
366 event.type = GDK_SELECTION_CLEAR;
367 event.window = old_owner->window;
368 event.selection = selection;
371 gtk_widget_event (old_owner, (GdkEvent *) &event);
379 /*************************************************************
380 * gtk_selection_add_target
381 * Add specified target to list of supported targets
384 * widget: The widget for which this target applies
387 * info: guint to pass to to the selection_get signal
390 *************************************************************/
392 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
394 struct _GtkSelectionTargetList {
399 static GtkTargetList *
400 gtk_selection_target_list_get (GtkWidget *widget,
403 GtkSelectionTargetList *sellist;
407 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
412 sellist = tmp_list->data;
413 if (sellist->selection == selection)
414 return sellist->list;
415 tmp_list = tmp_list->next;
418 sellist = g_new (GtkSelectionTargetList, 1);
419 sellist->selection = selection;
420 sellist->list = gtk_target_list_new (NULL, 0);
422 lists = g_list_prepend (lists, sellist);
423 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
425 return sellist->list;
429 gtk_selection_target_list_remove (GtkWidget *widget)
431 GtkSelectionTargetList *sellist;
435 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
440 sellist = tmp_list->data;
442 gtk_target_list_unref (sellist->list);
445 tmp_list = tmp_list->next;
449 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL);
453 gtk_selection_add_target (GtkWidget *widget,
460 g_return_if_fail (widget != NULL);
462 list = gtk_selection_target_list_get (widget, selection);
463 gtk_target_list_add (list, target, 0, info);
467 gtk_selection_add_targets (GtkWidget *widget,
469 const GtkTargetEntry *targets,
474 g_return_if_fail (widget != NULL);
475 g_return_if_fail (targets != NULL);
477 list = gtk_selection_target_list_get (widget, selection);
478 gtk_target_list_add_table (list, targets, ntargets);
481 /*************************************************************
482 * gtk_selection_remove_all:
483 * Removes all handlers and unsets ownership of all
484 * selections for a widget. Called when widget is being
490 *************************************************************/
493 gtk_selection_remove_all (GtkWidget *widget)
497 GtkSelectionInfo *selection_info;
499 /* Remove pending requests/incrs for this widget */
501 tmp_list = current_incrs;
504 next = tmp_list->next;
505 if (((GtkIncrInfo *)tmp_list->data)->widget == widget)
507 current_incrs = g_list_remove_link (current_incrs, tmp_list);
508 /* structure will be freed in timeout */
509 g_list_free (tmp_list);
514 tmp_list = current_retrievals;
517 next = tmp_list->next;
518 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
520 current_retrievals = g_list_remove_link (current_retrievals,
522 /* structure will be freed in timeout */
523 g_list_free (tmp_list);
528 /* Disclaim ownership of any selections */
530 tmp_list = current_selections;
533 next = tmp_list->next;
534 selection_info = (GtkSelectionInfo *)tmp_list->data;
536 if (selection_info->widget == widget)
538 gdk_selection_owner_set (NULL,
539 selection_info->selection,
540 GDK_CURRENT_TIME, FALSE);
541 current_selections = g_list_remove_link (current_selections,
543 g_list_free (tmp_list);
544 g_free (selection_info);
550 /* Remove all selection lists */
551 gtk_selection_target_list_remove (widget);
554 /*************************************************************
555 * gtk_selection_convert:
556 * Request the contents of a selection. When received,
557 * a "selection_received" signal will be generated.
560 * widget: The widget which acts as requestor
561 * selection: Which selection to get
562 * target: Form of information desired (e.g., STRING)
563 * time: Time of request (usually of triggering event)
564 * In emergency, you could use GDK_CURRENT_TIME
567 * TRUE if requested succeeded. FALSE if we could not process
568 * request. (e.g., there was already a request in process for
570 *************************************************************/
573 gtk_selection_convert (GtkWidget *widget,
578 GtkRetrievalInfo *info;
580 GdkWindow *owner_window;
582 g_return_val_if_fail (widget != NULL, FALSE);
585 gtk_selection_init ();
587 if (!GTK_WIDGET_REALIZED (widget))
588 gtk_widget_realize (widget);
590 /* Check to see if there are already any retrievals in progress for
591 this widget. If we changed GDK to use the selection for the
592 window property in which to store the retrieved information, then
593 we could support multiple retrievals for different selections.
594 This might be useful for DND. */
596 tmp_list = current_retrievals;
599 info = (GtkRetrievalInfo *)tmp_list->data;
600 if (info->widget == widget)
602 tmp_list = tmp_list->next;
605 info = g_new (GtkRetrievalInfo, 1);
607 info->widget = widget;
608 info->selection = selection;
609 info->target = target;
613 /* Check if this process has current owner. If so, call handler
614 procedure directly to avoid deadlocks with INCR. */
616 owner_window = gdk_selection_owner_get (selection);
618 if (owner_window != NULL)
620 GtkWidget *owner_widget;
621 GtkSelectionData selection_data;
623 selection_data.selection = selection;
624 selection_data.target = target;
625 selection_data.data = NULL;
626 selection_data.length = -1;
628 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
630 if (owner_widget != NULL)
632 gtk_selection_invoke_handler (owner_widget,
636 gtk_selection_retrieval_report (info,
638 selection_data.format,
640 selection_data.length,
643 g_free (selection_data.data);
650 /* Otherwise, we need to go through X */
652 current_retrievals = g_list_append (current_retrievals, info);
653 gdk_selection_convert (widget->window, selection, target, time);
654 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
659 /*************************************************************
660 * gtk_selection_data_set:
661 * Store new data into a GtkSelectionData object. Should
662 * _only_ by called from a selection handler callback.
663 * Null terminates the stored data.
665 * type: the type of selection data
666 * format: format (number of bits in a unit)
667 * data: pointer to the data (will be copied)
668 * length: length of the data
670 *************************************************************/
673 gtk_selection_data_set (GtkSelectionData *selection_data,
679 if (selection_data->data)
680 g_free (selection_data->data);
682 selection_data->type = type;
683 selection_data->format = format;
687 selection_data->data = g_new (guchar, length+1);
688 memcpy (selection_data->data, data, length);
689 selection_data->data[length] = 0;
693 g_return_if_fail (length <= 0);
696 selection_data->data = NULL;
698 selection_data->data = g_strdup("");
701 selection_data->length = length;
704 /*************************************************************
705 * gtk_selection_init:
706 * Initialize local variables
710 *************************************************************/
713 gtk_selection_init (void)
715 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
716 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
717 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
718 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
721 /*************************************************************
722 * gtk_selection_clear:
723 * Handler for "selection_clear_event"
728 *************************************************************/
731 gtk_selection_clear (GtkWidget *widget,
732 GdkEventSelection *event)
734 /* FIXME: there can be a problem if we change the selection
735 via gtk_selection_owner_set after another client claims
736 the selection, but before we get the notification event.
737 Tk filters based on serial #'s, which aren't retained by
738 GTK. Filtering based on time's will be inherently
739 somewhat unreliable. */
742 GtkSelectionInfo *selection_info = NULL;
744 tmp_list = current_selections;
747 selection_info = (GtkSelectionInfo *)tmp_list->data;
749 if ((selection_info->selection == event->selection) &&
750 (selection_info->widget == widget))
753 tmp_list = tmp_list->next;
758 if (selection_info->time > event->time)
759 return FALSE; /* return FALSE to indicate that
760 * the selection was out of date,
761 * and this clear should be ignored */
764 current_selections = g_list_remove_link (current_selections, tmp_list);
765 g_list_free (tmp_list);
766 g_free (selection_info);
774 /*************************************************************
775 * gtk_selection_request:
776 * Handler for "selection_request_event"
781 *************************************************************/
784 gtk_selection_request (GtkWidget *widget,
785 GdkEventSelection *event)
793 gtk_selection_init ();
795 /* Check if we own selection */
797 tmp_list = current_selections;
800 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
802 if ((selection_info->selection == event->selection) &&
803 (selection_info->widget == widget))
806 tmp_list = tmp_list->next;
809 if (tmp_list == NULL)
812 info = g_new(GtkIncrInfo, 1);
814 info->widget = widget;
815 info->selection = event->selection;
818 /* Create GdkWindow structure for the requestor */
820 info->requestor = gdk_window_lookup (event->requestor);
821 if (!info->requestor)
822 info->requestor = gdk_window_foreign_new (event->requestor);
824 /* Determine conversions we need to perform */
826 if (event->target == gtk_selection_atoms[MULTIPLE])
833 if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
834 0, GTK_SELECTION_MAX_SIZE, FALSE,
835 &type, &format, &length, &mult_atoms))
837 gdk_selection_send_notify (event->requestor, event->selection,
838 event->target, GDK_NONE, event->time);
844 info->num_conversions = length / (2*sizeof (GdkAtom));
845 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
847 for (i=0; i<info->num_conversions; i++)
849 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
850 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
853 else /* only a single conversion */
855 info->conversions = g_new (GtkIncrConversion, 1);
856 info->num_conversions = 1;
857 info->conversions[0].target = event->target;
858 info->conversions[0].property = event->property;
859 mult_atoms = (guchar *)info->conversions;
862 /* Loop through conversions and determine which of these are big
863 enough to require doing them via INCR */
864 for (i=0; i<info->num_conversions; i++)
866 GtkSelectionData data;
869 data.selection = event->selection;
870 data.target = info->conversions[i].target;
874 #ifdef DEBUG_SELECTION
875 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
876 event->selection, info->conversions[i].target,
877 gdk_atom_name(info->conversions[i].target),
878 event->requestor, event->property);
881 gtk_selection_invoke_handler (widget, &data, event->time);
885 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
886 info->conversions[i].property = GDK_NONE;
890 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
892 items = (data.length + data.format/8 - 1) / (data.format/8);
894 if (data.length > GTK_SELECTION_MAX_SIZE)
896 /* Sending via INCR */
898 info->conversions[i].offset = 0;
899 info->conversions[i].data = data;
902 gdk_property_change (info->requestor,
903 info->conversions[i].property,
904 gtk_selection_atoms[INCR],
906 GDK_PROP_MODE_REPLACE,
907 (guchar *)&items, 1);
911 info->conversions[i].offset = -1;
913 gdk_property_change (info->requestor,
914 info->conversions[i].property,
917 GDK_PROP_MODE_REPLACE,
924 /* If we have some INCR's, we need to send the rest of the data in
927 if (info->num_incrs > 0)
929 /* FIXME: this could be dangerous if window doesn't still
932 #ifdef DEBUG_SELECTION
933 g_message ("Starting INCR...");
936 gdk_window_set_events (info->requestor,
937 gdk_window_get_events (info->requestor) |
938 GDK_PROPERTY_CHANGE_MASK);
939 current_incrs = g_list_append (current_incrs, info);
940 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
943 /* If it was a MULTIPLE request, set the property to indicate which
944 conversions succeeded */
945 if (event->target == gtk_selection_atoms[MULTIPLE])
947 gdk_property_change (info->requestor, event->property,
948 GDK_SELECTION_TYPE_ATOM, 8*sizeof(GdkAtom),
949 GDK_PROP_MODE_REPLACE,
950 mult_atoms, 2*info->num_conversions);
954 gdk_selection_send_notify (event->requestor, event->selection, event->target,
955 event->property, event->time);
957 if (info->num_incrs == 0)
959 g_free (info->conversions);
966 /*************************************************************
967 * gtk_selection_incr_event:
968 * Called whenever an PropertyNotify event occurs for an
969 * GdkWindow with user_data == NULL. These will be notifications
970 * that a window we are sending the selection to via the
971 * INCR protocol has deleted a property and is ready for
975 * window: the requestor window
976 * event: the property event structure
979 *************************************************************/
982 gtk_selection_incr_event (GdkWindow *window,
983 GdkEventProperty *event)
986 GtkIncrInfo *info = NULL;
992 if (event->state != GDK_PROPERTY_DELETE)
995 #ifdef DEBUG_SELECTION
996 g_message ("PropertyDelete, property %ld", event->atom);
999 /* Now find the appropriate ongoing INCR */
1000 tmp_list = current_incrs;
1003 info = (GtkIncrInfo *)tmp_list->data;
1004 if (info->requestor == event->window)
1007 tmp_list = tmp_list->next;
1010 if (tmp_list == NULL)
1013 /* Find out which target this is for */
1014 for (i=0; i<info->num_conversions; i++)
1016 if (info->conversions[i].property == event->atom &&
1017 info->conversions[i].offset != -1)
1019 info->idle_time = 0;
1021 if (info->conversions[i].offset == -2) /* only the last 0-length
1029 num_bytes = info->conversions[i].data.length -
1030 info->conversions[i].offset;
1031 buffer = info->conversions[i].data.data +
1032 info->conversions[i].offset;
1034 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1036 num_bytes = GTK_SELECTION_MAX_SIZE;
1037 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1040 info->conversions[i].offset = -2;
1042 #ifdef DEBUG_SELECTION
1043 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1044 num_bytes, info->conversions[i].offset,
1045 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1047 gdk_property_change (info->requestor, event->atom,
1048 info->conversions[i].data.type,
1049 info->conversions[i].data.format,
1050 GDK_PROP_MODE_REPLACE,
1052 (num_bytes + info->conversions[i].data.format/8 - 1) /
1053 (info->conversions[i].data.format/8));
1055 if (info->conversions[i].offset == -2)
1057 g_free (info->conversions[i].data.data);
1058 info->conversions[i].data.data = NULL;
1064 info->conversions[i].offset = -1;
1070 /* Check if we're finished with all the targets */
1072 if (info->num_incrs == 0)
1074 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1075 g_list_free (tmp_list);
1076 /* Let the timeout free it */
1082 /*************************************************************
1083 * gtk_selection_incr_timeout:
1084 * Timeout callback for the sending portion of the INCR
1087 * info: Information about this incr
1089 *************************************************************/
1092 gtk_selection_incr_timeout (GtkIncrInfo *info)
1097 GDK_THREADS_ENTER ();
1099 /* Determine if retrieval has finished by checking if it still in
1100 list of pending retrievals */
1102 tmp_list = current_incrs;
1105 if (info == (GtkIncrInfo *)tmp_list->data)
1107 tmp_list = tmp_list->next;
1110 /* If retrieval is finished */
1111 if (!tmp_list || info->idle_time >= 5)
1113 if (tmp_list && info->idle_time >= 5)
1115 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1116 g_list_free (tmp_list);
1119 g_free (info->conversions);
1120 /* FIXME: we should check if requestor window is still in use,
1121 and if not, remove it? */
1125 retval = FALSE; /* remove timeout */
1131 retval = TRUE; /* timeout will happen again */
1134 GDK_THREADS_LEAVE ();
1139 /*************************************************************
1140 * gtk_selection_notify:
1141 * Handler for "selection_notify_event" signals on windows
1142 * where a retrieval is currently in process. The selection
1143 * owner has responded to our conversion request.
1145 * widget: Widget getting signal
1146 * event: Selection event structure
1147 * info: Information about this retrieval
1149 * was event handled?
1150 *************************************************************/
1153 gtk_selection_notify (GtkWidget *widget,
1154 GdkEventSelection *event)
1157 GtkRetrievalInfo *info = NULL;
1163 #ifdef DEBUG_SELECTION
1164 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1165 event->selection, event->target, event->property);
1168 tmp_list = current_retrievals;
1171 info = (GtkRetrievalInfo *)tmp_list->data;
1172 if (info->widget == widget && info->selection == event->selection)
1174 tmp_list = tmp_list->next;
1177 if (!tmp_list) /* no retrieval in progress */
1180 if (event->property == GDK_NONE)
1182 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1183 g_list_free (tmp_list);
1184 /* structure will be freed in timeout */
1185 gtk_selection_retrieval_report (info,
1186 GDK_NONE, 0, NULL, -1, event->time);
1191 length = gdk_selection_property_get (widget->window, &buffer,
1194 if (type == gtk_selection_atoms[INCR])
1196 /* The remainder of the selection will come through PropertyNotify
1199 info->notify_time = event->time;
1200 info->idle_time = 0;
1201 info->offset = 0; /* Mark as OK to proceed */
1202 gdk_window_set_events (widget->window,
1203 gdk_window_get_events (widget->window)
1204 | GDK_PROPERTY_CHANGE_MASK);
1208 /* We don't delete the info structure - that will happen in timeout */
1209 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1210 g_list_free (tmp_list);
1212 info->offset = length;
1213 gtk_selection_retrieval_report (info,
1215 buffer, length, event->time);
1218 gdk_property_delete (widget->window, event->property);
1225 /*************************************************************
1226 * gtk_selection_property_notify:
1227 * Handler for "property_notify_event" signals on windows
1228 * where a retrieval is currently in process. The selection
1229 * owner has added more data.
1231 * widget: Widget getting signal
1232 * event: Property event structure
1233 * info: Information about this retrieval
1235 * was event handled?
1236 *************************************************************/
1239 gtk_selection_property_notify (GtkWidget *widget,
1240 GdkEventProperty *event)
1243 GtkRetrievalInfo *info = NULL;
1249 g_return_val_if_fail (widget != NULL, FALSE);
1250 g_return_val_if_fail (event != NULL, FALSE);
1252 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1253 (event->atom != gdk_selection_property)) /* not the right property */
1256 #ifdef DEBUG_SELECTION
1257 g_message ("PropertyNewValue, property %ld",
1261 tmp_list = current_retrievals;
1264 info = (GtkRetrievalInfo *)tmp_list->data;
1265 if (info->widget == widget)
1267 tmp_list = tmp_list->next;
1270 if (!tmp_list) /* No retrieval in progress */
1273 if (info->offset < 0) /* We haven't got the SelectionNotify
1274 for this retrieval yet */
1277 info->idle_time = 0;
1279 length = gdk_selection_property_get (widget->window, &new_buffer,
1281 gdk_property_delete (widget->window, event->atom);
1283 /* We could do a lot better efficiency-wise by paying attention to
1284 what length was sent in the initial INCR transaction, instead of
1285 doing memory allocation at every step. But its only guaranteed to
1286 be a _lower bound_ (pretty useless!) */
1288 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1290 /* Info structure will be freed in timeout */
1291 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1292 g_list_free (tmp_list);
1293 gtk_selection_retrieval_report (info,
1295 (type == GDK_NONE) ? NULL : info->buffer,
1296 (type == GDK_NONE) ? -1 : info->offset,
1299 else /* append on newly arrived data */
1303 #ifdef DEBUG_SELECTION
1304 g_message ("Start - Adding %d bytes at offset 0",
1307 info->buffer = new_buffer;
1308 info->offset = length;
1313 #ifdef DEBUG_SELECTION
1314 g_message ("Appending %d bytes at offset %d",
1315 length,info->offset);
1317 /* We copy length+1 bytes to preserve guaranteed null termination */
1318 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1319 memcpy (info->buffer + info->offset, new_buffer, length+1);
1320 info->offset += length;
1321 g_free (new_buffer);
1328 /*************************************************************
1329 * gtk_selection_retrieval_timeout:
1330 * Timeout callback while receiving a selection.
1332 * info: Information about this retrieval
1334 *************************************************************/
1337 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1342 GDK_THREADS_ENTER ();
1344 /* Determine if retrieval has finished by checking if it still in
1345 list of pending retrievals */
1347 tmp_list = current_retrievals;
1350 if (info == (GtkRetrievalInfo *)tmp_list->data)
1352 tmp_list = tmp_list->next;
1355 /* If retrieval is finished */
1356 if (!tmp_list || info->idle_time >= 5)
1358 if (tmp_list && info->idle_time >= 5)
1360 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1361 g_list_free (tmp_list);
1362 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1365 g_free (info->buffer);
1368 retval = FALSE; /* remove timeout */
1374 retval = TRUE; /* timeout will happen again */
1377 GDK_THREADS_LEAVE ();
1382 /*************************************************************
1383 * gtk_selection_retrieval_report:
1384 * Emits a "selection_received" signal.
1386 * info: information about the retrieval that completed
1387 * buffer: buffer containing data (NULL => errror)
1388 * time: timestamp for data in buffer
1390 *************************************************************/
1393 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1394 GdkAtom type, gint format,
1395 guchar *buffer, gint length,
1398 GtkSelectionData data;
1400 data.selection = info->selection;
1401 data.target = info->target;
1403 data.format = format;
1405 data.length = length;
1408 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1409 "selection_received",
1413 /*************************************************************
1414 * gtk_selection_invoke_handler:
1415 * Finds and invokes handler for specified
1416 * widget/selection/target combination, calls
1417 * gtk_selection_default_handler if none exists.
1420 * widget: selection owner
1421 * data: selection data [INOUT]
1422 * time: time from requeset
1425 * Number of bytes written to buffer, -1 if error
1426 *************************************************************/
1429 gtk_selection_invoke_handler (GtkWidget *widget,
1430 GtkSelectionData *data,
1433 GtkTargetList *target_list;
1437 g_return_if_fail (widget != NULL);
1439 target_list = gtk_selection_target_list_get (widget, data->selection);
1441 gtk_target_list_find (target_list, data->target, &info))
1443 gtk_signal_emit_by_name (GTK_OBJECT (widget),
1449 gtk_selection_default_handler (widget, data);
1452 /*************************************************************
1453 * gtk_selection_default_handler:
1454 * Handles some default targets that exist for any widget
1455 * If it can't fit results into buffer, returns -1. This
1456 * won't happen in any conceivable case, since it would
1457 * require 1000 selection targets!
1460 * widget: selection owner
1461 * data: selection data [INOUT]
1463 *************************************************************/
1466 gtk_selection_default_handler (GtkWidget *widget,
1467 GtkSelectionData *data)
1469 if (data->target == gtk_selection_atoms[TIMESTAMP])
1471 /* Time which was used to obtain selection */
1473 GtkSelectionInfo *selection_info;
1475 tmp_list = current_selections;
1478 selection_info = (GtkSelectionInfo *)tmp_list->data;
1479 if ((selection_info->widget == widget) &&
1480 (selection_info->selection == data->selection))
1482 gtk_selection_data_set (data,
1483 GDK_SELECTION_TYPE_INTEGER,
1485 (guchar *)&selection_info->time,
1490 tmp_list = tmp_list->next;
1495 else if (data->target == gtk_selection_atoms[TARGETS])
1497 /* List of all targets supported for this widget/selection pair */
1501 GtkTargetList *target_list;
1502 GtkTargetPair *pair;
1504 target_list = gtk_selection_target_list_get (widget,
1506 count = g_list_length (target_list->list) + 3;
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);