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 ... */
48 * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
49 * file for a list of people on the GTK+ Team. See the ChangeLog
50 * files for a list of changes. These files are distributed with
51 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
58 #if defined (GDK_WINDOWING_X11)
59 #include "x11/gdkx.h" /* For gdk_window_lookup() */
60 #elif defined (GDK_WINDOWING_WIN32)
61 #include "win32/gdkwin32.h" /* For gdk_window_lookup() */
62 #elif defined (GDK_WINDOWING_NANOX)
63 #include "nanox/gdkprivate-nanox.h" /* For gdk_window_lookup() */
67 #include "gtkselection.h"
68 #include "gtksignal.h"
70 /* #define DEBUG_SELECTION */
72 /* Maximum size of a sent chunk, in bytes. Also the default size of
74 #ifdef GDK_WINDOWING_WIN32
75 /* No chunks on Win32 */
76 #define GTK_SELECTION_MAX_SIZE G_MAXINT
78 #define GTK_SELECTION_MAX_SIZE 4000
88 typedef struct _GtkSelectionInfo GtkSelectionInfo;
89 typedef struct _GtkIncrConversion GtkIncrConversion;
90 typedef struct _GtkIncrInfo GtkIncrInfo;
91 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
93 struct _GtkSelectionInfo
96 GtkWidget *widget; /* widget that owns selection */
97 guint32 time; /* time used to acquire selection */
100 struct _GtkIncrConversion
102 GdkAtom target; /* Requested target */
103 GdkAtom property; /* Property to store in */
104 GtkSelectionData data; /* The data being supplied */
105 gint offset; /* Current offset in sent selection.
107 * -2 => Only the final (empty) portion
113 GtkWidget *widget; /* Selection owner */
114 GdkWindow *requestor; /* Requestor window - we create a GdkWindow
115 so we can receive events */
116 GdkAtom selection; /* Selection we're sending */
118 GtkIncrConversion *conversions; /* Information about requested conversions -
119 * With MULTIPLE requests (benighted 1980's
120 * hardware idea), there can be more than
122 gint num_conversions;
123 gint num_incrs; /* number of remaining INCR style transactions */
128 struct _GtkRetrievalInfo
131 GdkAtom selection; /* Selection being retrieved. */
132 GdkAtom target; /* Form of selection that we requested */
133 guint32 idle_time; /* Number of seconds since we last heard
134 from selection owner */
135 guchar *buffer; /* Buffer in which to accumulate results */
136 gint offset; /* Current offset in buffer, -1 indicates
138 guint32 notify_time; /* Timestamp from SelectionNotify */
141 /* Local Functions */
142 static void gtk_selection_init (void);
143 static gint gtk_selection_incr_timeout (GtkIncrInfo *info);
144 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
145 static void gtk_selection_retrieval_report (GtkRetrievalInfo *info,
151 static void gtk_selection_invoke_handler (GtkWidget *widget,
152 GtkSelectionData *data,
154 static void gtk_selection_default_handler (GtkWidget *widget,
155 GtkSelectionData *data);
156 static int gtk_selection_bytes_per_item (gint format);
159 static gint initialize = TRUE;
160 static GList *current_retrievals = NULL;
161 static GList *current_incrs = NULL;
162 static GList *current_selections = NULL;
164 static GdkAtom gtk_selection_atoms[LAST_ATOM];
165 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
176 gtk_target_list_new (const GtkTargetEntry *targets,
179 GtkTargetList *result = g_new (GtkTargetList, 1);
181 result->ref_count = 1;
184 gtk_target_list_add_table (result, targets, ntargets);
190 gtk_target_list_ref (GtkTargetList *list)
192 g_return_if_fail (list != NULL);
198 gtk_target_list_unref (GtkTargetList *list)
200 g_return_if_fail (list != NULL);
201 g_return_if_fail (list->ref_count > 0);
204 if (list->ref_count == 0)
206 GList *tmp_list = list->list;
209 GtkTargetPair *pair = tmp_list->data;
212 tmp_list = tmp_list->next;
215 g_list_free (list->list);
221 gtk_target_list_add (GtkTargetList *list,
228 g_return_if_fail (list != NULL);
230 pair = g_new (GtkTargetPair, 1);
231 pair->target = target;
235 list->list = g_list_append (list->list, pair);
239 gtk_target_list_add_table (GtkTargetList *list,
240 const GtkTargetEntry *targets,
245 for (i=ntargets-1; i >= 0; i--)
247 GtkTargetPair *pair = g_new (GtkTargetPair, 1);
248 pair->target = gdk_atom_intern (targets[i].target, FALSE);
249 pair->flags = targets[i].flags;
250 pair->info = targets[i].info;
252 list->list = g_list_prepend (list->list, pair);
257 gtk_target_list_remove (GtkTargetList *list,
262 g_return_if_fail (list != NULL);
264 tmp_list = list->list;
267 GtkTargetPair *pair = tmp_list->data;
269 if (pair->target == target)
273 list->list = g_list_remove_link (list->list, tmp_list);
274 g_list_free_1 (tmp_list);
279 tmp_list = tmp_list->next;
284 gtk_target_list_find (GtkTargetList *list,
288 GList *tmp_list = list->list;
291 GtkTargetPair *pair = tmp_list->data;
293 if (pair->target == target)
298 tmp_list = tmp_list->next;
305 /*************************************************************
306 * gtk_selection_owner_set:
307 * Claim ownership of a selection.
309 * widget: new selection owner
310 * selection: which selection
311 * time: time (use GDK_CURRENT_TIME only if necessary)
314 *************************************************************/
317 gtk_selection_owner_set (GtkWidget *widget,
322 GtkWidget *old_owner;
323 GtkSelectionInfo *selection_info = NULL;
330 if (!GTK_WIDGET_REALIZED (widget))
331 gtk_widget_realize (widget);
333 window = widget->window;
336 tmp_list = current_selections;
339 selection_info = (GtkSelectionInfo *)tmp_list->data;
341 if (selection_info->selection == selection)
344 tmp_list = tmp_list->next;
347 if (tmp_list == NULL)
348 selection_info = NULL;
350 if (selection_info->widget == widget)
353 if (gdk_selection_owner_set (window, selection, time, TRUE))
361 old_owner = selection_info->widget;
362 current_selections = g_list_remove_link (current_selections,
364 g_list_free (tmp_list);
365 g_free (selection_info);
370 if (selection_info == NULL)
372 selection_info = g_new (GtkSelectionInfo, 1);
373 selection_info->selection = selection;
374 selection_info->widget = widget;
375 selection_info->time = time;
376 current_selections = g_list_append (current_selections,
381 old_owner = selection_info->widget;
382 selection_info->widget = widget;
383 selection_info->time = time;
386 /* If another widget in the application lost the selection,
387 * send it a GDK_SELECTION_CLEAR event, unless we're setting
388 * the owner to None, in which case an event will be sent */
389 if (old_owner && (widget != NULL))
391 GdkEventSelection event;
393 event.type = GDK_SELECTION_CLEAR;
394 event.window = old_owner->window;
395 event.selection = selection;
398 gtk_widget_event (old_owner, (GdkEvent *) &event);
406 /*************************************************************
407 * gtk_selection_add_target
408 * Add specified target to list of supported targets
411 * widget: The widget for which this target applies
414 * info: guint to pass to to the selection_get signal
417 *************************************************************/
419 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
421 struct _GtkSelectionTargetList {
426 static GtkTargetList *
427 gtk_selection_target_list_get (GtkWidget *widget,
430 GtkSelectionTargetList *sellist;
434 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
439 sellist = tmp_list->data;
440 if (sellist->selection == selection)
441 return sellist->list;
442 tmp_list = tmp_list->next;
445 sellist = g_new (GtkSelectionTargetList, 1);
446 sellist->selection = selection;
447 sellist->list = gtk_target_list_new (NULL, 0);
449 lists = g_list_prepend (lists, sellist);
450 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
452 return sellist->list;
456 gtk_selection_target_list_remove (GtkWidget *widget)
458 GtkSelectionTargetList *sellist;
462 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
467 sellist = tmp_list->data;
469 gtk_target_list_unref (sellist->list);
472 tmp_list = tmp_list->next;
476 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL);
480 gtk_selection_add_target (GtkWidget *widget,
487 g_return_if_fail (widget != NULL);
489 list = gtk_selection_target_list_get (widget, selection);
490 gtk_target_list_add (list, target, 0, info);
494 gtk_selection_add_targets (GtkWidget *widget,
496 const GtkTargetEntry *targets,
501 g_return_if_fail (widget != NULL);
502 g_return_if_fail (targets != NULL);
504 list = gtk_selection_target_list_get (widget, selection);
505 gtk_target_list_add_table (list, targets, ntargets);
508 /*************************************************************
509 * gtk_selection_remove_all:
510 * Removes all handlers and unsets ownership of all
511 * selections for a widget. Called when widget is being
517 *************************************************************/
520 gtk_selection_remove_all (GtkWidget *widget)
524 GtkSelectionInfo *selection_info;
526 /* Remove pending requests/incrs for this widget */
528 tmp_list = current_incrs;
531 next = tmp_list->next;
532 if (((GtkIncrInfo *)tmp_list->data)->widget == widget)
534 current_incrs = g_list_remove_link (current_incrs, tmp_list);
535 /* structure will be freed in timeout */
536 g_list_free (tmp_list);
541 tmp_list = current_retrievals;
544 next = tmp_list->next;
545 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
547 current_retrievals = g_list_remove_link (current_retrievals,
549 /* structure will be freed in timeout */
550 g_list_free (tmp_list);
555 /* Disclaim ownership of any selections */
557 tmp_list = current_selections;
560 next = tmp_list->next;
561 selection_info = (GtkSelectionInfo *)tmp_list->data;
563 if (selection_info->widget == widget)
565 gdk_selection_owner_set (NULL,
566 selection_info->selection,
567 GDK_CURRENT_TIME, FALSE);
568 current_selections = g_list_remove_link (current_selections,
570 g_list_free (tmp_list);
571 g_free (selection_info);
577 /* Remove all selection lists */
578 gtk_selection_target_list_remove (widget);
581 /*************************************************************
582 * gtk_selection_convert:
583 * Request the contents of a selection. When received,
584 * a "selection_received" signal will be generated.
587 * widget: The widget which acts as requestor
588 * selection: Which selection to get
589 * target: Form of information desired (e.g., STRING)
590 * time: Time of request (usually of triggering event)
591 * In emergency, you could use GDK_CURRENT_TIME
594 * TRUE if requested succeeded. FALSE if we could not process
595 * request. (e.g., there was already a request in process for
597 *************************************************************/
600 gtk_selection_convert (GtkWidget *widget,
605 GtkRetrievalInfo *info;
607 GdkWindow *owner_window;
609 g_return_val_if_fail (widget != NULL, FALSE);
612 gtk_selection_init ();
614 if (!GTK_WIDGET_REALIZED (widget))
615 gtk_widget_realize (widget);
617 /* Check to see if there are already any retrievals in progress for
618 this widget. If we changed GDK to use the selection for the
619 window property in which to store the retrieved information, then
620 we could support multiple retrievals for different selections.
621 This might be useful for DND. */
623 tmp_list = current_retrievals;
626 info = (GtkRetrievalInfo *)tmp_list->data;
627 if (info->widget == widget)
629 tmp_list = tmp_list->next;
632 info = g_new (GtkRetrievalInfo, 1);
634 info->widget = widget;
635 info->selection = selection;
636 info->target = target;
640 /* Check if this process has current owner. If so, call handler
641 procedure directly to avoid deadlocks with INCR. */
643 owner_window = gdk_selection_owner_get (selection);
645 if (owner_window != NULL)
647 GtkWidget *owner_widget;
648 GtkSelectionData selection_data;
650 selection_data.selection = selection;
651 selection_data.target = target;
652 selection_data.data = NULL;
653 selection_data.length = -1;
655 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
657 if (owner_widget != NULL)
659 gtk_selection_invoke_handler (owner_widget,
663 gtk_selection_retrieval_report (info,
665 selection_data.format,
667 selection_data.length,
670 g_free (selection_data.data);
677 /* Otherwise, we need to go through X */
679 current_retrievals = g_list_append (current_retrievals, info);
680 gdk_selection_convert (widget->window, selection, target, time);
681 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
686 /*************************************************************
687 * gtk_selection_data_set:
688 * Store new data into a GtkSelectionData object. Should
689 * _only_ by called from a selection handler callback.
690 * Null terminates the stored data.
692 * type: the type of selection data
693 * format: format (number of bits in a unit)
694 * data: pointer to the data (will be copied)
695 * length: length of the data
697 *************************************************************/
700 gtk_selection_data_set (GtkSelectionData *selection_data,
706 if (selection_data->data)
707 g_free (selection_data->data);
709 selection_data->type = type;
710 selection_data->format = format;
714 selection_data->data = g_new (guchar, length+1);
715 memcpy (selection_data->data, data, length);
716 selection_data->data[length] = 0;
720 g_return_if_fail (length <= 0);
723 selection_data->data = NULL;
725 selection_data->data = g_strdup("");
728 selection_data->length = length;
731 /*************************************************************
732 * gtk_selection_init:
733 * Initialize local variables
737 *************************************************************/
740 gtk_selection_init (void)
742 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
743 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
744 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
745 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
748 /*************************************************************
749 * gtk_selection_clear:
750 * Handler for "selection_clear_event"
755 *************************************************************/
758 gtk_selection_clear (GtkWidget *widget,
759 GdkEventSelection *event)
761 /* FIXME: there can be a problem if we change the selection
762 via gtk_selection_owner_set after another client claims
763 the selection, but before we get the notification event.
764 Tk filters based on serial #'s, which aren't retained by
765 GTK. Filtering based on time's will be inherently
766 somewhat unreliable. */
769 GtkSelectionInfo *selection_info = NULL;
771 tmp_list = current_selections;
774 selection_info = (GtkSelectionInfo *)tmp_list->data;
776 if ((selection_info->selection == event->selection) &&
777 (selection_info->widget == widget))
780 tmp_list = tmp_list->next;
785 if (selection_info->time > event->time)
786 return FALSE; /* return FALSE to indicate that
787 * the selection was out of date,
788 * and this clear should be ignored */
791 current_selections = g_list_remove_link (current_selections, tmp_list);
792 g_list_free (tmp_list);
793 g_free (selection_info);
801 /*************************************************************
802 * gtk_selection_request:
803 * Handler for "selection_request_event"
808 *************************************************************/
811 gtk_selection_request (GtkWidget *widget,
812 GdkEventSelection *event)
820 gtk_selection_init ();
822 /* Check if we own selection */
824 tmp_list = current_selections;
827 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
829 if ((selection_info->selection == event->selection) &&
830 (selection_info->widget == widget))
833 tmp_list = tmp_list->next;
836 if (tmp_list == NULL)
839 info = g_new(GtkIncrInfo, 1);
841 info->widget = widget;
842 info->selection = event->selection;
845 /* Create GdkWindow structure for the requestor */
847 info->requestor = gdk_window_lookup (event->requestor);
848 if (!info->requestor)
849 info->requestor = gdk_window_foreign_new (event->requestor);
851 /* Determine conversions we need to perform */
853 if (event->target == gtk_selection_atoms[MULTIPLE])
861 gdk_error_trap_push();
862 if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
863 0, GTK_SELECTION_MAX_SIZE, FALSE,
864 &type, &format, &length, &mult_atoms))
866 gdk_selection_send_notify (event->requestor, event->selection,
867 event->target, GDK_NONE, event->time);
872 gdk_error_trap_pop();
874 info->num_conversions = length / (2*sizeof (GdkAtom));
875 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
877 for (i=0; i<info->num_conversions; i++)
879 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
880 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
883 else /* only a single conversion */
885 info->conversions = g_new (GtkIncrConversion, 1);
886 info->num_conversions = 1;
887 info->conversions[0].target = event->target;
888 info->conversions[0].property = event->property;
889 mult_atoms = (guchar *)info->conversions;
892 /* Loop through conversions and determine which of these are big
893 enough to require doing them via INCR */
894 for (i=0; i<info->num_conversions; i++)
896 GtkSelectionData data;
899 data.selection = event->selection;
900 data.target = info->conversions[i].target;
904 #ifdef DEBUG_SELECTION
905 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
906 event->selection, info->conversions[i].target,
907 gdk_atom_name(info->conversions[i].target),
908 event->requestor, event->property);
911 gtk_selection_invoke_handler (widget, &data, event->time);
915 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
916 info->conversions[i].property = GDK_NONE;
920 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
922 items = data.length / gtk_selection_bytes_per_item (data.format);
924 if (data.length > GTK_SELECTION_MAX_SIZE)
926 /* Sending via INCR */
928 info->conversions[i].offset = 0;
929 info->conversions[i].data = data;
932 gdk_property_change (info->requestor,
933 info->conversions[i].property,
934 gtk_selection_atoms[INCR],
936 GDK_PROP_MODE_REPLACE,
937 (guchar *)&items, 1);
941 info->conversions[i].offset = -1;
943 gdk_property_change (info->requestor,
944 info->conversions[i].property,
947 GDK_PROP_MODE_REPLACE,
954 /* If we have some INCR's, we need to send the rest of the data in
957 if (info->num_incrs > 0)
959 /* FIXME: this could be dangerous if window doesn't still
962 #ifdef DEBUG_SELECTION
963 g_message ("Starting INCR...");
966 gdk_window_set_events (info->requestor,
967 gdk_window_get_events (info->requestor) |
968 GDK_PROPERTY_CHANGE_MASK);
969 current_incrs = g_list_append (current_incrs, info);
970 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
973 /* If it was a MULTIPLE request, set the property to indicate which
974 conversions succeeded */
975 if (event->target == gtk_selection_atoms[MULTIPLE])
977 gdk_property_change (info->requestor, event->property,
978 GDK_SELECTION_TYPE_ATOM, 32,
979 GDK_PROP_MODE_REPLACE,
980 mult_atoms, 2*info->num_conversions);
984 if (info->num_conversions == 1 &&
985 info->conversions[0].property == GDK_NONE)
987 /* Reject the entire conversion */
988 gdk_selection_send_notify (event->requestor, event->selection,
989 event->target, GDK_NONE, event->time);
993 gdk_selection_send_notify (event->requestor, event->selection,
994 event->target, event->property, event->time);
997 if (info->num_incrs == 0)
999 g_free (info->conversions);
1006 /*************************************************************
1007 * gtk_selection_incr_event:
1008 * Called whenever an PropertyNotify event occurs for an
1009 * GdkWindow with user_data == NULL. These will be notifications
1010 * that a window we are sending the selection to via the
1011 * INCR protocol has deleted a property and is ready for
1015 * window: the requestor window
1016 * event: the property event structure
1019 *************************************************************/
1022 gtk_selection_incr_event (GdkWindow *window,
1023 GdkEventProperty *event)
1026 GtkIncrInfo *info = NULL;
1032 if (event->state != GDK_PROPERTY_DELETE)
1035 #ifdef DEBUG_SELECTION
1036 g_message ("PropertyDelete, property %ld", event->atom);
1039 /* Now find the appropriate ongoing INCR */
1040 tmp_list = current_incrs;
1043 info = (GtkIncrInfo *)tmp_list->data;
1044 if (info->requestor == event->window)
1047 tmp_list = tmp_list->next;
1050 if (tmp_list == NULL)
1053 /* Find out which target this is for */
1054 for (i=0; i<info->num_conversions; i++)
1056 if (info->conversions[i].property == event->atom &&
1057 info->conversions[i].offset != -1)
1061 info->idle_time = 0;
1063 if (info->conversions[i].offset == -2) /* only the last 0-length
1071 num_bytes = info->conversions[i].data.length -
1072 info->conversions[i].offset;
1073 buffer = info->conversions[i].data.data +
1074 info->conversions[i].offset;
1076 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1078 num_bytes = GTK_SELECTION_MAX_SIZE;
1079 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1082 info->conversions[i].offset = -2;
1084 #ifdef DEBUG_SELECTION
1085 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1086 num_bytes, info->conversions[i].offset,
1087 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1090 bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1091 gdk_property_change (info->requestor, event->atom,
1092 info->conversions[i].data.type,
1093 info->conversions[i].data.format,
1094 GDK_PROP_MODE_REPLACE,
1096 num_bytes / bytes_per_item);
1098 if (info->conversions[i].offset == -2)
1100 g_free (info->conversions[i].data.data);
1101 info->conversions[i].data.data = NULL;
1107 info->conversions[i].offset = -1;
1113 /* Check if we're finished with all the targets */
1115 if (info->num_incrs == 0)
1117 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1118 g_list_free (tmp_list);
1119 /* Let the timeout free it */
1125 /*************************************************************
1126 * gtk_selection_incr_timeout:
1127 * Timeout callback for the sending portion of the INCR
1130 * info: Information about this incr
1132 *************************************************************/
1135 gtk_selection_incr_timeout (GtkIncrInfo *info)
1140 GDK_THREADS_ENTER ();
1142 /* Determine if retrieval has finished by checking if it still in
1143 list of pending retrievals */
1145 tmp_list = current_incrs;
1148 if (info == (GtkIncrInfo *)tmp_list->data)
1150 tmp_list = tmp_list->next;
1153 /* If retrieval is finished */
1154 if (!tmp_list || info->idle_time >= 5)
1156 if (tmp_list && info->idle_time >= 5)
1158 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1159 g_list_free (tmp_list);
1162 g_free (info->conversions);
1163 /* FIXME: we should check if requestor window is still in use,
1164 and if not, remove it? */
1168 retval = FALSE; /* remove timeout */
1174 retval = TRUE; /* timeout will happen again */
1177 GDK_THREADS_LEAVE ();
1182 /*************************************************************
1183 * gtk_selection_notify:
1184 * Handler for "selection_notify_event" signals on windows
1185 * where a retrieval is currently in process. The selection
1186 * owner has responded to our conversion request.
1188 * widget: Widget getting signal
1189 * event: Selection event structure
1190 * info: Information about this retrieval
1192 * was event handled?
1193 *************************************************************/
1196 gtk_selection_notify (GtkWidget *widget,
1197 GdkEventSelection *event)
1200 GtkRetrievalInfo *info = NULL;
1201 guchar *buffer = NULL;
1206 #ifdef DEBUG_SELECTION
1207 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1208 event->selection, event->target, event->property);
1211 tmp_list = current_retrievals;
1214 info = (GtkRetrievalInfo *)tmp_list->data;
1215 if (info->widget == widget && info->selection == event->selection)
1217 tmp_list = tmp_list->next;
1220 if (!tmp_list) /* no retrieval in progress */
1223 if (event->property != GDK_NONE)
1224 length = gdk_selection_property_get (widget->window, &buffer,
1227 if (event->property == GDK_NONE || buffer == NULL)
1229 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1230 g_list_free (tmp_list);
1231 /* structure will be freed in timeout */
1232 gtk_selection_retrieval_report (info,
1233 GDK_NONE, 0, NULL, -1, event->time);
1238 if (type == gtk_selection_atoms[INCR])
1240 /* The remainder of the selection will come through PropertyNotify
1243 info->notify_time = event->time;
1244 info->idle_time = 0;
1245 info->offset = 0; /* Mark as OK to proceed */
1246 gdk_window_set_events (widget->window,
1247 gdk_window_get_events (widget->window)
1248 | GDK_PROPERTY_CHANGE_MASK);
1252 /* We don't delete the info structure - that will happen in timeout */
1253 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1254 g_list_free (tmp_list);
1256 info->offset = length;
1257 gtk_selection_retrieval_report (info,
1259 buffer, length, event->time);
1262 gdk_property_delete (widget->window, event->property);
1269 /*************************************************************
1270 * gtk_selection_property_notify:
1271 * Handler for "property_notify_event" signals on windows
1272 * where a retrieval is currently in process. The selection
1273 * owner has added more data.
1275 * widget: Widget getting signal
1276 * event: Property event structure
1277 * info: Information about this retrieval
1279 * was event handled?
1280 *************************************************************/
1283 gtk_selection_property_notify (GtkWidget *widget,
1284 GdkEventProperty *event)
1287 GtkRetrievalInfo *info = NULL;
1293 g_return_val_if_fail (widget != NULL, FALSE);
1294 g_return_val_if_fail (event != NULL, FALSE);
1296 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1297 (event->atom != gdk_selection_property)) /* not the right property */
1300 #ifdef DEBUG_SELECTION
1301 g_message ("PropertyNewValue, property %ld",
1305 tmp_list = current_retrievals;
1308 info = (GtkRetrievalInfo *)tmp_list->data;
1309 if (info->widget == widget)
1311 tmp_list = tmp_list->next;
1314 if (!tmp_list) /* No retrieval in progress */
1317 if (info->offset < 0) /* We haven't got the SelectionNotify
1318 for this retrieval yet */
1321 info->idle_time = 0;
1323 length = gdk_selection_property_get (widget->window, &new_buffer,
1325 gdk_property_delete (widget->window, event->atom);
1327 /* We could do a lot better efficiency-wise by paying attention to
1328 what length was sent in the initial INCR transaction, instead of
1329 doing memory allocation at every step. But its only guaranteed to
1330 be a _lower bound_ (pretty useless!) */
1332 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1334 /* Info structure will be freed in timeout */
1335 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1336 g_list_free (tmp_list);
1337 gtk_selection_retrieval_report (info,
1339 (type == GDK_NONE) ? NULL : info->buffer,
1340 (type == GDK_NONE) ? -1 : info->offset,
1343 else /* append on newly arrived data */
1347 #ifdef DEBUG_SELECTION
1348 g_message ("Start - Adding %d bytes at offset 0",
1351 info->buffer = new_buffer;
1352 info->offset = length;
1357 #ifdef DEBUG_SELECTION
1358 g_message ("Appending %d bytes at offset %d",
1359 length,info->offset);
1361 /* We copy length+1 bytes to preserve guaranteed null termination */
1362 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1363 memcpy (info->buffer + info->offset, new_buffer, length+1);
1364 info->offset += length;
1365 g_free (new_buffer);
1372 /*************************************************************
1373 * gtk_selection_retrieval_timeout:
1374 * Timeout callback while receiving a selection.
1376 * info: Information about this retrieval
1378 *************************************************************/
1381 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1386 GDK_THREADS_ENTER ();
1388 /* Determine if retrieval has finished by checking if it still in
1389 list of pending retrievals */
1391 tmp_list = current_retrievals;
1394 if (info == (GtkRetrievalInfo *)tmp_list->data)
1396 tmp_list = tmp_list->next;
1399 /* If retrieval is finished */
1400 if (!tmp_list || info->idle_time >= 5)
1402 if (tmp_list && info->idle_time >= 5)
1404 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1405 g_list_free (tmp_list);
1406 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1409 g_free (info->buffer);
1412 retval = FALSE; /* remove timeout */
1418 retval = TRUE; /* timeout will happen again */
1421 GDK_THREADS_LEAVE ();
1426 /*************************************************************
1427 * gtk_selection_retrieval_report:
1428 * Emits a "selection_received" signal.
1430 * info: information about the retrieval that completed
1431 * buffer: buffer containing data (NULL => errror)
1432 * time: timestamp for data in buffer
1434 *************************************************************/
1437 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1438 GdkAtom type, gint format,
1439 guchar *buffer, gint length,
1442 GtkSelectionData data;
1444 data.selection = info->selection;
1445 data.target = info->target;
1447 data.format = format;
1449 data.length = length;
1452 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1453 "selection_received",
1457 /*************************************************************
1458 * gtk_selection_invoke_handler:
1459 * Finds and invokes handler for specified
1460 * widget/selection/target combination, calls
1461 * gtk_selection_default_handler if none exists.
1464 * widget: selection owner
1465 * data: selection data [INOUT]
1466 * time: time from requeset
1469 * Number of bytes written to buffer, -1 if error
1470 *************************************************************/
1473 gtk_selection_invoke_handler (GtkWidget *widget,
1474 GtkSelectionData *data,
1477 GtkTargetList *target_list;
1481 g_return_if_fail (widget != NULL);
1483 target_list = gtk_selection_target_list_get (widget, data->selection);
1485 gtk_target_list_find (target_list, data->target, &info))
1487 gtk_signal_emit_by_name (GTK_OBJECT (widget),
1493 gtk_selection_default_handler (widget, data);
1496 /*************************************************************
1497 * gtk_selection_default_handler:
1498 * Handles some default targets that exist for any widget
1499 * If it can't fit results into buffer, returns -1. This
1500 * won't happen in any conceivable case, since it would
1501 * require 1000 selection targets!
1504 * widget: selection owner
1505 * data: selection data [INOUT]
1507 *************************************************************/
1510 gtk_selection_default_handler (GtkWidget *widget,
1511 GtkSelectionData *data)
1513 if (data->target == gtk_selection_atoms[TIMESTAMP])
1515 /* Time which was used to obtain selection */
1517 GtkSelectionInfo *selection_info;
1519 tmp_list = current_selections;
1522 selection_info = (GtkSelectionInfo *)tmp_list->data;
1523 if ((selection_info->widget == widget) &&
1524 (selection_info->selection == data->selection))
1526 gulong time = selection_info->time;
1528 gtk_selection_data_set (data,
1529 GDK_SELECTION_TYPE_INTEGER,
1536 tmp_list = tmp_list->next;
1541 else if (data->target == gtk_selection_atoms[TARGETS])
1543 /* List of all targets supported for this widget/selection pair */
1547 GtkTargetList *target_list;
1548 GtkTargetPair *pair;
1550 target_list = gtk_selection_target_list_get (widget,
1552 count = g_list_length (target_list->list) + 3;
1554 data->type = GDK_SELECTION_TYPE_ATOM;
1556 data->length = count * sizeof (GdkAtom);
1558 p = g_new (GdkAtom, count);
1559 data->data = (guchar *)p;
1561 *p++ = gtk_selection_atoms[TIMESTAMP];
1562 *p++ = gtk_selection_atoms[TARGETS];
1563 *p++ = gtk_selection_atoms[MULTIPLE];
1565 tmp_list = target_list->list;
1568 pair = (GtkTargetPair *)tmp_list->data;
1569 *p++ = pair->target;
1571 tmp_list = tmp_list->next;
1582 gtk_selection_data_copy (GtkSelectionData *data)
1584 GtkSelectionData *new_data;
1586 g_return_val_if_fail (data != NULL, NULL);
1588 new_data = g_new (GtkSelectionData, 1);
1595 gtk_selection_data_free (GtkSelectionData *data)
1597 g_return_if_fail (data != NULL);
1603 gtk_selection_bytes_per_item (gint format)
1608 return sizeof (char);
1611 return sizeof (short);
1614 return sizeof (long);
1617 g_assert_not_reached();