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_FB)
63 #include "linux-fb/gdkfb.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 successfully 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) || defined(GDK_WINDOWING_FB)
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 length = 0; /* silence gcc */
1369 if (event->property == GDK_NONE || buffer == NULL)
1371 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1372 g_list_free (tmp_list);
1373 /* structure will be freed in timeout */
1374 gtk_selection_retrieval_report (info,
1375 GDK_NONE, 0, NULL, -1, event->time);
1380 if (type == gtk_selection_atoms[INCR])
1382 /* The remainder of the selection will come through PropertyNotify
1385 info->notify_time = event->time;
1386 info->idle_time = 0;
1387 info->offset = 0; /* Mark as OK to proceed */
1388 gdk_window_set_events (widget->window,
1389 gdk_window_get_events (widget->window)
1390 | GDK_PROPERTY_CHANGE_MASK);
1394 /* We don't delete the info structure - that will happen in timeout */
1395 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1396 g_list_free (tmp_list);
1398 info->offset = length;
1399 gtk_selection_retrieval_report (info,
1401 buffer, length, event->time);
1404 gdk_property_delete (widget->window, event->property);
1411 /*************************************************************
1412 * gtk_selection_property_notify:
1413 * Handler for "property_notify_event" signals on windows
1414 * where a retrieval is currently in process. The selection
1415 * owner has added more data.
1417 * widget: Widget getting signal
1418 * event: Property event structure
1419 * info: Information about this retrieval
1421 * was event handled?
1422 *************************************************************/
1425 gtk_selection_property_notify (GtkWidget *widget,
1426 GdkEventProperty *event)
1429 GtkRetrievalInfo *info = NULL;
1435 g_return_val_if_fail (widget != NULL, FALSE);
1436 g_return_val_if_fail (event != NULL, FALSE);
1438 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1439 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1440 (event->atom != gdk_atom_intern ("GDK_SELECTION", FALSE))) /* not the right property */
1444 #ifdef DEBUG_SELECTION
1445 g_message ("PropertyNewValue, property %ld",
1449 tmp_list = current_retrievals;
1452 info = (GtkRetrievalInfo *)tmp_list->data;
1453 if (info->widget == widget)
1455 tmp_list = tmp_list->next;
1458 if (!tmp_list) /* No retrieval in progress */
1461 if (info->offset < 0) /* We haven't got the SelectionNotify
1462 for this retrieval yet */
1465 info->idle_time = 0;
1467 length = gdk_selection_property_get (widget->window, &new_buffer,
1469 gdk_property_delete (widget->window, event->atom);
1471 /* We could do a lot better efficiency-wise by paying attention to
1472 what length was sent in the initial INCR transaction, instead of
1473 doing memory allocation at every step. But its only guaranteed to
1474 be a _lower bound_ (pretty useless!) */
1476 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1478 /* Info structure will be freed in timeout */
1479 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1480 g_list_free (tmp_list);
1481 gtk_selection_retrieval_report (info,
1483 (type == GDK_NONE) ? NULL : info->buffer,
1484 (type == GDK_NONE) ? -1 : info->offset,
1487 else /* append on newly arrived data */
1491 #ifdef DEBUG_SELECTION
1492 g_message ("Start - Adding %d bytes at offset 0",
1495 info->buffer = new_buffer;
1496 info->offset = length;
1501 #ifdef DEBUG_SELECTION
1502 g_message ("Appending %d bytes at offset %d",
1503 length,info->offset);
1505 /* We copy length+1 bytes to preserve guaranteed null termination */
1506 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1507 memcpy (info->buffer + info->offset, new_buffer, length+1);
1508 info->offset += length;
1509 g_free (new_buffer);
1516 /*************************************************************
1517 * gtk_selection_retrieval_timeout:
1518 * Timeout callback while receiving a selection.
1520 * info: Information about this retrieval
1522 *************************************************************/
1525 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1530 GDK_THREADS_ENTER ();
1532 /* Determine if retrieval has finished by checking if it still in
1533 list of pending retrievals */
1535 tmp_list = current_retrievals;
1538 if (info == (GtkRetrievalInfo *)tmp_list->data)
1540 tmp_list = tmp_list->next;
1543 /* If retrieval is finished */
1544 if (!tmp_list || info->idle_time >= 5)
1546 if (tmp_list && info->idle_time >= 5)
1548 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1549 g_list_free (tmp_list);
1550 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1553 g_free (info->buffer);
1556 retval = FALSE; /* remove timeout */
1562 retval = TRUE; /* timeout will happen again */
1565 GDK_THREADS_LEAVE ();
1570 /*************************************************************
1571 * gtk_selection_retrieval_report:
1572 * Emits a "selection_received" signal.
1574 * info: information about the retrieval that completed
1575 * buffer: buffer containing data (NULL => errror)
1576 * time: timestamp for data in buffer
1578 *************************************************************/
1581 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1582 GdkAtom type, gint format,
1583 guchar *buffer, gint length,
1586 GtkSelectionData data;
1588 data.selection = info->selection;
1589 data.target = info->target;
1591 data.format = format;
1593 data.length = length;
1596 gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1597 "selection_received",
1601 /*************************************************************
1602 * gtk_selection_invoke_handler:
1603 * Finds and invokes handler for specified
1604 * widget/selection/target combination, calls
1605 * gtk_selection_default_handler if none exists.
1608 * widget: selection owner
1609 * data: selection data [INOUT]
1610 * time: time from requeset
1613 * Number of bytes written to buffer, -1 if error
1614 *************************************************************/
1617 gtk_selection_invoke_handler (GtkWidget *widget,
1618 GtkSelectionData *data,
1621 GtkTargetList *target_list;
1625 g_return_if_fail (widget != NULL);
1627 target_list = gtk_selection_target_list_get (widget, data->selection);
1629 gtk_target_list_find (target_list, data->target, &info))
1631 gtk_signal_emit_by_name (GTK_OBJECT (widget),
1637 gtk_selection_default_handler (widget, data);
1640 /*************************************************************
1641 * gtk_selection_default_handler:
1642 * Handles some default targets that exist for any widget
1643 * If it can't fit results into buffer, returns -1. This
1644 * won't happen in any conceivable case, since it would
1645 * require 1000 selection targets!
1648 * widget: selection owner
1649 * data: selection data [INOUT]
1651 *************************************************************/
1654 gtk_selection_default_handler (GtkWidget *widget,
1655 GtkSelectionData *data)
1657 if (data->target == gtk_selection_atoms[TIMESTAMP])
1659 /* Time which was used to obtain selection */
1661 GtkSelectionInfo *selection_info;
1663 tmp_list = current_selections;
1666 selection_info = (GtkSelectionInfo *)tmp_list->data;
1667 if ((selection_info->widget == widget) &&
1668 (selection_info->selection == data->selection))
1670 gulong time = selection_info->time;
1672 gtk_selection_data_set (data,
1673 GDK_SELECTION_TYPE_INTEGER,
1680 tmp_list = tmp_list->next;
1685 else if (data->target == gtk_selection_atoms[TARGETS])
1687 /* List of all targets supported for this widget/selection pair */
1691 GtkTargetList *target_list;
1692 GtkTargetPair *pair;
1694 target_list = gtk_selection_target_list_get (widget,
1696 count = g_list_length (target_list->list) + 3;
1698 data->type = GDK_SELECTION_TYPE_ATOM;
1700 data->length = count * sizeof (GdkAtom);
1702 p = g_new (GdkAtom, count);
1703 data->data = (guchar *)p;
1705 *p++ = gtk_selection_atoms[TIMESTAMP];
1706 *p++ = gtk_selection_atoms[TARGETS];
1707 *p++ = gtk_selection_atoms[MULTIPLE];
1709 tmp_list = target_list->list;
1712 pair = (GtkTargetPair *)tmp_list->data;
1713 *p++ = pair->target;
1715 tmp_list = tmp_list->next;
1726 gtk_selection_data_copy (GtkSelectionData *selection_data)
1728 GtkSelectionData *new_data;
1730 g_return_val_if_fail (selection_data != NULL, NULL);
1732 new_data = g_new (GtkSelectionData, 1);
1733 *new_data = *selection_data;
1735 if (selection_data->data)
1737 new_data->data = g_malloc (selection_data->length + 1);
1738 memcpy (new_data->data, selection_data->data, selection_data->length + 1);
1745 gtk_selection_data_free (GtkSelectionData *data)
1747 g_return_if_fail (data != NULL);
1750 g_free (data->data);
1756 gtk_selection_bytes_per_item (gint format)
1761 return sizeof (char);
1764 return sizeof (short);
1767 return sizeof (long);
1770 g_assert_not_reached();