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 selection_set_string (GtkSelectionData *selection_data,
821 gchar *tmp = g_strndup (str, len);
822 gchar *latin1 = gdk_utf8_to_string_target (tmp);
827 gtk_selection_data_set (selection_data,
828 GDK_SELECTION_TYPE_STRING,
829 8, latin1, strlen (latin1));
839 selection_set_compound_text (GtkSelectionData *selection_data,
848 gboolean result = FALSE;
850 tmp = g_strndup (str, len);
851 if (gdk_utf8_to_compound_text_for_display (selection_data->display, tmp,
852 &encoding, &format, &text, &new_length))
854 gtk_selection_data_set (selection_data, encoding, format, text, new_length);
855 gdk_free_compound_text (text);
866 * gtk_selection_data_set_text:
867 * @selection_data: a #GtkSelectionData
868 * @str: a UTF-8 string
869 * @len: the length of @str, or -1 if @str is nul-terminated.
871 * Sets the contents of the selection from a UTF-8 encoded string.
872 * The string is converted to the form determined by
873 * @selection_data->target.
875 * Return value: %TRUE if the selection was successfully set,
879 gtk_selection_data_set_text (GtkSelectionData *selection_data,
888 if (selection_data->target == utf8_atom)
890 gtk_selection_data_set (selection_data,
892 8, (guchar *)str, len);
895 else if (selection_data->target == GDK_TARGET_STRING)
897 return selection_set_string (selection_data, str, len);
899 else if (selection_data->target == ctext_atom ||
900 selection_data->target == text_atom)
902 if (selection_set_compound_text (selection_data, str, len))
904 else if (selection_data->target == text_atom)
905 return selection_set_string (selection_data, str, len);
912 * gtk_selection_data_get_text:
913 * @selection_data: a #GtkSelectionData
915 * Gets the contents of the selection data as a UTF-8 string.
917 * Return value: if the selection data contained a recognized
918 * text type and it could be converted to UTF-8, a newly allocated
919 * string containing the converted text, otherwise %NULL.
920 * If the result is non-%NULL it must be freed with g_free().
923 gtk_selection_data_get_text (GtkSelectionData *selection_data)
925 guchar *result = NULL;
929 if (selection_data->length >= 0 &&
930 (selection_data->type == GDK_TARGET_STRING ||
931 selection_data->type == ctext_atom ||
932 selection_data->type == utf8_atom))
936 gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
937 selection_data->type,
938 selection_data->format,
939 selection_data->data,
940 selection_data->length,
945 for (i = 1; i < count; i++)
954 * gtk_selection_data_get_targets:
955 * @selection_data: a #GtkSelectionData object
956 * @targets: location to store an array of targets. The result
957 * stored here must be freed with g_free().
958 * @n_atoms: location to store number of items in @targets.
960 * Gets the contents of @selection_data as an array of targets.
961 * This can be used to interpret the results of getting
962 * the standard TARGETS target that is always supplied for
965 * Return value: %TRUE if @selection_data contains a valid
966 * array of targets, otherwise %FALSE.
969 gtk_selection_data_get_targets (GtkSelectionData *selection_data,
973 if (selection_data->length >= 0 &&
974 selection_data->format == 32 &&
975 selection_data->type == GDK_SELECTION_TYPE_ATOM)
978 *targets = g_memdup (selection_data->data, selection_data->length);
980 *n_atoms = selection_data->length / sizeof (GdkAtom);
996 * gtk_selection_data_targets_include_text:
997 * @selection_data: a #GtkSelectionData object
999 * Given a #GtkSelectionData object holding a list of targets,
1000 * determines if any of the targets in @targets can be used to
1003 * Return value: %TRUE if @selection_data holds a list of targets,
1004 * and a suitable target for text is included, otherwise %FALSE.
1007 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
1012 gboolean result = FALSE;
1014 if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
1016 for (i=0; i < n_targets; i++)
1018 if (targets[i] == gdk_atom_intern ("STRING", FALSE) ||
1019 targets[i] == gdk_atom_intern ("TEXT", FALSE) ||
1020 targets[i] == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
1021 targets[i] == gdk_atom_intern ("UTF8_STRING", FALSE))
1031 /*************************************************************
1032 * gtk_selection_init:
1033 * Initialize local variables
1037 *************************************************************/
1040 gtk_selection_init (void)
1042 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
1043 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
1044 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
1045 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
1051 * gtk_selection_clear:
1052 * @widget: a #GtkWidget
1055 * The default handler for the GtkWidget::selection_clear_event
1058 * Return value: %TRUE if the event was handled, otherwise false
1062 * Deprecated: Instead of calling this function, chain up from
1063 * your selection_clear_event handler. Calling this function
1064 * from any other context is illegal.
1067 gtk_selection_clear (GtkWidget *widget,
1068 GdkEventSelection *event)
1070 /* Note that we filter clear events in gdkselection-x11.c, so
1071 * that we only will get here if the clear event actually
1072 * represents a change that we didn't do ourself.
1075 GtkSelectionInfo *selection_info = NULL;
1077 tmp_list = current_selections;
1080 selection_info = (GtkSelectionInfo *)tmp_list->data;
1082 if ((selection_info->selection == event->selection) &&
1083 (selection_info->widget == widget))
1086 tmp_list = tmp_list->next;
1091 current_selections = g_list_remove_link (current_selections, tmp_list);
1092 g_list_free (tmp_list);
1093 g_free (selection_info);
1100 /*************************************************************
1101 * _gtk_selection_request:
1102 * Handler for "selection_request_event"
1107 *************************************************************/
1110 _gtk_selection_request (GtkWidget *widget,
1111 GdkEventSelection *event)
1113 GdkDisplay *display = gtk_widget_get_display (widget);
1119 gtk_selection_init ();
1121 /* Check if we own selection */
1123 tmp_list = current_selections;
1126 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1128 if ((selection_info->selection == event->selection) &&
1129 (selection_info->widget == widget))
1132 tmp_list = tmp_list->next;
1135 if (tmp_list == NULL)
1138 info = g_new (GtkIncrInfo, 1);
1140 g_object_ref (widget);
1142 info->selection = event->selection;
1143 info->num_incrs = 0;
1145 /* Create GdkWindow structure for the requestor */
1147 info->requestor = gdk_window_lookup_for_display (display,
1149 if (!info->requestor)
1150 info->requestor = gdk_window_foreign_new_for_display (display,
1153 /* Determine conversions we need to perform */
1155 if (event->target == gtk_selection_atoms[MULTIPLE])
1164 gdk_error_trap_push ();
1165 if (!gdk_property_get (info->requestor, event->property, GDK_NONE, /* AnyPropertyType */
1166 0, GTK_SELECTION_MAX_SIZE, FALSE,
1167 &type, &format, &length, &mult_atoms))
1169 gdk_selection_send_notify_for_display (display,
1175 g_free (mult_atoms);
1179 gdk_error_trap_pop ();
1181 /* This is annoying; the ICCCM doesn't specify the property type
1182 * used for the property contents, so the autoconversion for
1183 * ATOM / ATOM_PAIR in GDK doesn't work properly.
1185 #ifdef GDK_WINDOWING_X11
1186 if (type != GDK_SELECTION_TYPE_ATOM &&
1187 type != gdk_atom_intern ("ATOM_PAIR", FALSE))
1189 info->num_conversions = length / (2*sizeof (glong));
1190 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1192 for (i=0; i<info->num_conversions; i++)
1194 info->conversions[i].target = gdk_x11_xatom_to_atom_for_display (display,
1195 ((glong *)mult_atoms)[2*i]);
1196 info->conversions[i].property = gdk_x11_xatom_to_atom_for_display (display,
1197 ((glong *)mult_atoms)[2*i + 1]);
1203 info->num_conversions = length / (2*sizeof (GdkAtom));
1204 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1206 for (i=0; i<info->num_conversions; i++)
1208 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1209 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1213 else /* only a single conversion */
1215 info->conversions = g_new (GtkIncrConversion, 1);
1216 info->num_conversions = 1;
1217 info->conversions[0].target = event->target;
1218 info->conversions[0].property = event->property;
1221 /* Loop through conversions and determine which of these are big
1222 enough to require doing them via INCR */
1223 for (i=0; i<info->num_conversions; i++)
1225 GtkSelectionData data;
1228 data.selection = event->selection;
1229 data.target = info->conversions[i].target;
1232 data.display = gtk_widget_get_display (widget);
1234 #ifdef DEBUG_SELECTION
1235 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1236 event->selection, info->conversions[i].target,
1237 gdk_atom_name (info->conversions[i].target),
1238 event->requestor, event->property);
1241 gtk_selection_invoke_handler (widget, &data, event->time);
1243 if (data.length < 0)
1245 info->conversions[i].property = GDK_NONE;
1249 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1251 items = data.length / gtk_selection_bytes_per_item (data.format);
1253 if (data.length > GTK_SELECTION_MAX_SIZE)
1255 /* Sending via INCR */
1257 info->conversions[i].offset = 0;
1258 info->conversions[i].data = data;
1261 gdk_property_change (info->requestor,
1262 info->conversions[i].property,
1263 gtk_selection_atoms[INCR],
1265 GDK_PROP_MODE_REPLACE,
1266 (guchar *)&items, 1);
1270 info->conversions[i].offset = -1;
1272 gdk_property_change (info->requestor,
1273 info->conversions[i].property,
1276 GDK_PROP_MODE_REPLACE,
1283 /* If we have some INCR's, we need to send the rest of the data in
1286 if (info->num_incrs > 0)
1288 /* FIXME: this could be dangerous if window doesn't still
1291 #ifdef DEBUG_SELECTION
1292 g_message ("Starting INCR...");
1295 gdk_window_set_events (info->requestor,
1296 gdk_window_get_events (info->requestor) |
1297 GDK_PROPERTY_CHANGE_MASK);
1298 current_incrs = g_list_append (current_incrs, info);
1299 g_timeout_add (1000, (GSourceFunc) gtk_selection_incr_timeout, info);
1302 /* If it was a MULTIPLE request, set the property to indicate which
1303 conversions succeeded */
1304 if (event->target == gtk_selection_atoms[MULTIPLE])
1306 GdkAtom *mult_atoms = g_new (GdkAtom, 2 * info->num_conversions);
1307 for (i = 0; i < info->num_conversions; i++)
1309 mult_atoms[2*i] = info->conversions[i].target;
1310 mult_atoms[2*i+1] = info->conversions[i].property;
1313 gdk_property_change (info->requestor, event->property,
1314 gdk_atom_intern ("ATOM_PAIR", FALSE), 32,
1315 GDK_PROP_MODE_REPLACE,
1316 (guchar *)mult_atoms, 2*info->num_conversions);
1317 g_free (mult_atoms);
1320 if (info->num_conversions == 1 &&
1321 info->conversions[0].property == GDK_NONE)
1323 /* Reject the entire conversion */
1324 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1333 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1341 if (info->num_incrs == 0)
1343 g_free (info->conversions);
1347 g_object_unref (widget);
1352 /*************************************************************
1353 * _gtk_selection_incr_event:
1354 * Called whenever an PropertyNotify event occurs for an
1355 * GdkWindow with user_data == NULL. These will be notifications
1356 * that a window we are sending the selection to via the
1357 * INCR protocol has deleted a property and is ready for
1361 * window: the requestor window
1362 * event: the property event structure
1365 *************************************************************/
1368 _gtk_selection_incr_event (GdkWindow *window,
1369 GdkEventProperty *event)
1372 GtkIncrInfo *info = NULL;
1378 if (event->state != GDK_PROPERTY_DELETE)
1381 #ifdef DEBUG_SELECTION
1382 g_message ("PropertyDelete, property %ld", event->atom);
1385 /* Now find the appropriate ongoing INCR */
1386 tmp_list = current_incrs;
1389 info = (GtkIncrInfo *)tmp_list->data;
1390 if (info->requestor == event->window)
1393 tmp_list = tmp_list->next;
1396 if (tmp_list == NULL)
1399 /* Find out which target this is for */
1400 for (i=0; i<info->num_conversions; i++)
1402 if (info->conversions[i].property == event->atom &&
1403 info->conversions[i].offset != -1)
1407 info->idle_time = 0;
1409 if (info->conversions[i].offset == -2) /* only the last 0-length
1417 num_bytes = info->conversions[i].data.length -
1418 info->conversions[i].offset;
1419 buffer = info->conversions[i].data.data +
1420 info->conversions[i].offset;
1422 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1424 num_bytes = GTK_SELECTION_MAX_SIZE;
1425 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1428 info->conversions[i].offset = -2;
1430 #ifdef DEBUG_SELECTION
1431 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1432 num_bytes, info->conversions[i].offset,
1433 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1436 bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1437 gdk_property_change (info->requestor, event->atom,
1438 info->conversions[i].data.type,
1439 info->conversions[i].data.format,
1440 GDK_PROP_MODE_REPLACE,
1442 num_bytes / bytes_per_item);
1444 if (info->conversions[i].offset == -2)
1446 g_free (info->conversions[i].data.data);
1447 info->conversions[i].data.data = NULL;
1453 info->conversions[i].offset = -1;
1459 /* Check if we're finished with all the targets */
1461 if (info->num_incrs == 0)
1463 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1464 g_list_free (tmp_list);
1465 /* Let the timeout free it */
1471 /*************************************************************
1472 * gtk_selection_incr_timeout:
1473 * Timeout callback for the sending portion of the INCR
1476 * info: Information about this incr
1478 *************************************************************/
1481 gtk_selection_incr_timeout (GtkIncrInfo *info)
1486 GDK_THREADS_ENTER ();
1488 /* Determine if retrieval has finished by checking if it still in
1489 list of pending retrievals */
1491 tmp_list = current_incrs;
1494 if (info == (GtkIncrInfo *)tmp_list->data)
1496 tmp_list = tmp_list->next;
1499 /* If retrieval is finished */
1500 if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1502 if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1504 current_incrs = g_list_remove_link (current_incrs, tmp_list);
1505 g_list_free (tmp_list);
1508 g_free (info->conversions);
1509 /* FIXME: we should check if requestor window is still in use,
1510 and if not, remove it? */
1514 retval = FALSE; /* remove timeout */
1520 retval = TRUE; /* timeout will happen again */
1523 GDK_THREADS_LEAVE ();
1528 /*************************************************************
1529 * _gtk_selection_notify:
1530 * Handler for "selection_notify_event" signals on windows
1531 * where a retrieval is currently in process. The selection
1532 * owner has responded to our conversion request.
1534 * widget: Widget getting signal
1535 * event: Selection event structure
1536 * info: Information about this retrieval
1538 * was event handled?
1539 *************************************************************/
1542 _gtk_selection_notify (GtkWidget *widget,
1543 GdkEventSelection *event)
1546 GtkRetrievalInfo *info = NULL;
1547 guchar *buffer = NULL;
1552 #ifdef DEBUG_SELECTION
1553 g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1554 event->selection, event->target, event->property);
1557 tmp_list = current_retrievals;
1560 info = (GtkRetrievalInfo *)tmp_list->data;
1561 if (info->widget == widget && info->selection == event->selection)
1563 tmp_list = tmp_list->next;
1566 if (!tmp_list) /* no retrieval in progress */
1569 if (event->property != GDK_NONE)
1570 length = gdk_selection_property_get (widget->window, &buffer,
1573 length = 0; /* silence gcc */
1575 if (event->property == GDK_NONE || buffer == NULL)
1577 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1578 g_list_free (tmp_list);
1579 /* structure will be freed in timeout */
1580 gtk_selection_retrieval_report (info,
1581 GDK_NONE, 0, NULL, -1, event->time);
1586 if (type == gtk_selection_atoms[INCR])
1588 /* The remainder of the selection will come through PropertyNotify
1591 info->notify_time = event->time;
1592 info->idle_time = 0;
1593 info->offset = 0; /* Mark as OK to proceed */
1594 gdk_window_set_events (widget->window,
1595 gdk_window_get_events (widget->window)
1596 | GDK_PROPERTY_CHANGE_MASK);
1600 /* We don't delete the info structure - that will happen in timeout */
1601 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1602 g_list_free (tmp_list);
1604 info->offset = length;
1605 gtk_selection_retrieval_report (info,
1607 buffer, length, event->time);
1610 gdk_property_delete (widget->window, event->property);
1617 /*************************************************************
1618 * _gtk_selection_property_notify:
1619 * Handler for "property_notify_event" signals on windows
1620 * where a retrieval is currently in process. The selection
1621 * owner has added more data.
1623 * widget: Widget getting signal
1624 * event: Property event structure
1625 * info: Information about this retrieval
1627 * was event handled?
1628 *************************************************************/
1631 _gtk_selection_property_notify (GtkWidget *widget,
1632 GdkEventProperty *event)
1635 GtkRetrievalInfo *info = NULL;
1641 g_return_val_if_fail (widget != NULL, FALSE);
1642 g_return_val_if_fail (event != NULL, FALSE);
1644 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1645 if ((event->state != GDK_PROPERTY_NEW_VALUE) || /* property was deleted */
1646 (event->atom != gdk_atom_intern ("GDK_SELECTION", FALSE))) /* not the right property */
1650 #ifdef DEBUG_SELECTION
1651 g_message ("PropertyNewValue, property %ld",
1655 tmp_list = current_retrievals;
1658 info = (GtkRetrievalInfo *)tmp_list->data;
1659 if (info->widget == widget)
1661 tmp_list = tmp_list->next;
1664 if (!tmp_list) /* No retrieval in progress */
1667 if (info->offset < 0) /* We haven't got the SelectionNotify
1668 for this retrieval yet */
1671 info->idle_time = 0;
1673 length = gdk_selection_property_get (widget->window, &new_buffer,
1675 gdk_property_delete (widget->window, event->atom);
1677 /* We could do a lot better efficiency-wise by paying attention to
1678 what length was sent in the initial INCR transaction, instead of
1679 doing memory allocation at every step. But its only guaranteed to
1680 be a _lower bound_ (pretty useless!) */
1682 if (length == 0 || type == GDK_NONE) /* final zero length portion */
1684 /* Info structure will be freed in timeout */
1685 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1686 g_list_free (tmp_list);
1687 gtk_selection_retrieval_report (info,
1689 (type == GDK_NONE) ? NULL : info->buffer,
1690 (type == GDK_NONE) ? -1 : info->offset,
1693 else /* append on newly arrived data */
1697 #ifdef DEBUG_SELECTION
1698 g_message ("Start - Adding %d bytes at offset 0",
1701 info->buffer = new_buffer;
1702 info->offset = length;
1707 #ifdef DEBUG_SELECTION
1708 g_message ("Appending %d bytes at offset %d",
1709 length,info->offset);
1711 /* We copy length+1 bytes to preserve guaranteed null termination */
1712 info->buffer = g_realloc (info->buffer, info->offset+length+1);
1713 memcpy (info->buffer + info->offset, new_buffer, length+1);
1714 info->offset += length;
1715 g_free (new_buffer);
1722 /*************************************************************
1723 * gtk_selection_retrieval_timeout:
1724 * Timeout callback while receiving a selection.
1726 * info: Information about this retrieval
1728 *************************************************************/
1731 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1736 GDK_THREADS_ENTER ();
1738 /* Determine if retrieval has finished by checking if it still in
1739 list of pending retrievals */
1741 tmp_list = current_retrievals;
1744 if (info == (GtkRetrievalInfo *)tmp_list->data)
1746 tmp_list = tmp_list->next;
1749 /* If retrieval is finished */
1750 if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1752 if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1754 current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1755 g_list_free (tmp_list);
1756 gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1759 g_free (info->buffer);
1762 retval = FALSE; /* remove timeout */
1768 retval = TRUE; /* timeout will happen again */
1771 GDK_THREADS_LEAVE ();
1776 /*************************************************************
1777 * gtk_selection_retrieval_report:
1778 * Emits a "selection_received" signal.
1780 * info: information about the retrieval that completed
1781 * buffer: buffer containing data (NULL => errror)
1782 * time: timestamp for data in buffer
1784 *************************************************************/
1787 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1788 GdkAtom type, gint format,
1789 guchar *buffer, gint length,
1792 GtkSelectionData data;
1794 data.selection = info->selection;
1795 data.target = info->target;
1797 data.format = format;
1799 data.length = length;
1801 data.display = gtk_widget_get_display (info->widget);
1803 g_signal_emit_by_name (info->widget,
1804 "selection_received",
1808 /*************************************************************
1809 * gtk_selection_invoke_handler:
1810 * Finds and invokes handler for specified
1811 * widget/selection/target combination, calls
1812 * gtk_selection_default_handler if none exists.
1815 * widget: selection owner
1816 * data: selection data [INOUT]
1817 * time: time from requeset
1820 * Number of bytes written to buffer, -1 if error
1821 *************************************************************/
1824 gtk_selection_invoke_handler (GtkWidget *widget,
1825 GtkSelectionData *data,
1828 GtkTargetList *target_list;
1832 g_return_if_fail (widget != NULL);
1834 target_list = gtk_selection_target_list_get (widget, data->selection);
1836 gtk_target_list_find (target_list, data->target, &info))
1838 g_signal_emit_by_name (widget,
1844 gtk_selection_default_handler (widget, data);
1847 /*************************************************************
1848 * gtk_selection_default_handler:
1849 * Handles some default targets that exist for any widget
1850 * If it can't fit results into buffer, returns -1. This
1851 * won't happen in any conceivable case, since it would
1852 * require 1000 selection targets!
1855 * widget: selection owner
1856 * data: selection data [INOUT]
1858 *************************************************************/
1861 gtk_selection_default_handler (GtkWidget *widget,
1862 GtkSelectionData *data)
1864 if (data->target == gtk_selection_atoms[TIMESTAMP])
1866 /* Time which was used to obtain selection */
1868 GtkSelectionInfo *selection_info;
1870 tmp_list = current_selections;
1873 selection_info = (GtkSelectionInfo *)tmp_list->data;
1874 if ((selection_info->widget == widget) &&
1875 (selection_info->selection == data->selection))
1877 gulong time = selection_info->time;
1879 gtk_selection_data_set (data,
1880 GDK_SELECTION_TYPE_INTEGER,
1887 tmp_list = tmp_list->next;
1892 else if (data->target == gtk_selection_atoms[TARGETS])
1894 /* List of all targets supported for this widget/selection pair */
1898 GtkTargetList *target_list;
1899 GtkTargetPair *pair;
1901 target_list = gtk_selection_target_list_get (widget,
1903 count = g_list_length (target_list->list) + 3;
1905 data->type = GDK_SELECTION_TYPE_ATOM;
1907 data->length = count * sizeof (GdkAtom);
1909 /* selection data is always terminated by a trailing \0
1911 p = g_malloc (data->length + 1);
1912 data->data = (guchar *)p;
1913 data->data[data->length] = '\0';
1915 *p++ = gtk_selection_atoms[TIMESTAMP];
1916 *p++ = gtk_selection_atoms[TARGETS];
1917 *p++ = gtk_selection_atoms[MULTIPLE];
1919 tmp_list = target_list->list;
1922 pair = (GtkTargetPair *)tmp_list->data;
1923 *p++ = pair->target;
1925 tmp_list = tmp_list->next;
1936 gtk_selection_data_copy (GtkSelectionData *selection_data)
1938 GtkSelectionData *new_data;
1940 g_return_val_if_fail (selection_data != NULL, NULL);
1942 new_data = g_new (GtkSelectionData, 1);
1943 *new_data = *selection_data;
1945 if (selection_data->data)
1947 new_data->data = g_malloc (selection_data->length + 1);
1948 memcpy (new_data->data, selection_data->data, selection_data->length + 1);
1955 gtk_selection_data_free (GtkSelectionData *data)
1957 g_return_if_fail (data != NULL);
1960 g_free (data->data);
1966 gtk_selection_data_get_type (void)
1968 static GType our_type = 0;
1971 our_type = g_boxed_type_register_static ("GtkSelectionData",
1972 (GBoxedCopyFunc) gtk_selection_data_copy,
1973 (GBoxedFreeFunc) gtk_selection_data_free);
1979 gtk_selection_bytes_per_item (gint format)
1984 return sizeof (char);
1987 return sizeof (short);
1990 return sizeof (long);
1993 g_assert_not_reached();