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 Lesser 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 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser 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-2000. 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;
326 g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
331 window = widget->window;
333 tmp_list = current_selections;
336 if (((GtkSelectionInfo *)tmp_list->data)->selection == selection)
338 selection_info = tmp_list->data;
342 tmp_list = tmp_list->next;
345 if (gdk_selection_owner_set (window, selection, time, TRUE))
353 old_owner = selection_info->widget;
354 current_selections = g_list_remove_link (current_selections,
356 g_list_free (tmp_list);
357 g_free (selection_info);
362 if (selection_info == NULL)
364 selection_info = g_new (GtkSelectionInfo, 1);
365 selection_info->selection = selection;
366 selection_info->widget = widget;
367 selection_info->time = time;
368 current_selections = g_list_prepend (current_selections,
373 old_owner = selection_info->widget;
374 selection_info->widget = widget;
375 selection_info->time = time;
378 /* If another widget in the application lost the selection,
379 * send it a GDK_SELECTION_CLEAR event.
381 if (old_owner && old_owner != widget)
383 GdkEventSelection event;
385 event.type = GDK_SELECTION_CLEAR;
386 event.window = old_owner->window;
387 event.selection = selection;
390 gtk_widget_event (old_owner, (GdkEvent *) &event);
398 /*************************************************************
399 * gtk_selection_add_target
400 * Add specified target to list of supported targets
403 * widget: The widget for which this target applies
406 * info: guint to pass to to the selection_get signal
409 *************************************************************/
411 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
413 struct _GtkSelectionTargetList {
418 static GtkTargetList *
419 gtk_selection_target_list_get (GtkWidget *widget,
422 GtkSelectionTargetList *sellist;
426 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
431 sellist = tmp_list->data;
432 if (sellist->selection == selection)
433 return sellist->list;
434 tmp_list = tmp_list->next;
437 sellist = g_new (GtkSelectionTargetList, 1);
438 sellist->selection = selection;
439 sellist->list = gtk_target_list_new (NULL, 0);
441 lists = g_list_prepend (lists, sellist);
442 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
444 return sellist->list;
448 gtk_selection_target_list_remove (GtkWidget *widget)
450 GtkSelectionTargetList *sellist;
454 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
459 sellist = tmp_list->data;
461 gtk_target_list_unref (sellist->list);
464 tmp_list = tmp_list->next;
468 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL);
472 * gtk_selection_clear_targets:
473 * @widget: a #GtkWidget
474 * @selection: an atom representing a selection
476 * Remove all targets registered for the given selection for the
480 gtk_selection_clear_targets (GtkWidget *widget,
483 GtkSelectionTargetList *sellist;
487 lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
492 sellist = tmp_list->data;
493 if (sellist->selection == selection)
495 lists = g_list_delete_link (lists, tmp_list);
496 gtk_target_list_unref (sellist->list);
502 tmp_list = tmp_list->next;
505 gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
509 gtk_selection_add_target (GtkWidget *widget,
516 g_return_if_fail (widget != NULL);
518 list = gtk_selection_target_list_get (widget, selection);
519 gtk_target_list_add (list, target, 0, info);
523 gtk_selection_add_targets (GtkWidget *widget,
525 const GtkTargetEntry *targets,
530 g_return_if_fail (widget != NULL);
531 g_return_if_fail (targets != NULL);
533 list = gtk_selection_target_list_get (widget, selection);
534 gtk_target_list_add_table (list, targets, ntargets);
538 /*************************************************************
539 * gtk_selection_remove_all:
540 * Removes all handlers and unsets ownership of all
541 * selections for a widget. Called when widget is being
547 *************************************************************/
550 gtk_selection_remove_all (GtkWidget *widget)
554 GtkSelectionInfo *selection_info;
556 /* Remove pending requests/incrs for this widget */
558 tmp_list = current_incrs;
561 next = tmp_list->next;
562 if (((GtkIncrInfo *)tmp_list->data)->widget == widget)
564 current_incrs = g_list_remove_link (current_incrs, tmp_list);
565 /* structure will be freed in timeout */
566 g_list_free (tmp_list);
571 tmp_list = current_retrievals;
574 next = tmp_list->next;
575 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
577 current_retrievals = g_list_remove_link (current_retrievals,
579 /* structure will be freed in timeout */
580 g_list_free (tmp_list);
585 /* Disclaim ownership of any selections */
587 tmp_list = current_selections;
590 next = tmp_list->next;
591 selection_info = (GtkSelectionInfo *)tmp_list->data;
593 if (selection_info->widget == widget)
595 gdk_selection_owner_set (NULL,
596 selection_info->selection,
597 GDK_CURRENT_TIME, FALSE);
598 current_selections = g_list_remove_link (current_selections,
600 g_list_free (tmp_list);
601 g_free (selection_info);
607 /* Remove all selection lists */
608 gtk_selection_target_list_remove (widget);
611 /*************************************************************
612 * gtk_selection_convert:
613 * Request the contents of a selection. When received,
614 * a "selection_received" signal will be generated.
617 * widget: The widget which acts as requestor
618 * selection: Which selection to get
619 * target: Form of information desired (e.g., STRING)
620 * time: Time of request (usually of triggering event)
621 * In emergency, you could use GDK_CURRENT_TIME
624 * TRUE if requested succeeded. FALSE if we could not process
625 * request. (e.g., there was already a request in process for
627 *************************************************************/
630 gtk_selection_convert (GtkWidget *widget,
635 GtkRetrievalInfo *info;
637 GdkWindow *owner_window;
639 g_return_val_if_fail (widget != NULL, FALSE);
642 gtk_selection_init ();
644 if (!GTK_WIDGET_REALIZED (widget))
645 gtk_widget_realize (widget);
647 /* Check to see if there are already any retrievals in progress for
648 this widget. If we changed GDK to use the selection for the
649 window property in which to store the retrieved information, then
650 we could support multiple retrievals for different selections.
651 This might be useful for DND. */
653 tmp_list = current_retrievals;
656 info = (GtkRetrievalInfo *)tmp_list->data;
657 if (info->widget == widget)
659 tmp_list = tmp_list->next;
662 info = g_new (GtkRetrievalInfo, 1);
664 info->widget = widget;
665 info->selection = selection;
666 info->target = target;
670 /* Check if this process has current owner. If so, call handler
671 procedure directly to avoid deadlocks with INCR. */
673 owner_window = gdk_selection_owner_get (selection);
675 if (owner_window != NULL)
677 GtkWidget *owner_widget;
678 GtkSelectionData selection_data;
680 selection_data.selection = selection;
681 selection_data.target = target;
682 selection_data.data = NULL;
683 selection_data.length = -1;
685 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
687 if (owner_widget != NULL)
689 gtk_selection_invoke_handler (owner_widget,
693 gtk_selection_retrieval_report (info,
695 selection_data.format,
697 selection_data.length,
700 g_free (selection_data.data);
707 /* Otherwise, we need to go through X */
709 current_retrievals = g_list_append (current_retrievals, info);
710 gdk_selection_convert (widget->window, selection, target, time);
711 gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
716 /*************************************************************
717 * gtk_selection_data_set:
718 * Store new data into a GtkSelectionData object. Should
719 * _only_ by called from a selection handler callback.
720 * Null terminates the stored data.
722 * type: the type of selection data
723 * format: format (number of bits in a unit)
724 * data: pointer to the data (will be copied)
725 * length: length of the data
727 *************************************************************/
730 gtk_selection_data_set (GtkSelectionData *selection_data,
736 if (selection_data->data)
737 g_free (selection_data->data);
739 selection_data->type = type;
740 selection_data->format = format;
744 selection_data->data = g_new (guchar, length+1);
745 memcpy (selection_data->data, data, length);
746 selection_data->data[length] = 0;
750 g_return_if_fail (length <= 0);
753 selection_data->data = NULL;
755 selection_data->data = g_strdup("");
758 selection_data->length = length;
761 static GdkAtom utf8_atom;
762 static GdkAtom text_atom;
763 static GdkAtom ctext_atom;
770 utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
771 text_atom = gdk_atom_intern ("TEXT", FALSE);
772 ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
777 * gtk_selection_data_set_text:
778 * @selection_data: a #GtkSelectionData
779 * @str: a UTF-8 string
781 * Sets the contents of the selection from a UTF-8 encoded string.
782 * The string is converted to the form determined by
783 * @selection_data->target.
785 * Return value: %TRUE if the selection was succesfully set,
789 gtk_selection_data_set_text (GtkSelectionData *selection_data,
794 if (selection_data->target == utf8_atom)
796 gtk_selection_data_set (selection_data,
798 8, (guchar *)str, strlen (str));
801 else if (selection_data->target == GDK_TARGET_STRING)
803 gchar *latin1 = gdk_utf8_to_string_target (str);
807 gtk_selection_data_set (selection_data,
808 GDK_SELECTION_TYPE_STRING,
809 8, latin1, strlen (latin1));
816 else if (selection_data->target == ctext_atom ||
817 selection_data->target == text_atom)
824 if (gdk_utf8_to_compound_text (str, &encoding, &format, &text, &new_length))
826 gtk_selection_data_set (selection_data, encoding, format, text, new_length);
827 gdk_free_compound_text (text);
837 * gtk_selection_data_get_text:
838 * @selection_data: a #GtkSelectionData
840 * Gets the contents of the selection data as a UTF-8 string.
842 * Return value: if the selection data contained a recognized
843 * text type and it could be converted to UTF-8, a newly allocated
844 * string containing the converted text, otherwise %NULL.
845 * If the result is non-%NULL it must be freed with g_free().
848 gtk_selection_data_get_text (GtkSelectionData *selection_data)
850 guchar *result = NULL;
854 if (selection_data->length >= 0 &&
855 (selection_data->type == GDK_TARGET_STRING ||
856 selection_data->type == ctext_atom ||
857 selection_data->type == utf8_atom))
861 gint count = gdk_text_property_to_utf8_list (selection_data->type,
862 selection_data->format,
863 selection_data->data,
864 selection_data->length,
869 for (i = 1; i < count; i++)
877 /*************************************************************
878 * gtk_selection_init:
879 * Initialize local variables
883 *************************************************************/
886 gtk_selection_init (void)
888 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
889 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
890 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
891 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
894 /*************************************************************
895 * gtk_selection_clear:
896 * Handler for "selection_clear_event"
901 *************************************************************/
904 gtk_selection_clear (GtkWidget *widget,
905 GdkEventSelection *event)
907 /* Note that we filter clear events in gdkselection-x11.c, so
908 * that we only will get here if the clear event actually
909 * represents a change that we didn't do ourself.
912 GtkSelectionInfo *selection_info = NULL;
914 tmp_list = current_selections;
917 selection_info = (GtkSelectionInfo *)tmp_list->data;
919 if ((selection_info->selection == event->selection) &&
920 (selection_info->widget == widget))
923 tmp_list = tmp_list->next;
928 current_selections = g_list_remove_link (current_selections, tmp_list);
929 g_list_free (tmp_list);
930 g_free (selection_info);
937 /*************************************************************
938 * gtk_selection_request:
939 * Handler for "selection_request_event"
944 *************************************************************/
947 gtk_selection_request (GtkWidget *widget,
948 GdkEventSelection *event)
956 gtk_selection_init ();
958 /* Check if we own selection */
960 tmp_list = current_selections;
963 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
965 if ((selection_info->selection == event->selection) &&
966 (selection_info->widget == widget))
969 tmp_list = tmp_list->next;
972 if (tmp_list == NULL)
975 info = g_new(GtkIncrInfo, 1);
977 info->widget = widget;
978 info->selection = event->selection;
981 /* Create GdkWindow structure for the requestor */
983 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
984 info->requestor = gdk_window_lookup (event->requestor);
985 if (!info->requestor)
986 info->requestor = gdk_window_foreign_new (event->requestor);
988 info->requestor = NULL;
991 /* Determine conversions we need to perform */
993 if (event->target == gtk_selection_atoms[MULTIPLE])
1001 gdk_error_trap_push();
1002 if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
1003 0, GTK_SELECTION_MAX_SIZE, FALSE,
1004 &type, &format, &length, &mult_atoms))
1006 gdk_selection_send_notify (event->requestor, event->selection,
1007 event->target, GDK_NONE, event->time);
1008 g_free (mult_atoms);
1012 gdk_error_trap_pop();
1014 info->num_conversions = length / (2*sizeof (GdkAtom));
1015 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1017 for (i=0; i<info->num_conversions; i++)
1019 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1020 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1023 else /* only a single conversion */
1025 info->conversions = g_new (GtkIncrConversion, 1);
1026 info->num_conversions = 1;
1027 info->conversions[0].target = event->target;
1028 info->conversions[0].property = event->property;
1029 mult_atoms = (guchar *)info->conversions;
1032 /* Loop through conversions and determine which of these are big
1033 enough to require doing them via INCR */
1034 for (i=0; i<info->num_conversions; i++)
1036 GtkSelectionData data;
1039 data.selection = event->selection;
1040 data.target = info->conversions[i].target;
1044 #ifdef DEBUG_SELECTION
1045 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1046 event->selection, info->conversions[i].target,
1047 gdk_atom_name(info->conversions[i].target),
1048 event->requestor, event->property);
1051 gtk_selection_invoke_handler (widget, &data, event->time);
1053 if (data.length < 0)
1055 ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
1056 info->conversions[i].property = GDK_NONE;
1060 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1062 items = data.length / gtk_selection_bytes_per_item (data.format);
1064 if (data.length > GTK_SELECTION_MAX_SIZE)
1066 /* Sending via INCR */
1068 info->conversions[i].offset = 0;
1069 info->conversions[i].data = data;
1072 gdk_property_change (info->requestor,
1073 info->conversions[i].property,
1074 gtk_selection_atoms[INCR],
1076 GDK_PROP_MODE_REPLACE,
1077 (guchar *)&items, 1);
1081 info->conversions[i].offset = -1;
1083 gdk_property_change (info->requestor,
1084 info->conversions[i].property,
1087 GDK_PROP_MODE_REPLACE,
1094 /* If we have some INCR's, we need to send the rest of the data in
1097 if (info->num_incrs > 0)
1099 /* FIXME: this could be dangerous if window doesn't still
1102 #ifdef DEBUG_SELECTION
1103 g_message ("Starting INCR...");
1106 gdk_window_set_events (info->requestor,
1107 gdk_window_get_events (info->requestor) |
1108 GDK_PROPERTY_CHANGE_MASK);
1109 current_incrs = g_list_append (current_incrs, info);
1110 gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
1113 /* If it was a MULTIPLE request, set the property to indicate which
1114 conversions succeeded */
1115 if (event->target == gtk_selection_atoms[MULTIPLE])
1117 gdk_property_change (info->requestor, event->property,
1118 GDK_SELECTION_TYPE_ATOM, 32,
1119 GDK_PROP_MODE_REPLACE,
1120 mult_atoms, 2*info->num_conversions);
1121 g_free (mult_atoms);
1124 if (info->num_conversions == 1 &&
1125 info->conversions[0].property == GDK_NONE)
1127 /* Reject the entire conversion */
1128 gdk_selection_send_notify (event->requestor, event->selection,
1129 event->target, GDK_NONE, event->time);
1133 gdk_selection_send_notify (event->requestor, event->selection,
1134 event->target, event->property, event->time);
1137 if (info->num_incrs == 0)
1139 g_free (info->conversions);
1146 /*************************************************************
1147 * gtk_selection_incr_event:
1148 * Called whenever an PropertyNotify event occurs for an
1149 * GdkWindow with user_data == NULL. These will be notifications
1150 * that a window we are sending the selection to via the
1151 * INCR protocol has deleted a property and is ready for
1155 * window: the requestor window
1156 * event: the property event structure
1159 *************************************************************/
1162 gtk_selection_incr_event (GdkWindow *window,
1163 GdkEventProperty *event)
1166 GtkIncrInfo *info = NULL;
1172 if (event->state != GDK_PROPERTY_DELETE)
1175 #ifdef DEBUG_SELECTION
1176 g_message ("PropertyDelete, property %ld", event->atom);
1179 /* Now find the appropriate ongoing INCR */
1180 tmp_list = current_incrs;
1183 info = (GtkIncrInfo *)tmp_list->data;
1184 if (info->requestor == event->window)
1187 tmp_list = tmp_list->next;
1190 if (tmp_list == NULL)
1193 /* Find out which target this is for */
1194 for (i=0; i<info->num_conversions; i++)
1196 if (info->conversions[i].property == event->atom &&
1197 info->conversions[i].offset != -1)
1201 info->idle_time = 0;
1203 if (info->conversions[i].offset == -2) /* only the last 0-length
1211 num_bytes = info->conversions[i].data.length -
1212 info->conversions[i].offset;
1213 buffer = info->conversions[i].data.data +
1214 info->conversions[i].offset;
1216 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1218 num_bytes = GTK_SELECTION_MAX_SIZE;
1219 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1222 info->conversions[i].offset = -2;
1224 #ifdef DEBUG_SELECTION
1225 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1226 num_bytes, info->conversions[i].offset,
1227 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1230 bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1231 gdk_property_change (info->requestor, event->atom,
1232 info->conversions[i].data.type,
1233 info->conversions[i].data.format,
1234 GDK_PROP_MODE_REPLACE,
1236 num_bytes / bytes_per_item);
1238 if (info->conversions[i].offset == -2)
1240 g_free (info->conversions[i].data.data);
1241 info->conversions[i].data.data = NULL;
1247 info->conversions[i].offset = -1;
1253 /* Check if we're finished with all the targets */
1255 if (info->num_incrs == 0)
1257 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1258 g_list_free (tmp_list);
1259 /* Let the timeout free it */
1265 /*************************************************************
1266 * gtk_selection_incr_timeout:
1267 * Timeout callback for the sending portion of the INCR
1270 * info: Information about this incr
1272 *************************************************************/
1275 gtk_selection_incr_timeout (GtkIncrInfo *info)
1280 GDK_THREADS_ENTER ();
1282 /* Determine if retrieval has finished by checking if it still in
1283 list of pending retrievals */
1285 tmp_list = current_incrs;
1288 if (info == (GtkIncrInfo *)tmp_list->data)
1290 tmp_list = tmp_list->next;
1293 /* If retrieval is finished */
1294 if (!tmp_list || info->idle_time >= 5)
1296 if (tmp_list && info->idle_time >= 5)
1298 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1299 g_list_free (tmp_list);
1302 g_free (info->conversions);
1303 /* FIXME: we should check if requestor window is still in use,
1304 and if not, remove it? */
1308 retval = FALSE; /* remove timeout */
1314 retval = TRUE; /* timeout will happen again */
1317 GDK_THREADS_LEAVE ();
1322 /*************************************************************
1323 * gtk_selection_notify:
1324 * Handler for "selection_notify_event" signals on windows
1325 * where a retrieval is currently in process. The selection
1326 * owner has responded to our conversion request.
1328 * widget: Widget getting signal
1329 * event: Selection event structure
1330 * info: Information about this retrieval
1332 * was event handled?
1333 *************************************************************/
1336 gtk_selection_notify (GtkWidget *widget,
1337 GdkEventSelection *event)
1340 GtkRetrievalInfo *info = NULL;
1341 guchar *buffer = NULL;
1346 #ifdef DEBUG_SELECTION
1347 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1348 event->selection, event->target, event->property);
1351 tmp_list = current_retrievals;
1354 info = (GtkRetrievalInfo *)tmp_list->data;
1355 if (info->widget == widget && info->selection == event->selection)
1357 tmp_list = tmp_list->next;
1360 if (!tmp_list) /* no retrieval in progress */
1363 if (event->property != GDK_NONE)
1364 length = gdk_selection_property_get (widget->window, &buffer,
1367 if (event->property == GDK_NONE || buffer == NULL)
1369 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1370 g_list_free (tmp_list);
1371 /* structure will be freed in timeout */
1372 gtk_selection_retrieval_report (info,
1373 GDK_NONE, 0, NULL, -1, event->time);
1378 if (type == gtk_selection_atoms[INCR])
1380 /* The remainder of the selection will come through PropertyNotify
1383 info->notify_time = event->time;
1384 info->idle_time = 0;
1385 info->offset = 0; /* Mark as OK to proceed */
1386 gdk_window_set_events (widget->window,
1387 gdk_window_get_events (widget->window)
1388 | GDK_PROPERTY_CHANGE_MASK);
1392 /* We don't delete the info structure - that will happen in timeout */
1393 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1394 g_list_free (tmp_list);
1396 info->offset = length;
1397 gtk_selection_retrieval_report (info,
1399 buffer, length, event->time);
1402 gdk_property_delete (widget->window, event->property);
1409 /*************************************************************
1410 * gtk_selection_property_notify:
1411 * Handler for "property_notify_event" signals on windows
1412 * where a retrieval is currently in process. The selection
1413 * owner has added more data.
1415 * widget: Widget getting signal
1416 * event: Property event structure
1417 * info: Information about this retrieval
1419 * was event handled?
1420 *************************************************************/
1423 gtk_selection_property_notify (GtkWidget *widget,
1424 GdkEventProperty *event)
1427 GtkRetrievalInfo *info = NULL;
1433 g_return_val_if_fail (widget != NULL, FALSE);
1434 g_return_val_if_fail (event != NULL, FALSE);
1436 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1437 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1438 (event->atom != gdk_selection_property)) /* not the right property */
1442 #ifdef DEBUG_SELECTION
1443 g_message ("PropertyNewValue, property %ld",
1447 tmp_list = current_retrievals;
1450 info = (GtkRetrievalInfo *)tmp_list->data;
1451 if (info->widget == widget)
1453 tmp_list = tmp_list->next;
1456 if (!tmp_list) /* No retrieval in progress */
1459 if (info->offset < 0) /* We haven't got the SelectionNotify
1460 for this retrieval yet */
1463 info->idle_time = 0;
1465 length = gdk_selection_property_get (widget->window, &new_buffer,
1467 gdk_property_delete (widget->window, event->atom);
1469 /* We could do a lot better efficiency-wise by paying attention to
1470 what length was sent in the initial INCR transaction, instead of
1471 doing memory allocation at every step. But its only guaranteed to
1472 be a _lower bound_ (pretty useless!) */
1474 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1476 /* Info structure will be freed in timeout */
1477 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1478 g_list_free (tmp_list);
1479 gtk_selection_retrieval_report (info,
1481 (type == GDK_NONE) ? NULL : info->buffer,
1482 (type == GDK_NONE) ? -1 : info->offset,
1485 else /* append on newly arrived data */
1489 #ifdef DEBUG_SELECTION
1490 g_message ("Start - Adding %d bytes at offset 0",
1493 info->buffer = new_buffer;
1494 info->offset = length;
1499 #ifdef DEBUG_SELECTION
1500 g_message ("Appending %d bytes at offset %d",
1501 length,info->offset);
1503 /* We copy length+1 bytes to preserve guaranteed null termination */
1504 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1505 memcpy (info->buffer + info->offset, new_buffer, length+1);
1506 info->offset += length;
1507 g_free (new_buffer);
1514 /*************************************************************
1515 * gtk_selection_retrieval_timeout:
1516 * Timeout callback while receiving a selection.
1518 * info: Information about this retrieval
1520 *************************************************************/
1523 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1528 GDK_THREADS_ENTER ();
1530 /* Determine if retrieval has finished by checking if it still in
1531 list of pending retrievals */
1533 tmp_list = current_retrievals;
1536 if (info == (GtkRetrievalInfo *)tmp_list->data)
1538 tmp_list = tmp_list->next;
1541 /* If retrieval is finished */
1542 if (!tmp_list || info->idle_time >= 5)
1544 if (tmp_list && info->idle_time >= 5)
1546 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1547 g_list_free (tmp_list);
1548 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1551 g_free (info->buffer);
1554 retval = FALSE; /* remove timeout */
1560 retval = TRUE; /* timeout will happen again */
1563 GDK_THREADS_LEAVE ();
1568 /*************************************************************
1569 * gtk_selection_retrieval_report:
1570 * Emits a "selection_received" signal.
1572 * info: information about the retrieval that completed
1573 * buffer: buffer containing data (NULL => errror)
1574 * time: timestamp for data in buffer
1576 *************************************************************/
1579 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1580 GdkAtom type, gint format,
1581 guchar *buffer, gint length,
1584 GtkSelectionData data;
1586 data.selection = info->selection;
1587 data.target = info->target;
1589 data.format = format;
1591 data.length = length;
1594 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1595 "selection_received",
1599 /*************************************************************
1600 * gtk_selection_invoke_handler:
1601 * Finds and invokes handler for specified
1602 * widget/selection/target combination, calls
1603 * gtk_selection_default_handler if none exists.
1606 * widget: selection owner
1607 * data: selection data [INOUT]
1608 * time: time from requeset
1611 * Number of bytes written to buffer, -1 if error
1612 *************************************************************/
1615 gtk_selection_invoke_handler (GtkWidget *widget,
1616 GtkSelectionData *data,
1619 GtkTargetList *target_list;
1623 g_return_if_fail (widget != NULL);
1625 target_list = gtk_selection_target_list_get (widget, data->selection);
1627 gtk_target_list_find (target_list, data->target, &info))
1629 gtk_signal_emit_by_name (GTK_OBJECT (widget),
1635 gtk_selection_default_handler (widget, data);
1638 /*************************************************************
1639 * gtk_selection_default_handler:
1640 * Handles some default targets that exist for any widget
1641 * If it can't fit results into buffer, returns -1. This
1642 * won't happen in any conceivable case, since it would
1643 * require 1000 selection targets!
1646 * widget: selection owner
1647 * data: selection data [INOUT]
1649 *************************************************************/
1652 gtk_selection_default_handler (GtkWidget *widget,
1653 GtkSelectionData *data)
1655 if (data->target == gtk_selection_atoms[TIMESTAMP])
1657 /* Time which was used to obtain selection */
1659 GtkSelectionInfo *selection_info;
1661 tmp_list = current_selections;
1664 selection_info = (GtkSelectionInfo *)tmp_list->data;
1665 if ((selection_info->widget == widget) &&
1666 (selection_info->selection == data->selection))
1668 gulong time = selection_info->time;
1670 gtk_selection_data_set (data,
1671 GDK_SELECTION_TYPE_INTEGER,
1678 tmp_list = tmp_list->next;
1683 else if (data->target == gtk_selection_atoms[TARGETS])
1685 /* List of all targets supported for this widget/selection pair */
1689 GtkTargetList *target_list;
1690 GtkTargetPair *pair;
1692 target_list = gtk_selection_target_list_get (widget,
1694 count = g_list_length (target_list->list) + 3;
1696 data->type = GDK_SELECTION_TYPE_ATOM;
1698 data->length = count * sizeof (GdkAtom);
1700 p = g_new (GdkAtom, count);
1701 data->data = (guchar *)p;
1703 *p++ = gtk_selection_atoms[TIMESTAMP];
1704 *p++ = gtk_selection_atoms[TARGETS];
1705 *p++ = gtk_selection_atoms[MULTIPLE];
1707 tmp_list = target_list->list;
1710 pair = (GtkTargetPair *)tmp_list->data;
1711 *p++ = pair->target;
1713 tmp_list = tmp_list->next;
1724 gtk_selection_data_copy (GtkSelectionData *selection_data)
1726 GtkSelectionData *new_data;
1728 g_return_val_if_fail (selection_data != NULL, NULL);
1730 new_data = g_new (GtkSelectionData, 1);
1731 *new_data = *selection_data;
1733 if (selection_data->data)
1735 new_data->data = g_malloc (selection_data->length + 1);
1736 memcpy (new_data->data, selection_data->data, selection_data->length + 1);
1743 gtk_selection_data_free (GtkSelectionData *data)
1745 g_return_if_fail (data != NULL);
1748 g_free (data->data);
1754 gtk_selection_bytes_per_item (gint format)
1759 return sizeof (char);
1762 return sizeof (short);
1765 return sizeof (long);
1768 g_assert_not_reached();