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/.
59 #include "gtkselection.h"
61 #ifdef GDK_WINDOWING_X11
65 /* #define DEBUG_SELECTION */
67 /* Maximum size of a sent chunk, in bytes. Also the default size of
69 #ifdef GDK_WINDOWING_WIN32
70 /* No chunks on Win32 */
71 #define GTK_SELECTION_MAX_SIZE G_MAXINT
73 #define GTK_SELECTION_MAX_SIZE 4000
76 #define IDLE_ABORT_TIME 300
86 typedef struct _GtkSelectionInfo GtkSelectionInfo;
87 typedef struct _GtkIncrConversion GtkIncrConversion;
88 typedef struct _GtkIncrInfo GtkIncrInfo;
89 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
91 struct _GtkSelectionInfo
94 GtkWidget *widget; /* widget that owns selection */
95 guint32 time; /* time used to acquire selection */
96 GdkDisplay *display; /* needed in gtk_selection_remove_all */
99 struct _GtkIncrConversion
101 GdkAtom target; /* Requested target */
102 GdkAtom property; /* Property to store in */
103 GtkSelectionData data; /* The data being supplied */
104 gint offset; /* Current offset in sent selection.
106 * -2 => Only the final (empty) portion
112 GdkWindow *requestor; /* Requestor window - we create a GdkWindow
113 so we can receive events */
114 GdkAtom selection; /* Selection we're sending */
116 GtkIncrConversion *conversions; /* Information about requested conversions -
117 * With MULTIPLE requests (benighted 1980's
118 * hardware idea), there can be more than
120 gint num_conversions;
121 gint num_incrs; /* number of remaining INCR style transactions */
126 struct _GtkRetrievalInfo
129 GdkAtom selection; /* Selection being retrieved. */
130 GdkAtom target; /* Form of selection that we requested */
131 guint32 idle_time; /* Number of seconds since we last heard
132 from selection owner */
133 guchar *buffer; /* Buffer in which to accumulate results */
134 gint offset; /* Current offset in buffer, -1 indicates
136 guint32 notify_time; /* Timestamp from SelectionNotify */
139 /* Local Functions */
140 static void gtk_selection_init (void);
141 static gint gtk_selection_incr_timeout (GtkIncrInfo *info);
142 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
143 static void gtk_selection_retrieval_report (GtkRetrievalInfo *info,
149 static void gtk_selection_invoke_handler (GtkWidget *widget,
150 GtkSelectionData *data,
152 static void gtk_selection_default_handler (GtkWidget *widget,
153 GtkSelectionData *data);
154 static int gtk_selection_bytes_per_item (gint format);
157 static gint initialize = TRUE;
158 static GList *current_retrievals = NULL;
159 static GList *current_incrs = NULL;
160 static GList *current_selections = NULL;
162 static GdkAtom gtk_selection_atoms[LAST_ATOM];
163 static const char gtk_selection_handler_key[] = "gtk-selection-handlers";
174 gtk_target_list_new (const GtkTargetEntry *targets,
177 GtkTargetList *result = g_new (GtkTargetList, 1);
179 result->ref_count = 1;
182 gtk_target_list_add_table (result, targets, ntargets);
188 gtk_target_list_ref (GtkTargetList *list)
190 g_return_if_fail (list != NULL);
196 gtk_target_list_unref (GtkTargetList *list)
198 g_return_if_fail (list != NULL);
199 g_return_if_fail (list->ref_count > 0);
202 if (list->ref_count == 0)
204 GList *tmp_list = list->list;
207 GtkTargetPair *pair = tmp_list->data;
210 tmp_list = tmp_list->next;
213 g_list_free (list->list);
219 gtk_target_list_add (GtkTargetList *list,
226 g_return_if_fail (list != NULL);
228 pair = g_new (GtkTargetPair, 1);
229 pair->target = target;
233 list->list = g_list_append (list->list, pair);
237 gtk_target_list_add_table (GtkTargetList *list,
238 const GtkTargetEntry *targets,
243 for (i=ntargets-1; i >= 0; i--)
245 GtkTargetPair *pair = g_new (GtkTargetPair, 1);
246 pair->target = gdk_atom_intern (targets[i].target, FALSE);
247 pair->flags = targets[i].flags;
248 pair->info = targets[i].info;
250 list->list = g_list_prepend (list->list, pair);
255 gtk_target_list_remove (GtkTargetList *list,
260 g_return_if_fail (list != NULL);
262 tmp_list = list->list;
265 GtkTargetPair *pair = tmp_list->data;
267 if (pair->target == target)
271 list->list = g_list_remove_link (list->list, tmp_list);
272 g_list_free_1 (tmp_list);
277 tmp_list = tmp_list->next;
282 gtk_target_list_find (GtkTargetList *list,
286 GList *tmp_list = list->list;
289 GtkTargetPair *pair = tmp_list->data;
291 if (pair->target == target)
296 tmp_list = tmp_list->next;
303 * gtk_selection_owner_set_for_display:
304 * @display: the #Gdkdisplay where the selection is set
305 * @widget: new selection owner (a #GdkWidget), or %NULL.
306 * @selection: an interned atom representing the selection to claim.
307 * @time_: timestamp with which to claim the selection
309 * Claim ownership of a given selection for a particular widget, or,
310 * if @widget is %NULL, release ownership of the selection.
312 * Return value: TRUE if the operation succeeded
317 gtk_selection_owner_set_for_display (GdkDisplay *display,
323 GtkWidget *old_owner;
324 GtkSelectionInfo *selection_info = NULL;
327 g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
328 g_return_val_if_fail (selection != GDK_NONE, FALSE);
329 g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
330 g_return_val_if_fail (widget == NULL || gtk_widget_get_display (widget) == display, FALSE);
335 window = widget->window;
337 tmp_list = current_selections;
340 if (((GtkSelectionInfo *)tmp_list->data)->selection == selection)
342 selection_info = tmp_list->data;
346 tmp_list = tmp_list->next;
349 if (gdk_selection_owner_set_for_display (display, window, selection, time, TRUE))
357 old_owner = selection_info->widget;
358 current_selections = g_list_remove_link (current_selections,
360 g_list_free (tmp_list);
361 g_free (selection_info);
366 if (selection_info == NULL)
368 selection_info = g_new (GtkSelectionInfo, 1);
369 selection_info->selection = selection;
370 selection_info->widget = widget;
371 selection_info->time = time;
372 selection_info->display = display;
373 current_selections = g_list_prepend (current_selections,
378 old_owner = selection_info->widget;
379 selection_info->widget = widget;
380 selection_info->time = time;
381 selection_info->display = display;
384 /* If another widget in the application lost the selection,
385 * send it a GDK_SELECTION_CLEAR event.
387 if (old_owner && old_owner != widget)
389 GdkEvent *event = gdk_event_new (GDK_SELECTION_CLEAR);
391 event->selection.window = g_object_ref (old_owner->window);
392 event->selection.selection = selection;
393 event->selection.time = time;
395 gtk_widget_event (old_owner, event);
397 gdk_event_free (event);
406 * gtk_selection_owner_set:
407 * @widget: a #GtkWidget, or %NULL.
408 * @selection: an interned atom representing the selection to claim
409 * @time_: timestamp with which to claim the selection
411 * Claims ownership of a given selection for a particular widget,
412 * or, if @widget is %NULL, release ownership of the selection.
414 * Return value: %TRUE if the operation succeeded
417 gtk_selection_owner_set (GtkWidget *widget,
423 g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
424 g_return_val_if_fail (selection != GDK_NONE, FALSE);
427 display = gtk_widget_get_display (widget);
431 g_warning ("gtk_selection_owner_set (NULL,...) is not multihead safe"));
433 display = gdk_display_get_default ();
436 return gtk_selection_owner_set_for_display (display, widget,
440 /*************************************************************
441 * gtk_selection_add_target
442 * Add specified target to list of supported targets
445 * widget: The widget for which this target applies
448 * info: guint to pass to to the selection_get signal
451 *************************************************************/
453 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
455 struct _GtkSelectionTargetList {
460 static GtkTargetList *
461 gtk_selection_target_list_get (GtkWidget *widget,
464 GtkSelectionTargetList *sellist;
468 lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
473 sellist = tmp_list->data;
474 if (sellist->selection == selection)
475 return sellist->list;
476 tmp_list = tmp_list->next;
479 sellist = g_new (GtkSelectionTargetList, 1);
480 sellist->selection = selection;
481 sellist->list = gtk_target_list_new (NULL, 0);
483 lists = g_list_prepend (lists, sellist);
484 g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
486 return sellist->list;
490 gtk_selection_target_list_remove (GtkWidget *widget)
492 GtkSelectionTargetList *sellist;
496 lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
501 sellist = tmp_list->data;
503 gtk_target_list_unref (sellist->list);
506 tmp_list = tmp_list->next;
510 g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, NULL);
514 * gtk_selection_clear_targets:
515 * @widget: a #GtkWidget
516 * @selection: an atom representing a selection
518 * Remove all targets registered for the given selection for the
522 gtk_selection_clear_targets (GtkWidget *widget,
525 GtkSelectionTargetList *sellist;
529 g_return_if_fail (GTK_IS_WIDGET (widget));
530 g_return_if_fail (selection != GDK_NONE);
532 lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
537 sellist = tmp_list->data;
538 if (sellist->selection == selection)
540 lists = g_list_delete_link (lists, tmp_list);
541 gtk_target_list_unref (sellist->list);
547 tmp_list = tmp_list->next;
550 g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
554 gtk_selection_add_target (GtkWidget *widget,
561 g_return_if_fail (GTK_IS_WIDGET (widget));
562 g_return_if_fail (selection != GDK_NONE);
564 list = gtk_selection_target_list_get (widget, selection);
565 gtk_target_list_add (list, target, 0, info);
569 gtk_selection_add_targets (GtkWidget *widget,
571 const GtkTargetEntry *targets,
576 g_return_if_fail (GTK_IS_WIDGET (widget));
577 g_return_if_fail (selection != GDK_NONE);
578 g_return_if_fail (targets != NULL);
580 list = gtk_selection_target_list_get (widget, selection);
581 gtk_target_list_add_table (list, targets, ntargets);
585 /*************************************************************
586 * gtk_selection_remove_all:
587 * Removes all handlers and unsets ownership of all
588 * selections for a widget. Called when widget is being
594 *************************************************************/
597 gtk_selection_remove_all (GtkWidget *widget)
601 GtkSelectionInfo *selection_info;
603 /* Remove pending requests/incrs for this widget */
605 tmp_list = current_retrievals;
608 next = tmp_list->next;
609 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
611 current_retrievals = g_list_remove_link (current_retrievals,
613 /* structure will be freed in timeout */
614 g_list_free (tmp_list);
619 /* Disclaim ownership of any selections */
621 tmp_list = current_selections;
624 next = tmp_list->next;
625 selection_info = (GtkSelectionInfo *)tmp_list->data;
627 if (selection_info->widget == widget)
629 gdk_selection_owner_set_for_display (selection_info->display,
631 selection_info->selection,
632 GDK_CURRENT_TIME, FALSE);
633 current_selections = g_list_remove_link (current_selections,
635 g_list_free (tmp_list);
636 g_free (selection_info);
642 /* Remove all selection lists */
643 gtk_selection_target_list_remove (widget);
646 /*************************************************************
647 * gtk_selection_convert:
648 * Request the contents of a selection. When received,
649 * a "selection_received" signal will be generated.
652 * widget: The widget which acts as requestor
653 * selection: Which selection to get
654 * target: Form of information desired (e.g., STRING)
655 * time: Time of request (usually of triggering event)
656 * In emergency, you could use GDK_CURRENT_TIME
659 * TRUE if requested succeeded. FALSE if we could not process
660 * request. (e.g., there was already a request in process for
662 *************************************************************/
665 gtk_selection_convert (GtkWidget *widget,
670 GtkRetrievalInfo *info;
672 GdkWindow *owner_window;
675 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
676 g_return_val_if_fail (selection != GDK_NONE, FALSE);
679 gtk_selection_init ();
681 if (!GTK_WIDGET_REALIZED (widget))
682 gtk_widget_realize (widget);
684 /* Check to see if there are already any retrievals in progress for
685 this widget. If we changed GDK to use the selection for the
686 window property in which to store the retrieved information, then
687 we could support multiple retrievals for different selections.
688 This might be useful for DND. */
690 tmp_list = current_retrievals;
693 info = (GtkRetrievalInfo *)tmp_list->data;
694 if (info->widget == widget)
696 tmp_list = tmp_list->next;
699 info = g_new (GtkRetrievalInfo, 1);
701 info->widget = widget;
702 info->selection = selection;
703 info->target = target;
708 /* Check if this process has current owner. If so, call handler
709 procedure directly to avoid deadlocks with INCR. */
711 display = gtk_widget_get_display (widget);
712 owner_window = gdk_selection_owner_get_for_display (display, selection);
714 if (owner_window != NULL)
716 GtkWidget *owner_widget;
717 GtkSelectionData selection_data;
719 selection_data.selection = selection;
720 selection_data.target = target;
721 selection_data.data = NULL;
722 selection_data.length = -1;
723 selection_data.display = display;
725 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
727 if (owner_widget != NULL)
729 gtk_selection_invoke_handler (owner_widget,
733 gtk_selection_retrieval_report (info,
735 selection_data.format,
737 selection_data.length,
740 g_free (selection_data.data);
747 /* Otherwise, we need to go through X */
749 current_retrievals = g_list_append (current_retrievals, info);
750 gdk_selection_convert (widget->window, selection, target, time);
751 g_timeout_add (1000, (GSourceFunc) gtk_selection_retrieval_timeout, info);
756 /*************************************************************
757 * gtk_selection_data_set:
758 * Store new data into a GtkSelectionData object. Should
759 * _only_ by called from a selection handler callback.
760 * Null terminates the stored data.
762 * type: the type of selection data
763 * format: format (number of bits in a unit)
764 * data: pointer to the data (will be copied)
765 * length: length of the data
767 *************************************************************/
770 gtk_selection_data_set (GtkSelectionData *selection_data,
776 if (selection_data->data)
777 g_free (selection_data->data);
779 selection_data->type = type;
780 selection_data->format = format;
784 selection_data->data = g_new (guchar, length+1);
785 memcpy (selection_data->data, data, length);
786 selection_data->data[length] = 0;
790 g_return_if_fail (length <= 0);
793 selection_data->data = NULL;
795 selection_data->data = g_strdup("");
798 selection_data->length = length;
801 static GdkAtom utf8_atom;
802 static GdkAtom text_atom;
803 static GdkAtom ctext_atom;
810 utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
811 text_atom = gdk_atom_intern ("TEXT", FALSE);
812 ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
817 * gtk_selection_data_set_text:
818 * @selection_data: a #GtkSelectionData
819 * @str: a UTF-8 string
820 * @len: the length of @str, or -1 if @str is nul-terminated.
822 * Sets the contents of the selection from a UTF-8 encoded string.
823 * The string is converted to the form determined by
824 * @selection_data->target.
826 * Return value: %TRUE if the selection was successfully set,
830 gtk_selection_data_set_text (GtkSelectionData *selection_data,
834 gboolean result = FALSE;
841 if (selection_data->target == utf8_atom)
843 gtk_selection_data_set (selection_data,
845 8, (guchar *)str, len);
848 else if (selection_data->target == GDK_TARGET_STRING)
850 gchar *tmp = g_strndup (str, len);
851 gchar *latin1 = gdk_utf8_to_string_target (tmp);
856 gtk_selection_data_set (selection_data,
857 GDK_SELECTION_TYPE_STRING,
858 8, latin1, strlen (latin1));
865 else if (selection_data->target == ctext_atom ||
866 selection_data->target == text_atom)
874 tmp = g_strndup (str, len);
875 if (gdk_utf8_to_compound_text_for_display (selection_data->display, tmp,
876 &encoding, &format, &text, &new_length))
878 gtk_selection_data_set (selection_data, encoding, format, text, new_length);
879 gdk_free_compound_text (text);
891 * gtk_selection_data_get_text:
892 * @selection_data: a #GtkSelectionData
894 * Gets the contents of the selection data as a UTF-8 string.
896 * Return value: if the selection data contained a recognized
897 * text type and it could be converted to UTF-8, a newly allocated
898 * string containing the converted text, otherwise %NULL.
899 * If the result is non-%NULL it must be freed with g_free().
902 gtk_selection_data_get_text (GtkSelectionData *selection_data)
904 guchar *result = NULL;
908 if (selection_data->length >= 0 &&
909 (selection_data->type == GDK_TARGET_STRING ||
910 selection_data->type == ctext_atom ||
911 selection_data->type == utf8_atom))
915 gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
916 selection_data->type,
917 selection_data->format,
918 selection_data->data,
919 selection_data->length,
924 for (i = 1; i < count; i++)
933 * gtk_selection_data_get_targets:
934 * @selection_data: a #GtkSelectionData object
935 * @targets: location to store an array of targets. The result
936 * stored here must be freed with g_free().
937 * @n_atoms: location to store number of items in @targets.
939 * Gets the contents of @selection_data as an array of targets.
940 * This can be used to interpret the results of getting
941 * the standard TARGETS target that is always supplied for
944 * Return value: %TRUE if @selection_data contains a valid
945 * array of targets, otherwise %FALSE.
948 gtk_selection_data_get_targets (GtkSelectionData *selection_data,
952 if (selection_data->length >= 0 &&
953 selection_data->format == 32 &&
954 selection_data->type == GDK_SELECTION_TYPE_ATOM)
957 *targets = g_memdup (selection_data->data, selection_data->length);
959 *n_atoms = selection_data->length / sizeof (GdkAtom);
975 * gtk_selection_data_targets_include_text:
976 * @selection_data: a #GtkSelectionData object
978 * Given a #GtkSelectionData object holding a list of targets,
979 * determines if any of the targets in @targets can be used to
982 * Return value: %TRUE if @selection_data holds a list of targets,
983 * and a suitable target for text is included, otherwise %FALSE.
986 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
991 gboolean result = FALSE;
993 if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
995 for (i=0; i < n_targets; i++)
997 if (targets[i] == gdk_atom_intern ("STRING", FALSE) ||
998 targets[i] == gdk_atom_intern ("TEXT", FALSE) ||
999 targets[i] == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
1000 targets[i] == gdk_atom_intern ("UTF8_STRING", FALSE))
1010 /*************************************************************
1011 * gtk_selection_init:
1012 * Initialize local variables
1016 *************************************************************/
1019 gtk_selection_init (void)
1021 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
1022 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
1023 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
1024 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
1030 * gtk_selection_clear:
1031 * @widget: a #GtkWidget
1034 * The default handler for the GtkWidget::selection_clear_event
1037 * Return value: %TRUE if the event was handled, otherwise false
1041 * Deprecated: Instead of calling this function, chain up from
1042 * your selection_clear_event handler. Calling this function
1043 * from any other context is illegal.
1046 gtk_selection_clear (GtkWidget *widget,
1047 GdkEventSelection *event)
1049 /* Note that we filter clear events in gdkselection-x11.c, so
1050 * that we only will get here if the clear event actually
1051 * represents a change that we didn't do ourself.
1054 GtkSelectionInfo *selection_info = NULL;
1056 tmp_list = current_selections;
1059 selection_info = (GtkSelectionInfo *)tmp_list->data;
1061 if ((selection_info->selection == event->selection) &&
1062 (selection_info->widget == widget))
1065 tmp_list = tmp_list->next;
1070 current_selections = g_list_remove_link (current_selections, tmp_list);
1071 g_list_free (tmp_list);
1072 g_free (selection_info);
1079 /*************************************************************
1080 * _gtk_selection_request:
1081 * Handler for "selection_request_event"
1086 *************************************************************/
1089 _gtk_selection_request (GtkWidget *widget,
1090 GdkEventSelection *event)
1092 GdkDisplay *display = gtk_widget_get_display (widget);
1098 gtk_selection_init ();
1100 /* Check if we own selection */
1102 tmp_list = current_selections;
1105 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1107 if ((selection_info->selection == event->selection) &&
1108 (selection_info->widget == widget))
1111 tmp_list = tmp_list->next;
1114 if (tmp_list == NULL)
1117 info = g_new (GtkIncrInfo, 1);
1119 g_object_ref (widget);
1121 info->selection = event->selection;
1122 info->num_incrs = 0;
1124 /* Create GdkWindow structure for the requestor */
1126 info->requestor = gdk_window_lookup_for_display (display,
1128 if (!info->requestor)
1129 info->requestor = gdk_window_foreign_new_for_display (display,
1132 /* Determine conversions we need to perform */
1134 if (event->target == gtk_selection_atoms[MULTIPLE])
1143 gdk_error_trap_push ();
1144 if (!gdk_property_get (info->requestor, event->property, GDK_NONE, /* AnyPropertyType */
1145 0, GTK_SELECTION_MAX_SIZE, FALSE,
1146 &type, &format, &length, &mult_atoms))
1148 gdk_selection_send_notify_for_display (display,
1154 g_free (mult_atoms);
1158 gdk_error_trap_pop ();
1160 /* This is annoying; the ICCCM doesn't specify the property type
1161 * used for the property contents, so the autoconversion for
1162 * ATOM / ATOM_PAIR in GDK doesn't work properly.
1164 #ifdef GDK_WINDOWING_X11
1165 if (type != GDK_SELECTION_TYPE_ATOM &&
1166 type != gdk_atom_intern ("ATOM_PAIR", FALSE))
1168 info->num_conversions = length / (2*sizeof (glong));
1169 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1171 for (i=0; i<info->num_conversions; i++)
1173 info->conversions[i].target = gdk_x11_xatom_to_atom_for_display (display,
1174 ((glong *)mult_atoms)[2*i]);
1175 info->conversions[i].property = gdk_x11_xatom_to_atom_for_display (display,
1176 ((glong *)mult_atoms)[2*i + 1]);
1182 info->num_conversions = length / (2*sizeof (GdkAtom));
1183 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1185 for (i=0; i<info->num_conversions; i++)
1187 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1188 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1192 else /* only a single conversion */
1194 info->conversions = g_new (GtkIncrConversion, 1);
1195 info->num_conversions = 1;
1196 info->conversions[0].target = event->target;
1197 info->conversions[0].property = event->property;
1200 /* Loop through conversions and determine which of these are big
1201 enough to require doing them via INCR */
1202 for (i=0; i<info->num_conversions; i++)
1204 GtkSelectionData data;
1207 data.selection = event->selection;
1208 data.target = info->conversions[i].target;
1211 data.display = gtk_widget_get_display (widget);
1213 #ifdef DEBUG_SELECTION
1214 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1215 event->selection, info->conversions[i].target,
1216 gdk_atom_name (info->conversions[i].target),
1217 event->requestor, event->property);
1220 gtk_selection_invoke_handler (widget, &data, event->time);
1222 if (data.length < 0)
1224 info->conversions[i].property = GDK_NONE;
1228 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1230 items = data.length / gtk_selection_bytes_per_item (data.format);
1232 if (data.length > GTK_SELECTION_MAX_SIZE)
1234 /* Sending via INCR */
1236 info->conversions[i].offset = 0;
1237 info->conversions[i].data = data;
1240 gdk_property_change (info->requestor,
1241 info->conversions[i].property,
1242 gtk_selection_atoms[INCR],
1244 GDK_PROP_MODE_REPLACE,
1245 (guchar *)&items, 1);
1249 info->conversions[i].offset = -1;
1251 gdk_property_change (info->requestor,
1252 info->conversions[i].property,
1255 GDK_PROP_MODE_REPLACE,
1262 /* If we have some INCR's, we need to send the rest of the data in
1265 if (info->num_incrs > 0)
1267 /* FIXME: this could be dangerous if window doesn't still
1270 #ifdef DEBUG_SELECTION
1271 g_message ("Starting INCR...");
1274 gdk_window_set_events (info->requestor,
1275 gdk_window_get_events (info->requestor) |
1276 GDK_PROPERTY_CHANGE_MASK);
1277 current_incrs = g_list_append (current_incrs, info);
1278 g_timeout_add (1000, (GSourceFunc) gtk_selection_incr_timeout, info);
1281 /* If it was a MULTIPLE request, set the property to indicate which
1282 conversions succeeded */
1283 if (event->target == gtk_selection_atoms[MULTIPLE])
1285 GdkAtom *mult_atoms = g_new (GdkAtom, 2 * info->num_conversions);
1286 for (i = 0; i < info->num_conversions; i++)
1288 mult_atoms[2*i] = info->conversions[i].target;
1289 mult_atoms[2*i+1] = info->conversions[i].property;
1292 gdk_property_change (info->requestor, event->property,
1293 gdk_atom_intern ("ATOM_PAIR", FALSE), 32,
1294 GDK_PROP_MODE_REPLACE,
1295 (guchar *)mult_atoms, 2*info->num_conversions);
1296 g_free (mult_atoms);
1299 if (info->num_conversions == 1 &&
1300 info->conversions[0].property == GDK_NONE)
1302 /* Reject the entire conversion */
1303 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1312 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1320 if (info->num_incrs == 0)
1322 g_free (info->conversions);
1326 g_object_unref (widget);
1331 /*************************************************************
1332 * _gtk_selection_incr_event:
1333 * Called whenever an PropertyNotify event occurs for an
1334 * GdkWindow with user_data == NULL. These will be notifications
1335 * that a window we are sending the selection to via the
1336 * INCR protocol has deleted a property and is ready for
1340 * window: the requestor window
1341 * event: the property event structure
1344 *************************************************************/
1347 _gtk_selection_incr_event (GdkWindow *window,
1348 GdkEventProperty *event)
1351 GtkIncrInfo *info = NULL;
1357 if (event->state != GDK_PROPERTY_DELETE)
1360 #ifdef DEBUG_SELECTION
1361 g_message ("PropertyDelete, property %ld", event->atom);
1364 /* Now find the appropriate ongoing INCR */
1365 tmp_list = current_incrs;
1368 info = (GtkIncrInfo *)tmp_list->data;
1369 if (info->requestor == event->window)
1372 tmp_list = tmp_list->next;
1375 if (tmp_list == NULL)
1378 /* Find out which target this is for */
1379 for (i=0; i<info->num_conversions; i++)
1381 if (info->conversions[i].property == event->atom &&
1382 info->conversions[i].offset != -1)
1386 info->idle_time = 0;
1388 if (info->conversions[i].offset == -2) /* only the last 0-length
1396 num_bytes = info->conversions[i].data.length -
1397 info->conversions[i].offset;
1398 buffer = info->conversions[i].data.data +
1399 info->conversions[i].offset;
1401 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1403 num_bytes = GTK_SELECTION_MAX_SIZE;
1404 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1407 info->conversions[i].offset = -2;
1409 #ifdef DEBUG_SELECTION
1410 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1411 num_bytes, info->conversions[i].offset,
1412 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1415 bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1416 gdk_property_change (info->requestor, event->atom,
1417 info->conversions[i].data.type,
1418 info->conversions[i].data.format,
1419 GDK_PROP_MODE_REPLACE,
1421 num_bytes / bytes_per_item);
1423 if (info->conversions[i].offset == -2)
1425 g_free (info->conversions[i].data.data);
1426 info->conversions[i].data.data = NULL;
1432 info->conversions[i].offset = -1;
1438 /* Check if we're finished with all the targets */
1440 if (info->num_incrs == 0)
1442 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1443 g_list_free (tmp_list);
1444 /* Let the timeout free it */
1450 /*************************************************************
1451 * gtk_selection_incr_timeout:
1452 * Timeout callback for the sending portion of the INCR
1455 * info: Information about this incr
1457 *************************************************************/
1460 gtk_selection_incr_timeout (GtkIncrInfo *info)
1465 GDK_THREADS_ENTER ();
1467 /* Determine if retrieval has finished by checking if it still in
1468 list of pending retrievals */
1470 tmp_list = current_incrs;
1473 if (info == (GtkIncrInfo *)tmp_list->data)
1475 tmp_list = tmp_list->next;
1478 /* If retrieval is finished */
1479 if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1481 if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1483 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1484 g_list_free (tmp_list);
1487 g_free (info->conversions);
1488 /* FIXME: we should check if requestor window is still in use,
1489 and if not, remove it? */
1493 retval = FALSE; /* remove timeout */
1499 retval = TRUE; /* timeout will happen again */
1502 GDK_THREADS_LEAVE ();
1507 /*************************************************************
1508 * _gtk_selection_notify:
1509 * Handler for "selection_notify_event" signals on windows
1510 * where a retrieval is currently in process. The selection
1511 * owner has responded to our conversion request.
1513 * widget: Widget getting signal
1514 * event: Selection event structure
1515 * info: Information about this retrieval
1517 * was event handled?
1518 *************************************************************/
1521 _gtk_selection_notify (GtkWidget *widget,
1522 GdkEventSelection *event)
1525 GtkRetrievalInfo *info = NULL;
1526 guchar *buffer = NULL;
1531 #ifdef DEBUG_SELECTION
1532 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1533 event->selection, event->target, event->property);
1536 tmp_list = current_retrievals;
1539 info = (GtkRetrievalInfo *)tmp_list->data;
1540 if (info->widget == widget && info->selection == event->selection)
1542 tmp_list = tmp_list->next;
1545 if (!tmp_list) /* no retrieval in progress */
1548 if (event->property != GDK_NONE)
1549 length = gdk_selection_property_get (widget->window, &buffer,
1552 length = 0; /* silence gcc */
1554 if (event->property == GDK_NONE || buffer == NULL)
1556 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1557 g_list_free (tmp_list);
1558 /* structure will be freed in timeout */
1559 gtk_selection_retrieval_report (info,
1560 GDK_NONE, 0, NULL, -1, event->time);
1565 if (type == gtk_selection_atoms[INCR])
1567 /* The remainder of the selection will come through PropertyNotify
1570 info->notify_time = event->time;
1571 info->idle_time = 0;
1572 info->offset = 0; /* Mark as OK to proceed */
1573 gdk_window_set_events (widget->window,
1574 gdk_window_get_events (widget->window)
1575 | GDK_PROPERTY_CHANGE_MASK);
1579 /* We don't delete the info structure - that will happen in timeout */
1580 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1581 g_list_free (tmp_list);
1583 info->offset = length;
1584 gtk_selection_retrieval_report (info,
1586 buffer, length, event->time);
1589 gdk_property_delete (widget->window, event->property);
1596 /*************************************************************
1597 * _gtk_selection_property_notify:
1598 * Handler for "property_notify_event" signals on windows
1599 * where a retrieval is currently in process. The selection
1600 * owner has added more data.
1602 * widget: Widget getting signal
1603 * event: Property event structure
1604 * info: Information about this retrieval
1606 * was event handled?
1607 *************************************************************/
1610 _gtk_selection_property_notify (GtkWidget *widget,
1611 GdkEventProperty *event)
1614 GtkRetrievalInfo *info = NULL;
1620 g_return_val_if_fail (widget != NULL, FALSE);
1621 g_return_val_if_fail (event != NULL, FALSE);
1623 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1624 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1625 (event->atom != gdk_atom_intern ("GDK_SELECTION", FALSE))) /* not the right property */
1629 #ifdef DEBUG_SELECTION
1630 g_message ("PropertyNewValue, property %ld",
1634 tmp_list = current_retrievals;
1637 info = (GtkRetrievalInfo *)tmp_list->data;
1638 if (info->widget == widget)
1640 tmp_list = tmp_list->next;
1643 if (!tmp_list) /* No retrieval in progress */
1646 if (info->offset < 0) /* We haven't got the SelectionNotify
1647 for this retrieval yet */
1650 info->idle_time = 0;
1652 length = gdk_selection_property_get (widget->window, &new_buffer,
1654 gdk_property_delete (widget->window, event->atom);
1656 /* We could do a lot better efficiency-wise by paying attention to
1657 what length was sent in the initial INCR transaction, instead of
1658 doing memory allocation at every step. But its only guaranteed to
1659 be a _lower bound_ (pretty useless!) */
1661 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1663 /* Info structure will be freed in timeout */
1664 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1665 g_list_free (tmp_list);
1666 gtk_selection_retrieval_report (info,
1668 (type == GDK_NONE) ? NULL : info->buffer,
1669 (type == GDK_NONE) ? -1 : info->offset,
1672 else /* append on newly arrived data */
1676 #ifdef DEBUG_SELECTION
1677 g_message ("Start - Adding %d bytes at offset 0",
1680 info->buffer = new_buffer;
1681 info->offset = length;
1686 #ifdef DEBUG_SELECTION
1687 g_message ("Appending %d bytes at offset %d",
1688 length,info->offset);
1690 /* We copy length+1 bytes to preserve guaranteed null termination */
1691 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1692 memcpy (info->buffer + info->offset, new_buffer, length+1);
1693 info->offset += length;
1694 g_free (new_buffer);
1701 /*************************************************************
1702 * gtk_selection_retrieval_timeout:
1703 * Timeout callback while receiving a selection.
1705 * info: Information about this retrieval
1707 *************************************************************/
1710 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1715 GDK_THREADS_ENTER ();
1717 /* Determine if retrieval has finished by checking if it still in
1718 list of pending retrievals */
1720 tmp_list = current_retrievals;
1723 if (info == (GtkRetrievalInfo *)tmp_list->data)
1725 tmp_list = tmp_list->next;
1728 /* If retrieval is finished */
1729 if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1731 if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1733 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1734 g_list_free (tmp_list);
1735 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1738 g_free (info->buffer);
1741 retval = FALSE; /* remove timeout */
1747 retval = TRUE; /* timeout will happen again */
1750 GDK_THREADS_LEAVE ();
1755 /*************************************************************
1756 * gtk_selection_retrieval_report:
1757 * Emits a "selection_received" signal.
1759 * info: information about the retrieval that completed
1760 * buffer: buffer containing data (NULL => errror)
1761 * time: timestamp for data in buffer
1763 *************************************************************/
1766 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1767 GdkAtom type, gint format,
1768 guchar *buffer, gint length,
1771 GtkSelectionData data;
1773 data.selection = info->selection;
1774 data.target = info->target;
1776 data.format = format;
1778 data.length = length;
1780 data.display = gtk_widget_get_display (info->widget);
1782 g_signal_emit_by_name (info->widget,
1783 "selection_received",
1787 /*************************************************************
1788 * gtk_selection_invoke_handler:
1789 * Finds and invokes handler for specified
1790 * widget/selection/target combination, calls
1791 * gtk_selection_default_handler if none exists.
1794 * widget: selection owner
1795 * data: selection data [INOUT]
1796 * time: time from requeset
1799 * Number of bytes written to buffer, -1 if error
1800 *************************************************************/
1803 gtk_selection_invoke_handler (GtkWidget *widget,
1804 GtkSelectionData *data,
1807 GtkTargetList *target_list;
1811 g_return_if_fail (widget != NULL);
1813 target_list = gtk_selection_target_list_get (widget, data->selection);
1815 gtk_target_list_find (target_list, data->target, &info))
1817 g_signal_emit_by_name (widget,
1823 gtk_selection_default_handler (widget, data);
1826 /*************************************************************
1827 * gtk_selection_default_handler:
1828 * Handles some default targets that exist for any widget
1829 * If it can't fit results into buffer, returns -1. This
1830 * won't happen in any conceivable case, since it would
1831 * require 1000 selection targets!
1834 * widget: selection owner
1835 * data: selection data [INOUT]
1837 *************************************************************/
1840 gtk_selection_default_handler (GtkWidget *widget,
1841 GtkSelectionData *data)
1843 if (data->target == gtk_selection_atoms[TIMESTAMP])
1845 /* Time which was used to obtain selection */
1847 GtkSelectionInfo *selection_info;
1849 tmp_list = current_selections;
1852 selection_info = (GtkSelectionInfo *)tmp_list->data;
1853 if ((selection_info->widget == widget) &&
1854 (selection_info->selection == data->selection))
1856 gulong time = selection_info->time;
1858 gtk_selection_data_set (data,
1859 GDK_SELECTION_TYPE_INTEGER,
1866 tmp_list = tmp_list->next;
1871 else if (data->target == gtk_selection_atoms[TARGETS])
1873 /* List of all targets supported for this widget/selection pair */
1877 GtkTargetList *target_list;
1878 GtkTargetPair *pair;
1880 target_list = gtk_selection_target_list_get (widget,
1882 count = g_list_length (target_list->list) + 3;
1884 data->type = GDK_SELECTION_TYPE_ATOM;
1886 data->length = count * sizeof (GdkAtom);
1888 /* selection data is always terminated by a trailing \0
1890 p = g_malloc (data->length + 1);
1891 data->data = (guchar *)p;
1892 data->data[data->length] = '\0';
1894 *p++ = gtk_selection_atoms[TIMESTAMP];
1895 *p++ = gtk_selection_atoms[TARGETS];
1896 *p++ = gtk_selection_atoms[MULTIPLE];
1898 tmp_list = target_list->list;
1901 pair = (GtkTargetPair *)tmp_list->data;
1902 *p++ = pair->target;
1904 tmp_list = tmp_list->next;
1915 gtk_selection_data_copy (GtkSelectionData *selection_data)
1917 GtkSelectionData *new_data;
1919 g_return_val_if_fail (selection_data != NULL, NULL);
1921 new_data = g_new (GtkSelectionData, 1);
1922 *new_data = *selection_data;
1924 if (selection_data->data)
1926 new_data->data = g_malloc (selection_data->length + 1);
1927 memcpy (new_data->data, selection_data->data, selection_data->length + 1);
1934 gtk_selection_data_free (GtkSelectionData *data)
1936 g_return_if_fail (data != NULL);
1939 g_free (data->data);
1945 gtk_selection_data_get_type (void)
1947 static GType our_type = 0;
1950 our_type = g_boxed_type_register_static ("GtkSelectionData",
1951 (GBoxedCopyFunc) gtk_selection_data_copy,
1952 (GBoxedFreeFunc) gtk_selection_data_free);
1958 gtk_selection_bytes_per_item (gint format)
1963 return sizeof (char);
1966 return sizeof (short);
1969 return sizeof (long);
1972 g_assert_not_reached();