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 (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 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 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)
1093 /* Determine if retrieval has finished by checking if it still in
1094 list of pending retrievals */
1096 tmp_list = current_incrs;
1099 if (info == (GtkIncrInfo *)tmp_list->data)
1101 tmp_list = tmp_list->next;
1104 /* If retrieval is finished */
1105 if (!tmp_list || info->idle_time >= 5)
1107 if (tmp_list && info->idle_time >= 5)
1109 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1110 g_list_free (tmp_list);
1113 g_free (info->conversions);
1114 /* FIXME: we should check if requestor window is still in use,
1115 and if not, remove it? */
1119 return FALSE; /* remove timeout */
1125 return TRUE; /* timeout will happen again */
1129 /*************************************************************
1130 * gtk_selection_notify:
1131 * Handler for "selection_notify_event" signals on windows
1132 * where a retrieval is currently in process. The selection
1133 * owner has responded to our conversion request.
1135 * widget: Widget getting signal
1136 * event: Selection event structure
1137 * info: Information about this retrieval
1139 * was event handled?
1140 *************************************************************/
1143 gtk_selection_notify (GtkWidget *widget,
1144 GdkEventSelection *event)
1147 GtkRetrievalInfo *info;
1153 #ifdef DEBUG_SELECTION
1154 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1155 event->selection, event->target, event->property);
1158 tmp_list = current_retrievals;
1161 info = (GtkRetrievalInfo *)tmp_list->data;
1162 if (info->widget == widget && info->selection == event->selection)
1164 tmp_list = tmp_list->next;
1167 if (!tmp_list) /* no retrieval in progress */
1170 if (event->property == GDK_NONE)
1172 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1173 g_list_free (tmp_list);
1174 /* structure will be freed in timeout */
1175 gtk_selection_retrieval_report (info,
1176 GDK_NONE, 0, NULL, -1, event->time);
1181 length = gdk_selection_property_get (widget->window, &buffer,
1184 if (type == gtk_selection_atoms[INCR])
1186 /* The remainder of the selection will come through PropertyNotify
1189 info->notify_time = event->time;
1190 info->idle_time = 0;
1191 info->offset = 0; /* Mark as OK to proceed */
1192 gdk_window_set_events (widget->window,
1193 gdk_window_get_events (widget->window)
1194 | GDK_PROPERTY_CHANGE_MASK);
1198 /* We don't delete the info structure - that will happen in timeout */
1199 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1200 g_list_free (tmp_list);
1202 info->offset = length;
1203 gtk_selection_retrieval_report (info,
1205 buffer, length, event->time);
1208 gdk_property_delete (widget->window, event->property);
1215 /*************************************************************
1216 * gtk_selection_property_notify:
1217 * Handler for "property_notify_event" signals on windows
1218 * where a retrieval is currently in process. The selection
1219 * owner has added more data.
1221 * widget: Widget getting signal
1222 * event: Property event structure
1223 * info: Information about this retrieval
1225 * was event handled?
1226 *************************************************************/
1229 gtk_selection_property_notify (GtkWidget *widget,
1230 GdkEventProperty *event)
1233 GtkRetrievalInfo *info;
1239 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1240 (event->atom != gdk_selection_property)) /* not the right property */
1243 #ifdef DEBUG_SELECTION
1244 g_message ("PropertyNewValue, property %ld",
1248 tmp_list = current_retrievals;
1251 info = (GtkRetrievalInfo *)tmp_list->data;
1252 if (info->widget == widget)
1254 tmp_list = tmp_list->next;
1257 if (!tmp_list) /* No retrieval in progress */
1260 if (info->offset < 0) /* We haven't got the SelectionNotify
1261 for this retrieval yet */
1264 info->idle_time = 0;
1266 length = gdk_selection_property_get (widget->window, &new_buffer,
1268 gdk_property_delete (widget->window, event->atom);
1270 /* We could do a lot better efficiency-wise by paying attention to
1271 what length was sent in the initial INCR transaction, instead of
1272 doing memory allocation at every step. But its only guaranteed to
1273 be a _lower bound_ (pretty useless!) */
1275 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1277 /* Info structure will be freed in timeout */
1278 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1279 g_list_free (tmp_list);
1280 gtk_selection_retrieval_report (info,
1282 (type == GDK_NONE) ? NULL : info->buffer,
1283 (type == GDK_NONE) ? -1 : info->offset,
1286 else /* append on newly arrived data */
1290 #ifdef DEBUG_SELECTION
1291 g_message ("Start - Adding %d bytes at offset 0",
1294 info->buffer = new_buffer;
1295 info->offset = length;
1300 #ifdef DEBUG_SELECTION
1301 g_message ("Appending %d bytes at offset %d",
1302 length,info->offset);
1304 /* We copy length+1 bytes to preserve guaranteed null termination */
1305 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1306 memcpy (info->buffer + info->offset, new_buffer, length+1);
1307 info->offset += length;
1308 g_free (new_buffer);
1315 /*************************************************************
1316 * gtk_selection_retrieval_timeout:
1317 * Timeout callback while receiving a selection.
1319 * info: Information about this retrieval
1321 *************************************************************/
1324 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1328 /* Determine if retrieval has finished by checking if it still in
1329 list of pending retrievals */
1331 tmp_list = current_retrievals;
1334 if (info == (GtkRetrievalInfo *)tmp_list->data)
1336 tmp_list = tmp_list->next;
1339 /* If retrieval is finished */
1340 if (!tmp_list || info->idle_time >= 5)
1342 if (tmp_list && info->idle_time >= 5)
1344 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1345 g_list_free (tmp_list);
1346 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1349 g_free (info->buffer);
1352 return FALSE; /* remove timeout */
1358 return TRUE; /* timeout will happen again */
1363 /*************************************************************
1364 * gtk_selection_retrieval_report:
1365 * Emits a "selection_received" signal.
1367 * info: information about the retrieval that completed
1368 * buffer: buffer containing data (NULL => errror)
1369 * time: timestamp for data in buffer
1371 *************************************************************/
1374 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1375 GdkAtom type, gint format,
1376 guchar *buffer, gint length,
1379 GtkSelectionData data;
1381 data.selection = info->selection;
1382 data.target = info->target;
1384 data.format = format;
1386 data.length = length;
1389 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1390 "selection_received",
1394 /*************************************************************
1395 * gtk_selection_invoke_handler:
1396 * Finds and invokes handler for specified
1397 * widget/selection/target combination, calls
1398 * gtk_selection_default_handler if none exists.
1401 * widget: selection owner
1402 * data: selection data [INOUT]
1403 * time: time from requeset
1406 * Number of bytes written to buffer, -1 if error
1407 *************************************************************/
1410 gtk_selection_invoke_handler (GtkWidget *widget,
1411 GtkSelectionData *data,
1414 GtkTargetList *target_list;
1418 g_return_if_fail (widget != NULL);
1420 target_list = gtk_selection_target_list_get (widget, data->selection);
1422 gtk_target_list_find (target_list, data->target, &info))
1424 gtk_signal_emit_by_name (GTK_OBJECT (widget),
1430 gtk_selection_default_handler (widget, data);
1433 /*************************************************************
1434 * gtk_selection_default_handler:
1435 * Handles some default targets that exist for any widget
1436 * If it can't fit results into buffer, returns -1. This
1437 * won't happen in any conceivable case, since it would
1438 * require 1000 selection targets!
1441 * widget: selection owner
1442 * data: selection data [INOUT]
1444 *************************************************************/
1447 gtk_selection_default_handler (GtkWidget *widget,
1448 GtkSelectionData *data)
1450 if (data->target == gtk_selection_atoms[TIMESTAMP])
1452 /* Time which was used to obtain selection */
1454 GtkSelectionInfo *selection_info;
1456 tmp_list = current_selections;
1459 selection_info = (GtkSelectionInfo *)tmp_list->data;
1460 if ((selection_info->widget == widget) &&
1461 (selection_info->selection == data->selection))
1463 gtk_selection_data_set (data,
1464 GDK_SELECTION_TYPE_INTEGER,
1466 (guchar *)&selection_info->time,
1471 tmp_list = tmp_list->next;
1476 else if (data->target == gtk_selection_atoms[TARGETS])
1478 /* List of all targets supported for this widget/selection pair */
1482 GtkTargetList *target_list;
1483 GtkTargetPair *pair;
1486 target_list = gtk_selection_target_list_get (widget,
1488 tmp_list = target_list->list;
1492 tmp_list = tmp_list->next;
1495 data->type = GDK_SELECTION_TYPE_ATOM;
1496 data->format = 8*sizeof (GdkAtom);
1497 data->length = count*sizeof (GdkAtom);
1499 p = g_new (GdkAtom, count);
1500 data->data = (guchar *)p;
1502 *p++ = gtk_selection_atoms[TIMESTAMP];
1503 *p++ = gtk_selection_atoms[TARGETS];
1504 *p++ = gtk_selection_atoms[MULTIPLE];
1506 tmp_list = target_list->list;
1509 pair = (GtkTargetPair *)tmp_list->data;
1510 *p++ = pair->target;
1512 tmp_list = tmp_list->next;
1523 gtk_selection_data_copy (GtkSelectionData *data)
1525 GtkSelectionData *new_data;
1527 g_return_val_if_fail (data != NULL, NULL);
1529 new_data = g_new (GtkSelectionData, 1);
1536 gtk_selection_data_free (GtkSelectionData *data)
1538 g_return_if_fail (data != NULL);