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 ICCCM 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 ICCCM 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/.
60 #include "gtkselection.h"
62 #ifdef GDK_WINDOWING_X11
66 /* #define DEBUG_SELECTION */
68 /* Maximum size of a sent chunk, in bytes. Also the default size of
70 #ifdef GDK_WINDOWING_WIN32
71 /* No chunks on Win32 */
72 #define GTK_SELECTION_MAX_SIZE G_MAXINT
74 #define GTK_SELECTION_MAX_SIZE 4000
77 #define IDLE_ABORT_TIME 300
87 typedef struct _GtkSelectionInfo GtkSelectionInfo;
88 typedef struct _GtkIncrConversion GtkIncrConversion;
89 typedef struct _GtkIncrInfo GtkIncrInfo;
90 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
92 struct _GtkSelectionInfo
95 GtkWidget *widget; /* widget that owns selection */
96 guint32 time; /* time used to acquire selection */
97 GdkDisplay *display; /* needed in gtk_selection_remove_all */
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 GdkWindow *requestor; /* Requestor window - we create a GdkWindow
114 so we can receive events */
115 GdkAtom selection; /* Selection we're sending */
117 GtkIncrConversion *conversions; /* Information about requested conversions -
118 * With MULTIPLE requests (benighted 1980's
119 * hardware idea), there can be more than
121 gint num_conversions;
122 gint num_incrs; /* number of remaining INCR style transactions */
127 struct _GtkRetrievalInfo
130 GdkAtom selection; /* Selection being retrieved. */
131 GdkAtom target; /* Form of selection that we requested */
132 guint32 idle_time; /* Number of seconds since we last heard
133 from selection owner */
134 guchar *buffer; /* Buffer in which to accumulate results */
135 gint offset; /* Current offset in buffer, -1 indicates
137 guint32 notify_time; /* Timestamp from SelectionNotify */
140 /* Local Functions */
141 static void gtk_selection_init (void);
142 static gint gtk_selection_incr_timeout (GtkIncrInfo *info);
143 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
144 static void gtk_selection_retrieval_report (GtkRetrievalInfo *info,
150 static void gtk_selection_invoke_handler (GtkWidget *widget,
151 GtkSelectionData *data,
153 static void gtk_selection_default_handler (GtkWidget *widget,
154 GtkSelectionData *data);
155 static int gtk_selection_bytes_per_item (gint format);
158 static gint initialize = TRUE;
159 static GList *current_retrievals = NULL;
160 static GList *current_incrs = NULL;
161 static GList *current_selections = NULL;
163 static GdkAtom gtk_selection_atoms[LAST_ATOM];
164 static const char gtk_selection_handler_key[] = "gtk-selection-handlers";
175 gtk_target_list_new (const GtkTargetEntry *targets,
178 GtkTargetList *result = g_new (GtkTargetList, 1);
180 result->ref_count = 1;
183 gtk_target_list_add_table (result, targets, ntargets);
189 gtk_target_list_ref (GtkTargetList *list)
191 g_return_if_fail (list != NULL);
197 gtk_target_list_unref (GtkTargetList *list)
199 g_return_if_fail (list != NULL);
200 g_return_if_fail (list->ref_count > 0);
203 if (list->ref_count == 0)
205 GList *tmp_list = list->list;
208 GtkTargetPair *pair = tmp_list->data;
211 tmp_list = tmp_list->next;
214 g_list_free (list->list);
220 gtk_target_list_add (GtkTargetList *list,
227 g_return_if_fail (list != NULL);
229 pair = g_new (GtkTargetPair, 1);
230 pair->target = target;
234 list->list = g_list_append (list->list, pair);
238 gtk_target_list_add_table (GtkTargetList *list,
239 const GtkTargetEntry *targets,
244 for (i=ntargets-1; i >= 0; i--)
246 GtkTargetPair *pair = g_new (GtkTargetPair, 1);
247 pair->target = gdk_atom_intern (targets[i].target, FALSE);
248 pair->flags = targets[i].flags;
249 pair->info = targets[i].info;
251 list->list = g_list_prepend (list->list, pair);
256 gtk_target_list_remove (GtkTargetList *list,
261 g_return_if_fail (list != NULL);
263 tmp_list = list->list;
266 GtkTargetPair *pair = tmp_list->data;
268 if (pair->target == target)
272 list->list = g_list_remove_link (list->list, tmp_list);
273 g_list_free_1 (tmp_list);
278 tmp_list = tmp_list->next;
283 gtk_target_list_find (GtkTargetList *list,
287 GList *tmp_list = list->list;
290 GtkTargetPair *pair = tmp_list->data;
292 if (pair->target == target)
297 tmp_list = tmp_list->next;
304 * gtk_selection_owner_set_for_display:
305 * @display: the #Gdkdisplay where the selection is set
306 * @widget: new selection owner (a #GdkWidget), or %NULL.
307 * @selection: an interned atom representing the selection to claim.
308 * @time_: timestamp with which to claim the selection
310 * Claim ownership of a given selection for a particular widget, or,
311 * if @widget is %NULL, release ownership of the selection.
313 * Return value: TRUE if the operation succeeded
318 gtk_selection_owner_set_for_display (GdkDisplay *display,
324 GtkWidget *old_owner;
325 GtkSelectionInfo *selection_info = NULL;
328 g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
329 g_return_val_if_fail (selection != GDK_NONE, FALSE);
330 g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
331 g_return_val_if_fail (widget == NULL || gtk_widget_get_display (widget) == display, FALSE);
336 window = widget->window;
338 tmp_list = current_selections;
341 if (((GtkSelectionInfo *)tmp_list->data)->selection == selection)
343 selection_info = tmp_list->data;
347 tmp_list = tmp_list->next;
350 if (gdk_selection_owner_set_for_display (display, window, selection, time, TRUE))
358 old_owner = selection_info->widget;
359 current_selections = g_list_remove_link (current_selections,
361 g_list_free (tmp_list);
362 g_free (selection_info);
367 if (selection_info == NULL)
369 selection_info = g_new (GtkSelectionInfo, 1);
370 selection_info->selection = selection;
371 selection_info->widget = widget;
372 selection_info->time = time;
373 selection_info->display = display;
374 current_selections = g_list_prepend (current_selections,
379 old_owner = selection_info->widget;
380 selection_info->widget = widget;
381 selection_info->time = time;
382 selection_info->display = display;
385 /* If another widget in the application lost the selection,
386 * send it a GDK_SELECTION_CLEAR event.
388 if (old_owner && old_owner != widget)
390 GdkEvent *event = gdk_event_new (GDK_SELECTION_CLEAR);
392 event->selection.window = g_object_ref (old_owner->window);
393 event->selection.selection = selection;
394 event->selection.time = time;
396 gtk_widget_event (old_owner, event);
398 gdk_event_free (event);
407 * gtk_selection_owner_set:
408 * @widget: a #GtkWidget, or %NULL.
409 * @selection: an interned atom representing the selection to claim
410 * @time_: timestamp with which to claim the selection
412 * Claims ownership of a given selection for a particular widget,
413 * or, if @widget is %NULL, release ownership of the selection.
415 * Return value: %TRUE if the operation succeeded
418 gtk_selection_owner_set (GtkWidget *widget,
424 g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
425 g_return_val_if_fail (selection != GDK_NONE, FALSE);
428 display = gtk_widget_get_display (widget);
432 g_warning ("gtk_selection_owner_set (NULL,...) is not multihead safe"));
434 display = gdk_display_get_default ();
437 return gtk_selection_owner_set_for_display (display, widget,
441 /*************************************************************
442 * gtk_selection_add_target
443 * Add specified target to list of supported targets
446 * widget: The widget for which this target applies
449 * info: guint to pass to to the selection_get signal
452 *************************************************************/
454 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
456 struct _GtkSelectionTargetList {
461 static GtkTargetList *
462 gtk_selection_target_list_get (GtkWidget *widget,
465 GtkSelectionTargetList *sellist;
469 lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
474 sellist = tmp_list->data;
475 if (sellist->selection == selection)
476 return sellist->list;
477 tmp_list = tmp_list->next;
480 sellist = g_new (GtkSelectionTargetList, 1);
481 sellist->selection = selection;
482 sellist->list = gtk_target_list_new (NULL, 0);
484 lists = g_list_prepend (lists, sellist);
485 g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
487 return sellist->list;
491 gtk_selection_target_list_remove (GtkWidget *widget)
493 GtkSelectionTargetList *sellist;
497 lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
502 sellist = tmp_list->data;
504 gtk_target_list_unref (sellist->list);
507 tmp_list = tmp_list->next;
511 g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, NULL);
515 * gtk_selection_clear_targets:
516 * @widget: a #GtkWidget
517 * @selection: an atom representing a selection
519 * Remove all targets registered for the given selection for the
523 gtk_selection_clear_targets (GtkWidget *widget,
526 GtkSelectionTargetList *sellist;
530 g_return_if_fail (GTK_IS_WIDGET (widget));
531 g_return_if_fail (selection != GDK_NONE);
533 lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
538 sellist = tmp_list->data;
539 if (sellist->selection == selection)
541 lists = g_list_delete_link (lists, tmp_list);
542 gtk_target_list_unref (sellist->list);
548 tmp_list = tmp_list->next;
551 g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
555 gtk_selection_add_target (GtkWidget *widget,
562 g_return_if_fail (GTK_IS_WIDGET (widget));
563 g_return_if_fail (selection != GDK_NONE);
565 list = gtk_selection_target_list_get (widget, selection);
566 gtk_target_list_add (list, target, 0, info);
570 gtk_selection_add_targets (GtkWidget *widget,
572 const GtkTargetEntry *targets,
577 g_return_if_fail (GTK_IS_WIDGET (widget));
578 g_return_if_fail (selection != GDK_NONE);
579 g_return_if_fail (targets != NULL);
581 list = gtk_selection_target_list_get (widget, selection);
582 gtk_target_list_add_table (list, targets, ntargets);
586 /*************************************************************
587 * gtk_selection_remove_all:
588 * Removes all handlers and unsets ownership of all
589 * selections for a widget. Called when widget is being
595 *************************************************************/
598 gtk_selection_remove_all (GtkWidget *widget)
602 GtkSelectionInfo *selection_info;
604 /* Remove pending requests/incrs for this widget */
606 tmp_list = current_retrievals;
609 next = tmp_list->next;
610 if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
612 current_retrievals = g_list_remove_link (current_retrievals,
614 /* structure will be freed in timeout */
615 g_list_free (tmp_list);
620 /* Disclaim ownership of any selections */
622 tmp_list = current_selections;
625 next = tmp_list->next;
626 selection_info = (GtkSelectionInfo *)tmp_list->data;
628 if (selection_info->widget == widget)
630 gdk_selection_owner_set_for_display (selection_info->display,
632 selection_info->selection,
633 GDK_CURRENT_TIME, FALSE);
634 current_selections = g_list_remove_link (current_selections,
636 g_list_free (tmp_list);
637 g_free (selection_info);
643 /* Remove all selection lists */
644 gtk_selection_target_list_remove (widget);
647 /*************************************************************
648 * gtk_selection_convert:
649 * Request the contents of a selection. When received,
650 * a "selection_received" signal will be generated.
653 * widget: The widget which acts as requestor
654 * selection: Which selection to get
655 * target: Form of information desired (e.g., STRING)
656 * time: Time of request (usually of triggering event)
657 * In emergency, you could use GDK_CURRENT_TIME
660 * TRUE if requested succeeded. FALSE if we could not process
661 * request. (e.g., there was already a request in process for
663 *************************************************************/
666 gtk_selection_convert (GtkWidget *widget,
671 GtkRetrievalInfo *info;
673 GdkWindow *owner_window;
676 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
677 g_return_val_if_fail (selection != GDK_NONE, FALSE);
680 gtk_selection_init ();
682 if (!GTK_WIDGET_REALIZED (widget))
683 gtk_widget_realize (widget);
685 /* Check to see if there are already any retrievals in progress for
686 this widget. If we changed GDK to use the selection for the
687 window property in which to store the retrieved information, then
688 we could support multiple retrievals for different selections.
689 This might be useful for DND. */
691 tmp_list = current_retrievals;
694 info = (GtkRetrievalInfo *)tmp_list->data;
695 if (info->widget == widget)
697 tmp_list = tmp_list->next;
700 info = g_new (GtkRetrievalInfo, 1);
702 info->widget = widget;
703 info->selection = selection;
704 info->target = target;
709 /* Check if this process has current owner. If so, call handler
710 procedure directly to avoid deadlocks with INCR. */
712 display = gtk_widget_get_display (widget);
713 owner_window = gdk_selection_owner_get_for_display (display, selection);
715 if (owner_window != NULL)
717 GtkWidget *owner_widget;
718 GtkSelectionData selection_data;
720 selection_data.selection = selection;
721 selection_data.target = target;
722 selection_data.data = NULL;
723 selection_data.length = -1;
724 selection_data.display = display;
726 gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
728 if (owner_widget != NULL)
730 gtk_selection_invoke_handler (owner_widget,
734 gtk_selection_retrieval_report (info,
736 selection_data.format,
738 selection_data.length,
741 g_free (selection_data.data);
748 /* Otherwise, we need to go through X */
750 current_retrievals = g_list_append (current_retrievals, info);
751 gdk_selection_convert (widget->window, selection, target, time);
752 g_timeout_add (1000, (GSourceFunc) gtk_selection_retrieval_timeout, info);
757 /*************************************************************
758 * gtk_selection_data_set:
759 * Store new data into a GtkSelectionData object. Should
760 * _only_ by called from a selection handler callback.
761 * Null terminates the stored data.
763 * type: the type of selection data
764 * format: format (number of bits in a unit)
765 * data: pointer to the data (will be copied)
766 * length: length of the data
768 *************************************************************/
771 gtk_selection_data_set (GtkSelectionData *selection_data,
777 if (selection_data->data)
778 g_free (selection_data->data);
780 selection_data->type = type;
781 selection_data->format = format;
785 selection_data->data = g_new (guchar, length+1);
786 memcpy (selection_data->data, data, length);
787 selection_data->data[length] = 0;
791 g_return_if_fail (length <= 0);
794 selection_data->data = NULL;
796 selection_data->data = g_strdup("");
799 selection_data->length = length;
802 static GdkAtom utf8_atom;
803 static GdkAtom text_atom;
804 static GdkAtom ctext_atom;
811 utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
812 text_atom = gdk_atom_intern ("TEXT", FALSE);
813 ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
818 selection_set_string (GtkSelectionData *selection_data,
822 gchar *tmp = g_strndup (str, len);
823 gchar *latin1 = gdk_utf8_to_string_target (tmp);
828 gtk_selection_data_set (selection_data,
829 GDK_SELECTION_TYPE_STRING,
830 8, latin1, strlen (latin1));
840 selection_set_compound_text (GtkSelectionData *selection_data,
849 gboolean result = FALSE;
851 tmp = g_strndup (str, len);
852 if (gdk_utf8_to_compound_text_for_display (selection_data->display, tmp,
853 &encoding, &format, &text, &new_length))
855 gtk_selection_data_set (selection_data, encoding, format, text, new_length);
856 gdk_free_compound_text (text);
867 * gtk_selection_data_set_text:
868 * @selection_data: a #GtkSelectionData
869 * @str: a UTF-8 string
870 * @len: the length of @str, or -1 if @str is nul-terminated.
872 * Sets the contents of the selection from a UTF-8 encoded string.
873 * The string is converted to the form determined by
874 * @selection_data->target.
876 * Return value: %TRUE if the selection was successfully set,
880 gtk_selection_data_set_text (GtkSelectionData *selection_data,
889 if (selection_data->target == utf8_atom)
891 gtk_selection_data_set (selection_data,
893 8, (guchar *)str, len);
896 else if (selection_data->target == GDK_TARGET_STRING)
898 return selection_set_string (selection_data, str, len);
900 else if (selection_data->target == ctext_atom ||
901 selection_data->target == text_atom)
903 if (selection_set_compound_text (selection_data, str, len))
905 else if (selection_data->target == text_atom)
906 return selection_set_string (selection_data, str, len);
913 * gtk_selection_data_get_text:
914 * @selection_data: a #GtkSelectionData
916 * Gets the contents of the selection data as a UTF-8 string.
918 * Return value: if the selection data contained a recognized
919 * text type and it could be converted to UTF-8, a newly allocated
920 * string containing the converted text, otherwise %NULL.
921 * If the result is non-%NULL it must be freed with g_free().
924 gtk_selection_data_get_text (GtkSelectionData *selection_data)
926 guchar *result = NULL;
930 if (selection_data->length >= 0 &&
931 (selection_data->type == GDK_TARGET_STRING ||
932 selection_data->type == ctext_atom ||
933 selection_data->type == utf8_atom))
937 gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
938 selection_data->type,
939 selection_data->format,
940 selection_data->data,
941 selection_data->length,
946 for (i = 1; i < count; i++)
955 * gtk_selection_data_get_targets:
956 * @selection_data: a #GtkSelectionData object
957 * @targets: location to store an array of targets. The result
958 * stored here must be freed with g_free().
959 * @n_atoms: location to store number of items in @targets.
961 * Gets the contents of @selection_data as an array of targets.
962 * This can be used to interpret the results of getting
963 * the standard TARGETS target that is always supplied for
966 * Return value: %TRUE if @selection_data contains a valid
967 * array of targets, otherwise %FALSE.
970 gtk_selection_data_get_targets (GtkSelectionData *selection_data,
974 if (selection_data->length >= 0 &&
975 selection_data->format == 32 &&
976 selection_data->type == GDK_SELECTION_TYPE_ATOM)
979 *targets = g_memdup (selection_data->data, selection_data->length);
981 *n_atoms = selection_data->length / sizeof (GdkAtom);
997 * gtk_selection_data_targets_include_text:
998 * @selection_data: a #GtkSelectionData object
1000 * Given a #GtkSelectionData object holding a list of targets,
1001 * determines if any of the targets in @targets can be used to
1004 * Return value: %TRUE if @selection_data holds a list of targets,
1005 * and a suitable target for text is included, otherwise %FALSE.
1008 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
1013 gboolean result = FALSE;
1015 if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
1017 for (i=0; i < n_targets; i++)
1019 if (targets[i] == gdk_atom_intern ("STRING", FALSE) ||
1020 targets[i] == gdk_atom_intern ("TEXT", FALSE) ||
1021 targets[i] == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
1022 targets[i] == gdk_atom_intern ("UTF8_STRING", FALSE))
1032 /*************************************************************
1033 * gtk_selection_init:
1034 * Initialize local variables
1038 *************************************************************/
1041 gtk_selection_init (void)
1043 gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
1044 gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
1045 gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
1046 gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
1052 * gtk_selection_clear:
1053 * @widget: a #GtkWidget
1056 * The default handler for the GtkWidget::selection_clear_event
1059 * Return value: %TRUE if the event was handled, otherwise false
1063 * Deprecated: Instead of calling this function, chain up from
1064 * your selection_clear_event handler. Calling this function
1065 * from any other context is illegal.
1068 gtk_selection_clear (GtkWidget *widget,
1069 GdkEventSelection *event)
1071 /* Note that we filter clear events in gdkselection-x11.c, so
1072 * that we only will get here if the clear event actually
1073 * represents a change that we didn't do ourself.
1076 GtkSelectionInfo *selection_info = NULL;
1078 tmp_list = current_selections;
1081 selection_info = (GtkSelectionInfo *)tmp_list->data;
1083 if ((selection_info->selection == event->selection) &&
1084 (selection_info->widget == widget))
1087 tmp_list = tmp_list->next;
1092 current_selections = g_list_remove_link (current_selections, tmp_list);
1093 g_list_free (tmp_list);
1094 g_free (selection_info);
1101 /*************************************************************
1102 * _gtk_selection_request:
1103 * Handler for "selection_request_event"
1108 *************************************************************/
1111 _gtk_selection_request (GtkWidget *widget,
1112 GdkEventSelection *event)
1114 GdkDisplay *display = gtk_widget_get_display (widget);
1120 gtk_selection_init ();
1122 /* Check if we own selection */
1124 tmp_list = current_selections;
1127 GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1129 if ((selection_info->selection == event->selection) &&
1130 (selection_info->widget == widget))
1133 tmp_list = tmp_list->next;
1136 if (tmp_list == NULL)
1139 info = g_new (GtkIncrInfo, 1);
1141 g_object_ref (widget);
1143 info->selection = event->selection;
1144 info->num_incrs = 0;
1146 /* Create GdkWindow structure for the requestor */
1148 info->requestor = gdk_window_lookup_for_display (display,
1150 if (!info->requestor)
1151 info->requestor = gdk_window_foreign_new_for_display (display,
1154 /* Determine conversions we need to perform */
1156 if (event->target == gtk_selection_atoms[MULTIPLE])
1165 gdk_error_trap_push ();
1166 if (!gdk_property_get (info->requestor, event->property, GDK_NONE, /* AnyPropertyType */
1167 0, GTK_SELECTION_MAX_SIZE, FALSE,
1168 &type, &format, &length, &mult_atoms))
1170 gdk_selection_send_notify_for_display (display,
1176 g_free (mult_atoms);
1180 gdk_error_trap_pop ();
1182 /* This is annoying; the ICCCM doesn't specify the property type
1183 * used for the property contents, so the autoconversion for
1184 * ATOM / ATOM_PAIR in GDK doesn't work properly.
1186 #ifdef GDK_WINDOWING_X11
1187 if (type != GDK_SELECTION_TYPE_ATOM &&
1188 type != gdk_atom_intern ("ATOM_PAIR", FALSE))
1190 info->num_conversions = length / (2*sizeof (glong));
1191 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1193 for (i=0; i<info->num_conversions; i++)
1195 info->conversions[i].target = gdk_x11_xatom_to_atom_for_display (display,
1196 ((glong *)mult_atoms)[2*i]);
1197 info->conversions[i].property = gdk_x11_xatom_to_atom_for_display (display,
1198 ((glong *)mult_atoms)[2*i + 1]);
1204 info->num_conversions = length / (2*sizeof (GdkAtom));
1205 info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1207 for (i=0; i<info->num_conversions; i++)
1209 info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1210 info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1214 else /* only a single conversion */
1216 info->conversions = g_new (GtkIncrConversion, 1);
1217 info->num_conversions = 1;
1218 info->conversions[0].target = event->target;
1219 info->conversions[0].property = event->property;
1222 /* Loop through conversions and determine which of these are big
1223 enough to require doing them via INCR */
1224 for (i=0; i<info->num_conversions; i++)
1226 GtkSelectionData data;
1229 data.selection = event->selection;
1230 data.target = info->conversions[i].target;
1233 data.display = gtk_widget_get_display (widget);
1235 #ifdef DEBUG_SELECTION
1236 g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1237 event->selection, info->conversions[i].target,
1238 gdk_atom_name (info->conversions[i].target),
1239 event->requestor, info->conversions[i].property);
1242 gtk_selection_invoke_handler (widget, &data, event->time);
1244 if (data.length < 0)
1246 info->conversions[i].property = GDK_NONE;
1250 g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1252 items = data.length / gtk_selection_bytes_per_item (data.format);
1254 if (data.length > GTK_SELECTION_MAX_SIZE)
1256 /* Sending via INCR */
1258 info->conversions[i].offset = 0;
1259 info->conversions[i].data = data;
1262 gdk_property_change (info->requestor,
1263 info->conversions[i].property,
1264 gtk_selection_atoms[INCR],
1266 GDK_PROP_MODE_REPLACE,
1267 (guchar *)&items, 1);
1271 info->conversions[i].offset = -1;
1273 gdk_property_change (info->requestor,
1274 info->conversions[i].property,
1277 GDK_PROP_MODE_REPLACE,
1284 /* If we have some INCR's, we need to send the rest of the data in
1287 if (info->num_incrs > 0)
1289 /* FIXME: this could be dangerous if window doesn't still
1292 #ifdef DEBUG_SELECTION
1293 g_message ("Starting INCR...");
1296 gdk_window_set_events (info->requestor,
1297 gdk_window_get_events (info->requestor) |
1298 GDK_PROPERTY_CHANGE_MASK);
1299 current_incrs = g_list_append (current_incrs, info);
1300 g_timeout_add (1000, (GSourceFunc) gtk_selection_incr_timeout, info);
1303 /* If it was a MULTIPLE request, set the property to indicate which
1304 conversions succeeded */
1305 if (event->target == gtk_selection_atoms[MULTIPLE])
1307 GdkAtom *mult_atoms = g_new (GdkAtom, 2 * info->num_conversions);
1308 for (i = 0; i < info->num_conversions; i++)
1310 mult_atoms[2*i] = info->conversions[i].target;
1311 mult_atoms[2*i+1] = info->conversions[i].property;
1314 gdk_property_change (info->requestor, event->property,
1315 gdk_atom_intern ("ATOM_PAIR", FALSE), 32,
1316 GDK_PROP_MODE_REPLACE,
1317 (guchar *)mult_atoms, 2*info->num_conversions);
1318 g_free (mult_atoms);
1321 if (info->num_conversions == 1 &&
1322 info->conversions[0].property == GDK_NONE)
1324 /* Reject the entire conversion */
1325 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1334 gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1342 if (info->num_incrs == 0)
1344 g_free (info->conversions);
1348 g_object_unref (widget);
1353 /*************************************************************
1354 * _gtk_selection_incr_event:
1355 * Called whenever an PropertyNotify event occurs for an
1356 * GdkWindow with user_data == NULL. These will be notifications
1357 * that a window we are sending the selection to via the
1358 * INCR protocol has deleted a property and is ready for
1362 * window: the requestor window
1363 * event: the property event structure
1366 *************************************************************/
1369 _gtk_selection_incr_event (GdkWindow *window,
1370 GdkEventProperty *event)
1373 GtkIncrInfo *info = NULL;
1379 if (event->state != GDK_PROPERTY_DELETE)
1382 #ifdef DEBUG_SELECTION
1383 g_message ("PropertyDelete, property %ld", event->atom);
1386 /* Now find the appropriate ongoing INCR */
1387 tmp_list = current_incrs;
1390 info = (GtkIncrInfo *)tmp_list->data;
1391 if (info->requestor == event->window)
1394 tmp_list = tmp_list->next;
1397 if (tmp_list == NULL)
1400 /* Find out which target this is for */
1401 for (i=0; i<info->num_conversions; i++)
1403 if (info->conversions[i].property == event->atom &&
1404 info->conversions[i].offset != -1)
1408 info->idle_time = 0;
1410 if (info->conversions[i].offset == -2) /* only the last 0-length
1418 num_bytes = info->conversions[i].data.length -
1419 info->conversions[i].offset;
1420 buffer = info->conversions[i].data.data +
1421 info->conversions[i].offset;
1423 if (num_bytes > GTK_SELECTION_MAX_SIZE)
1425 num_bytes = GTK_SELECTION_MAX_SIZE;
1426 info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1429 info->conversions[i].offset = -2;
1431 #ifdef DEBUG_SELECTION
1432 g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1433 num_bytes, info->conversions[i].offset,
1434 GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1437 bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1438 gdk_property_change (info->requestor, event->atom,
1439 info->conversions[i].data.type,
1440 info->conversions[i].data.format,
1441 GDK_PROP_MODE_REPLACE,
1443 num_bytes / bytes_per_item);
1445 if (info->conversions[i].offset == -2)
1447 g_free (info->conversions[i].data.data);
1448 info->conversions[i].data.data = NULL;
1454 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();