]> Pileus Git - ~andy/gtk/blob - gtk/gtkselection.c
Switch style of stamp file usage.
[~andy/gtk] / gtk / gtkselection.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
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.
8  *
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.
13  *
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.
18  */
19
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 
23  * same way.
24  *
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.
30  *
31  * By Owen Taylor <owt1@cornell.edu>          8/16/97
32  */
33
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 ....)
40  */
41
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 ... */
46
47 /*
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/. 
52  */
53
54 #include <stdarg.h>
55 #include <string.h>
56 #include "gdk.h"
57
58 #include "gtkmain.h"
59 #include "gtkselection.h"
60 #include "gtksignal.h"
61
62 /* #define DEBUG_SELECTION */
63
64 /* Maximum size of a sent chunk, in bytes. Also the default size of
65    our buffers */
66 #ifdef GDK_WINDOWING_WIN32
67 /* No chunks on Win32 */
68 #define GTK_SELECTION_MAX_SIZE G_MAXINT
69 #else
70 #define GTK_SELECTION_MAX_SIZE 4000
71 #endif
72 enum {
73   INCR,
74   MULTIPLE,
75   TARGETS,
76   TIMESTAMP,
77   LAST_ATOM
78 };
79
80 typedef struct _GtkSelectionInfo GtkSelectionInfo;
81 typedef struct _GtkIncrConversion GtkIncrConversion;
82 typedef struct _GtkIncrInfo GtkIncrInfo;
83 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
84
85 struct _GtkSelectionInfo
86 {
87   GdkAtom    selection;
88   GtkWidget *widget;            /* widget that owns selection */
89   guint32    time;              /* time used to acquire selection */
90 };
91
92 struct _GtkIncrConversion 
93 {
94   GdkAtom           target;     /* Requested target */
95   GdkAtom           property;   /* Property to store in */
96   GtkSelectionData  data;       /* The data being supplied */
97   gint              offset;     /* Current offset in sent selection.
98                                  *  -1 => All done
99                                  *  -2 => Only the final (empty) portion
100                                  *        left to send */
101 };
102
103 struct _GtkIncrInfo
104 {
105   GdkWindow *requestor;         /* Requestor window - we create a GdkWindow
106                                    so we can receive events */
107   GdkAtom    selection;         /* Selection we're sending */
108   
109   GtkIncrConversion *conversions; /* Information about requested conversions -
110                                    * With MULTIPLE requests (benighted 1980's
111                                    * hardware idea), there can be more than
112                                    * one */
113   gint num_conversions;
114   gint num_incrs;               /* number of remaining INCR style transactions */
115   guint32 idle_time;
116 };
117
118
119 struct _GtkRetrievalInfo
120 {
121   GtkWidget *widget;
122   GdkAtom selection;            /* Selection being retrieved. */
123   GdkAtom target;               /* Form of selection that we requested */
124   guint32 idle_time;            /* Number of seconds since we last heard
125                                    from selection owner */
126   guchar   *buffer;             /* Buffer in which to accumulate results */
127   gint     offset;              /* Current offset in buffer, -1 indicates
128                                    not yet started */
129   guint32 notify_time;          /* Timestamp from SelectionNotify */
130 };
131
132 /* Local Functions */
133 static void gtk_selection_init              (void);
134 static gint gtk_selection_incr_timeout      (GtkIncrInfo      *info);
135 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
136 static void gtk_selection_retrieval_report  (GtkRetrievalInfo *info,
137                                              GdkAtom           type,
138                                              gint              format,
139                                              guchar           *buffer,
140                                              gint              length,
141                                              guint32           time);
142 static void gtk_selection_invoke_handler    (GtkWidget        *widget,
143                                              GtkSelectionData *data,
144                                              guint             time);
145 static void gtk_selection_default_handler   (GtkWidget        *widget,
146                                              GtkSelectionData *data);
147 static int  gtk_selection_bytes_per_item    (gint              format);
148
149 /* Local Data */
150 static gint initialize = TRUE;
151 static GList *current_retrievals = NULL;
152 static GList *current_incrs = NULL;
153 static GList *current_selections = NULL;
154
155 static GdkAtom gtk_selection_atoms[LAST_ATOM];
156 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
157
158 /****************
159  * Target Lists *
160  ****************/
161
162 /*
163  * Target lists
164  */
165
166 GtkTargetList *
167 gtk_target_list_new (const GtkTargetEntry *targets,
168                      guint                 ntargets)
169 {
170   GtkTargetList *result = g_new (GtkTargetList, 1);
171   result->list = NULL;
172   result->ref_count = 1;
173
174   if (targets)
175     gtk_target_list_add_table (result, targets, ntargets);
176   
177   return result;
178 }
179
180 void               
181 gtk_target_list_ref (GtkTargetList *list)
182 {
183   g_return_if_fail (list != NULL);
184
185   list->ref_count++;
186 }
187
188 void               
189 gtk_target_list_unref (GtkTargetList *list)
190 {
191   g_return_if_fail (list != NULL);
192   g_return_if_fail (list->ref_count > 0);
193
194   list->ref_count--;
195   if (list->ref_count == 0)
196     {
197       GList *tmp_list = list->list;
198       while (tmp_list)
199         {
200           GtkTargetPair *pair = tmp_list->data;
201           g_free (pair);
202
203           tmp_list = tmp_list->next;
204         }
205       
206       g_list_free (list->list);
207       g_free (list);
208     }
209 }
210
211 void 
212 gtk_target_list_add (GtkTargetList *list,
213                      GdkAtom            target,
214                      guint              flags,
215                      guint              info)
216 {
217   GtkTargetPair *pair;
218
219   g_return_if_fail (list != NULL);
220   
221   pair = g_new (GtkTargetPair, 1);
222   pair->target = target;
223   pair->flags = flags;
224   pair->info = info;
225
226   list->list = g_list_append (list->list, pair);
227 }
228
229 void               
230 gtk_target_list_add_table (GtkTargetList        *list,
231                            const GtkTargetEntry *targets,
232                            guint                 ntargets)
233 {
234   gint i;
235
236   for (i=ntargets-1; i >= 0; i--)
237     {
238       GtkTargetPair *pair = g_new (GtkTargetPair, 1);
239       pair->target = gdk_atom_intern (targets[i].target, FALSE);
240       pair->flags = targets[i].flags;
241       pair->info = targets[i].info;
242       
243       list->list = g_list_prepend (list->list, pair);
244     }
245 }
246
247 void 
248 gtk_target_list_remove (GtkTargetList *list,
249                         GdkAtom            target)
250 {
251   GList *tmp_list;
252
253   g_return_if_fail (list != NULL);
254
255   tmp_list = list->list;
256   while (tmp_list)
257     {
258       GtkTargetPair *pair = tmp_list->data;
259       
260       if (pair->target == target)
261         {
262           g_free (pair);
263
264           list->list = g_list_remove_link (list->list, tmp_list);
265           g_list_free_1 (tmp_list);
266
267           return;
268         }
269       
270       tmp_list = tmp_list->next;
271     }
272 }
273
274 gboolean
275 gtk_target_list_find (GtkTargetList *list,
276                       GdkAtom        target,
277                       guint         *info)
278 {
279   GList *tmp_list = list->list;
280   while (tmp_list)
281     {
282       GtkTargetPair *pair = tmp_list->data;
283
284       if (pair->target == target)
285         {
286           *info = pair->info;
287           return TRUE;
288         }
289       tmp_list = tmp_list->next;
290     }
291
292   return FALSE;
293 }
294
295
296 /*************************************************************
297  * gtk_selection_owner_set:
298  *     Claim ownership of a selection.
299  *   arguments:
300  *     widget:          new selection owner
301  *     selection:       which selection
302  *     time:            time (use GDK_CURRENT_TIME only if necessary)
303  *
304  *   results:
305  *************************************************************/
306
307 gboolean
308 gtk_selection_owner_set (GtkWidget *widget,
309                          GdkAtom    selection,
310                          guint32    time)
311 {
312   GList *tmp_list;
313   GtkWidget *old_owner;
314   GtkSelectionInfo *selection_info = NULL;
315   GdkWindow *window;
316
317   g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
318   
319   if (widget == NULL)
320     window = NULL;
321   else
322     window = widget->window;
323
324   tmp_list = current_selections;
325   while (tmp_list)
326     {
327       if (((GtkSelectionInfo *)tmp_list->data)->selection == selection)
328         {
329           selection_info = tmp_list->data;
330           break;
331         }
332       
333       tmp_list = tmp_list->next;
334     }
335   
336   if (gdk_selection_owner_set (window, selection, time, TRUE))
337     {
338       old_owner = NULL;
339       
340       if (widget == NULL)
341         {
342           if (selection_info)
343             {
344               old_owner = selection_info->widget;
345               current_selections = g_list_remove_link (current_selections,
346                                                        tmp_list);
347               g_list_free (tmp_list);
348               g_free (selection_info);
349             }
350         }
351       else
352         {
353           if (selection_info == NULL)
354             {
355               selection_info = g_new (GtkSelectionInfo, 1);
356               selection_info->selection = selection;
357               selection_info->widget = widget;
358               selection_info->time = time;
359               current_selections = g_list_prepend (current_selections, 
360                                                    selection_info);
361             }
362           else
363             {
364               old_owner = selection_info->widget;
365               selection_info->widget = widget;
366               selection_info->time = time;
367             }
368         }
369       /* If another widget in the application lost the selection,
370        *  send it a GDK_SELECTION_CLEAR event.
371        */
372       if (old_owner && old_owner != widget)
373         {
374           GdkEventSelection event;
375           
376           event.type = GDK_SELECTION_CLEAR;
377           event.window = old_owner->window;
378           event.selection = selection;
379           event.time = time;
380           
381           gtk_widget_event (old_owner, (GdkEvent *) &event);
382         }
383       return TRUE;
384     }
385   else
386     return FALSE;
387 }
388
389 /*************************************************************
390  * gtk_selection_add_target
391  *     Add specified target to list of supported targets
392  *
393  *   arguments:
394  *     widget:     The widget for which this target applies
395  *     selection:
396  *     target:
397  *     info:       guint to pass to to the selection_get signal 
398  *
399  *   results:
400  *************************************************************/
401
402 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
403
404 struct _GtkSelectionTargetList {
405   GdkAtom selection;
406   GtkTargetList *list;
407 };
408
409 static GtkTargetList *
410 gtk_selection_target_list_get (GtkWidget    *widget,
411                                GdkAtom       selection)
412 {
413   GtkSelectionTargetList *sellist;
414   GList *tmp_list;
415   GList *lists;
416
417   lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
418   
419   tmp_list = lists;
420   while (tmp_list)
421     {
422       sellist = tmp_list->data;
423       if (sellist->selection == selection)
424         return sellist->list;
425       tmp_list = tmp_list->next;
426     }
427
428   sellist = g_new (GtkSelectionTargetList, 1);
429   sellist->selection = selection;
430   sellist->list = gtk_target_list_new (NULL, 0);
431
432   lists = g_list_prepend (lists, sellist);
433   gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
434
435   return sellist->list;
436 }
437
438 static void
439 gtk_selection_target_list_remove (GtkWidget    *widget)
440 {
441   GtkSelectionTargetList *sellist;
442   GList *tmp_list;
443   GList *lists;
444
445   lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
446   
447   tmp_list = lists;
448   while (tmp_list)
449     {
450       sellist = tmp_list->data;
451
452       gtk_target_list_unref (sellist->list);
453
454       g_free (sellist);
455       tmp_list = tmp_list->next;
456     }
457
458   g_list_free (lists);
459   gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL);
460 }
461
462 /**
463  * gtk_selection_clear_targets:
464  * @widget:    a #GtkWidget
465  * @selection: an atom representing a selection
466  *
467  * Remove all targets registered for the given selection for the
468  * widget.
469  **/
470 void 
471 gtk_selection_clear_targets (GtkWidget *widget,
472                              GdkAtom    selection)
473 {
474   GtkSelectionTargetList *sellist;
475   GList *tmp_list;
476   GList *lists;
477
478   lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
479   
480   tmp_list = lists;
481   while (tmp_list)
482     {
483       sellist = tmp_list->data;
484       if (sellist->selection == selection)
485         {
486           lists = g_list_delete_link (lists, tmp_list);
487           gtk_target_list_unref (sellist->list);
488           g_free (sellist);
489
490           break;
491         }
492       
493       tmp_list = tmp_list->next;
494     }
495   
496   gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
497 }
498
499 void 
500 gtk_selection_add_target (GtkWidget         *widget, 
501                           GdkAtom            selection,
502                           GdkAtom            target,
503                           guint              info)
504 {
505   GtkTargetList *list;
506
507   g_return_if_fail (widget != NULL);
508
509   list = gtk_selection_target_list_get (widget, selection);
510   gtk_target_list_add (list, target, 0, info);
511 }
512
513 void 
514 gtk_selection_add_targets (GtkWidget            *widget, 
515                            GdkAtom               selection,
516                            const GtkTargetEntry *targets,
517                            guint                 ntargets)
518 {
519   GtkTargetList *list;
520   
521   g_return_if_fail (widget != NULL);
522   g_return_if_fail (targets != NULL);
523   
524   list = gtk_selection_target_list_get (widget, selection);
525   gtk_target_list_add_table (list, targets, ntargets);
526 }
527
528
529 /*************************************************************
530  * gtk_selection_remove_all:
531  *     Removes all handlers and unsets ownership of all 
532  *     selections for a widget. Called when widget is being
533  *     destroyed
534  *     
535  *   arguments:
536  *     widget:    The widget
537  *   results:
538  *************************************************************/
539
540 void
541 gtk_selection_remove_all (GtkWidget *widget)
542 {
543   GList *tmp_list;
544   GList *next;
545   GtkSelectionInfo *selection_info;
546   
547   /* Remove pending requests/incrs for this widget */
548   
549   tmp_list = current_retrievals;
550   while (tmp_list)
551     {
552       next = tmp_list->next;
553       if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
554         {
555           current_retrievals = g_list_remove_link (current_retrievals, 
556                                                    tmp_list);
557           /* structure will be freed in timeout */
558           g_list_free (tmp_list);
559         }
560       tmp_list = next;
561     }
562   
563   /* Disclaim ownership of any selections */
564   
565   tmp_list = current_selections;
566   while (tmp_list)
567     {
568       next = tmp_list->next;
569       selection_info = (GtkSelectionInfo *)tmp_list->data;
570       
571       if (selection_info->widget == widget)
572         {       
573           gdk_selection_owner_set (NULL, 
574                                    selection_info->selection,
575                                    GDK_CURRENT_TIME, FALSE);
576           current_selections = g_list_remove_link (current_selections, 
577                                                    tmp_list);
578           g_list_free (tmp_list);
579           g_free (selection_info);
580         }
581       
582       tmp_list = next;
583     }
584
585   /* Remove all selection lists */
586   gtk_selection_target_list_remove (widget);
587 }
588
589 /*************************************************************
590  * gtk_selection_convert:
591  *     Request the contents of a selection. When received, 
592  *     a "selection_received" signal will be generated.
593  *
594  *   arguments:
595  *     widget:     The widget which acts as requestor
596  *     selection:  Which selection to get
597  *     target:     Form of information desired (e.g., STRING)
598  *     time:       Time of request (usually of triggering event)
599  *                 In emergency, you could use GDK_CURRENT_TIME
600  *
601  *   results:
602  *     TRUE if requested succeeded. FALSE if we could not process
603  *     request. (e.g., there was already a request in process for
604  *     this widget). 
605  *************************************************************/
606
607 gboolean
608 gtk_selection_convert (GtkWidget *widget, 
609                        GdkAtom    selection, 
610                        GdkAtom    target,
611                        guint32    time)
612 {
613   GtkRetrievalInfo *info;
614   GList *tmp_list;
615   GdkWindow *owner_window;
616   
617   g_return_val_if_fail (widget != NULL, FALSE);
618   
619   if (initialize)
620     gtk_selection_init ();
621   
622   if (!GTK_WIDGET_REALIZED (widget))
623     gtk_widget_realize (widget);
624   
625   /* Check to see if there are already any retrievals in progress for
626      this widget. If we changed GDK to use the selection for the 
627      window property in which to store the retrieved information, then
628      we could support multiple retrievals for different selections.
629      This might be useful for DND. */
630   
631   tmp_list = current_retrievals;
632   while (tmp_list)
633     {
634       info = (GtkRetrievalInfo *)tmp_list->data;
635       if (info->widget == widget)
636         return FALSE;
637       tmp_list = tmp_list->next;
638     }
639   
640   info = g_new (GtkRetrievalInfo, 1);
641   
642   info->widget = widget;
643   info->selection = selection;
644   info->target = target;
645   info->buffer = NULL;
646   info->offset = -1;
647   
648   /* Check if this process has current owner. If so, call handler
649      procedure directly to avoid deadlocks with INCR. */
650   
651   owner_window = gdk_selection_owner_get (selection);
652   
653   if (owner_window != NULL)
654     {
655       GtkWidget *owner_widget;
656       GtkSelectionData selection_data;
657       
658       selection_data.selection = selection;
659       selection_data.target = target;
660       selection_data.data = NULL;
661       selection_data.length = -1;
662       
663       gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
664       
665       if (owner_widget != NULL)
666         {
667           gtk_selection_invoke_handler (owner_widget, 
668                                         &selection_data,
669                                         time);
670           
671           gtk_selection_retrieval_report (info,
672                                           selection_data.type, 
673                                           selection_data.format,
674                                           selection_data.data,
675                                           selection_data.length,
676                                           time);
677           
678           g_free (selection_data.data);
679           
680           g_free (info);
681           return TRUE;
682         }
683     }
684   
685   /* Otherwise, we need to go through X */
686   
687   current_retrievals = g_list_append (current_retrievals, info);
688   gdk_selection_convert (widget->window, selection, target, time);
689   gtk_timeout_add (1000, (GtkFunction) gtk_selection_retrieval_timeout, info);
690   
691   return TRUE;
692 }
693
694 /*************************************************************
695  * gtk_selection_data_set:
696  *     Store new data into a GtkSelectionData object. Should
697  *     _only_ by called from a selection handler callback.
698  *     Null terminates the stored data.
699  *   arguments:
700  *     type:    the type of selection data
701  *     format:  format (number of bits in a unit)
702  *     data:    pointer to the data (will be copied)
703  *     length:  length of the data
704  *   results:
705  *************************************************************/
706
707 void 
708 gtk_selection_data_set (GtkSelectionData *selection_data,
709                         GdkAtom           type,
710                         gint              format,
711                         const guchar     *data,
712                         gint              length)
713 {
714   if (selection_data->data)
715     g_free (selection_data->data);
716   
717   selection_data->type = type;
718   selection_data->format = format;
719   
720   if (data)
721     {
722       selection_data->data = g_new (guchar, length+1);
723       memcpy (selection_data->data, data, length);
724       selection_data->data[length] = 0;
725     }
726   else
727     {
728       g_return_if_fail (length <= 0);
729       
730       if (length < 0)
731         selection_data->data = NULL;
732       else
733         selection_data->data = g_strdup("");
734     }
735   
736   selection_data->length = length;
737 }
738
739 static GdkAtom utf8_atom;
740 static GdkAtom text_atom;
741 static GdkAtom ctext_atom;
742
743 static void 
744 init_atoms (void)
745 {
746   if (!utf8_atom)
747     {
748       utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
749       text_atom = gdk_atom_intern ("TEXT", FALSE);
750       ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
751     }
752 }
753
754 /**
755  * gtk_selection_data_set_text:
756  * @selection_data: a #GtkSelectionData
757  * @str: a UTF-8 string
758  * @len: the length of @str, or -1 if @str is nul-terminated.
759  * 
760  * Sets the contents of the selection from a UTF-8 encoded string.
761  * The string is converted to the form determined by
762  * @selection_data->target.
763  * 
764  * Return value: %TRUE if the selection was successfully set,
765  *   otherwise %FALSE.
766  **/
767 gboolean
768 gtk_selection_data_set_text (GtkSelectionData     *selection_data,
769                              const gchar          *str,
770                              gint                  len)
771 {
772   gboolean result = FALSE;
773   
774   if (len < 0)
775     len = strlen (str);
776   
777   init_atoms ();
778
779   if (selection_data->target == utf8_atom)
780     {
781       gtk_selection_data_set (selection_data,
782                               utf8_atom,
783                               8, (guchar *)str, len);
784       result = TRUE;
785     }
786   else if (selection_data->target == GDK_TARGET_STRING)
787     {
788       gchar *tmp = g_strndup (str, len);
789       gchar *latin1 = gdk_utf8_to_string_target (tmp);
790       g_free (tmp);
791
792       if (latin1)
793         {
794           gtk_selection_data_set (selection_data,
795                                   GDK_SELECTION_TYPE_STRING,
796                                   8, latin1, strlen (latin1));
797           g_free (latin1);
798           
799           result = TRUE;
800         }
801
802     }
803   else if (selection_data->target == ctext_atom ||
804            selection_data->target == text_atom)
805     {
806       gchar *tmp;
807       guchar *text;
808       GdkAtom encoding;
809       gint format;
810       gint new_length;
811
812       tmp = g_strndup (str, len);
813       if (gdk_utf8_to_compound_text (tmp, &encoding, &format, &text, &new_length))
814         {
815           gtk_selection_data_set (selection_data, encoding, format, text, new_length);
816           gdk_free_compound_text (text);
817
818           result = TRUE;
819         }
820
821       g_free (tmp);
822     }
823   
824   return result;
825 }
826
827 /**
828  * gtk_selection_data_get_text:
829  * @selection_data: a #GtkSelectionData
830  * 
831  * Gets the contents of the selection data as a UTF-8 string.
832  * 
833  * Return value: if the selection data contained a recognized
834  *   text type and it could be converted to UTF-8, a newly allocated
835  *   string containing the converted text, otherwise %NULL.
836  *   If the result is non-%NULL it must be freed with g_free().
837  **/
838 guchar *
839 gtk_selection_data_get_text (GtkSelectionData *selection_data)
840 {
841   guchar *result = NULL;
842
843   init_atoms ();
844   
845   if (selection_data->length >= 0 &&
846       (selection_data->type == GDK_TARGET_STRING ||
847        selection_data->type == ctext_atom ||
848        selection_data->type == utf8_atom))
849     {
850       gchar **list;
851       gint i;
852       gint count = gdk_text_property_to_utf8_list (selection_data->type,
853                                                    selection_data->format, 
854                                                    selection_data->data,
855                                                    selection_data->length,
856                                                    &list);
857       if (count > 0)
858         result = list[0];
859
860       for (i = 1; i < count; i++)
861         g_free (list[i]);
862       g_free (list);
863     }
864
865   return result;
866 }
867
868 /**
869  * gtk_selection_data_get_targets:
870  * @selection_data: a #GtkSelectionData object
871  * @targets: location to store an array of targets. The result
872  *           stored here must be freed with g_free().
873  * @n_atoms: location to store number of items in @targets.
874  * 
875  * Gets the contents of @selection_data as an array of targets.
876  * This can be used to interpret the results of getting
877  * the standard TARGETS target that is always supplied for
878  * any selection.
879  * 
880  * Return value: %TRUE if @selection_data contains a valid
881  *    array of targets, otherwise %FALSE.
882  **/
883 gboolean
884 gtk_selection_data_get_targets (GtkSelectionData  *selection_data,
885                                 GdkAtom          **targets,
886                                 gint              *n_atoms)
887 {
888   if (selection_data->length >= 0 &&
889       selection_data->format == 32 &&
890       selection_data->type == GDK_SELECTION_TYPE_ATOM)
891     {
892       if (targets)
893         *targets = g_memdup (selection_data->data, selection_data->length);
894       if (n_atoms)
895         *n_atoms = selection_data->length / sizeof (GdkAtom);
896
897       return TRUE;
898     }
899   else
900     {
901       if (targets)
902         *targets = NULL;
903       if (n_atoms)
904         *n_atoms = -1;
905
906       return FALSE;
907     }
908 }
909
910 /**
911  * gtk_selection_data_targets_include_text:
912  * @selection_data: a #GtkSelectionData object
913  * 
914  * Given a #GtkSelectionData object holding a list of targets,
915  * determines if any of the targets in @targets can be used to
916  * provide text.
917  * 
918  * Return value: %TRUE if @selection_data holds a list of targets,
919  *   and a suitable target for text is included, otherwise %FALSE.
920  **/
921 gboolean
922 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
923 {
924   GdkAtom *targets;
925   gint n_targets;
926   gint i;
927   gboolean result = FALSE;
928
929   if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
930     {
931       for (i=0; i < n_targets; i++)
932         {
933           if (targets[i] == gdk_atom_intern ("STRING", FALSE) ||
934               targets[i] == gdk_atom_intern ("TEXT", FALSE) ||
935               targets[i] == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
936               targets[i] == gdk_atom_intern ("UTF8_STRING", FALSE))
937             result = TRUE;
938         }
939
940       g_free (targets);
941     }
942
943   return result;
944 }
945           
946 /*************************************************************
947  * gtk_selection_init:
948  *     Initialize local variables
949  *   arguments:
950  *     
951  *   results:
952  *************************************************************/
953
954 static void
955 gtk_selection_init (void)
956 {
957   gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
958   gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
959   gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
960   gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
961 }
962
963 /*************************************************************
964  * gtk_selection_clear:
965  *     Handler for "selection_clear_event"
966  *   arguments:
967  *     widget:
968  *     event:
969  *   results:
970  *************************************************************/
971
972 gboolean
973 gtk_selection_clear (GtkWidget         *widget,
974                      GdkEventSelection *event)
975 {
976   /* Note that we filter clear events in gdkselection-x11.c, so
977    * that we only will get here if the clear event actually
978    * represents a change that we didn't do ourself.
979    */
980   GList *tmp_list;
981   GtkSelectionInfo *selection_info = NULL;
982   
983   tmp_list = current_selections;
984   while (tmp_list)
985     {
986       selection_info = (GtkSelectionInfo *)tmp_list->data;
987       
988       if ((selection_info->selection == event->selection) &&
989           (selection_info->widget == widget))
990         break;
991       
992       tmp_list = tmp_list->next;
993     }
994   
995   if (tmp_list)
996     {
997       current_selections = g_list_remove_link (current_selections, tmp_list);
998       g_list_free (tmp_list);
999       g_free (selection_info);
1000     }
1001   
1002   return TRUE;
1003 }
1004
1005
1006 /*************************************************************
1007  * gtk_selection_request:
1008  *     Handler for "selection_request_event" 
1009  *   arguments:
1010  *     widget:
1011  *     event:
1012  *   results:
1013  *************************************************************/
1014
1015 gboolean
1016 gtk_selection_request (GtkWidget *widget,
1017                        GdkEventSelection *event)
1018 {
1019   GtkIncrInfo *info;
1020   GList *tmp_list;
1021   guchar *mult_atoms;
1022   int i;
1023   
1024   if (initialize)
1025     gtk_selection_init ();
1026   
1027   /* Check if we own selection */
1028   
1029   tmp_list = current_selections;
1030   while (tmp_list)
1031     {
1032       GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1033       
1034       if ((selection_info->selection == event->selection) &&
1035           (selection_info->widget == widget))
1036         break;
1037       
1038       tmp_list = tmp_list->next;
1039     }
1040   
1041   if (tmp_list == NULL)
1042     return FALSE;
1043   
1044   info = g_new (GtkIncrInfo, 1);
1045
1046   g_object_ref (widget);
1047   
1048   info->selection = event->selection;
1049   info->num_incrs = 0;
1050   
1051   /* Create GdkWindow structure for the requestor */
1052   info->requestor = gdk_window_lookup (event->requestor);
1053   if (!info->requestor)
1054     info->requestor = gdk_window_foreign_new (event->requestor);
1055   
1056   /* Determine conversions we need to perform */
1057   
1058   if (event->target == gtk_selection_atoms[MULTIPLE])
1059     {
1060       GdkAtom  type;
1061       gint     format;
1062       gint     length;
1063       
1064       mult_atoms = NULL;
1065       
1066       gdk_error_trap_push ();
1067       if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */
1068                              0, GTK_SELECTION_MAX_SIZE, FALSE,
1069                              &type, &format, &length, &mult_atoms))
1070         {
1071           gdk_selection_send_notify (event->requestor, event->selection,
1072                                      event->target, GDK_NONE, event->time);
1073           g_free (mult_atoms);
1074           g_free (info);
1075           return TRUE;
1076         }
1077       gdk_error_trap_pop ();
1078       
1079       info->num_conversions = length / (2*sizeof (GdkAtom));
1080       info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1081       
1082       for (i=0; i<info->num_conversions; i++)
1083         {
1084           info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1085           info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1086         }
1087     }
1088   else                          /* only a single conversion */
1089     {
1090       info->conversions = g_new (GtkIncrConversion, 1);
1091       info->num_conversions = 1;
1092       info->conversions[0].target = event->target;
1093       info->conversions[0].property = event->property;
1094       mult_atoms = (guchar *)info->conversions;
1095     }
1096   
1097   /* Loop through conversions and determine which of these are big
1098      enough to require doing them via INCR */
1099   for (i=0; i<info->num_conversions; i++)
1100     {
1101       GtkSelectionData data;
1102       glong items;
1103       
1104       data.selection = event->selection;
1105       data.target = info->conversions[i].target;
1106       data.data = NULL;
1107       data.length = -1;
1108       
1109 #ifdef DEBUG_SELECTION
1110       g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1111                  event->selection, info->conversions[i].target,
1112                  gdk_atom_name (info->conversions[i].target),
1113                  event->requestor, event->property);
1114 #endif
1115       
1116       gtk_selection_invoke_handler (widget, &data, event->time);
1117       
1118       if (data.length < 0)
1119         {
1120           ((GdkAtom *)mult_atoms)[2*i+1] = GDK_NONE;
1121           info->conversions[i].property = GDK_NONE;
1122           continue;
1123         }
1124       
1125       g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1126       
1127       items = data.length / gtk_selection_bytes_per_item (data.format);
1128       
1129       if (data.length > GTK_SELECTION_MAX_SIZE)
1130         {
1131           /* Sending via INCR */
1132           
1133           info->conversions[i].offset = 0;
1134           info->conversions[i].data = data;
1135           info->num_incrs++;
1136           
1137           gdk_property_change (info->requestor, 
1138                                info->conversions[i].property,
1139                                gtk_selection_atoms[INCR],
1140                                32,
1141                                GDK_PROP_MODE_REPLACE,
1142                                (guchar *)&items, 1);
1143         }
1144       else
1145         {
1146           info->conversions[i].offset = -1;
1147           
1148           gdk_property_change (info->requestor, 
1149                                info->conversions[i].property,
1150                                data.type,
1151                                data.format,
1152                                GDK_PROP_MODE_REPLACE,
1153                                data.data, items);
1154           
1155           g_free (data.data);
1156         }
1157     }
1158   
1159   /* If we have some INCR's, we need to send the rest of the data in
1160      a callback */
1161   
1162   if (info->num_incrs > 0)
1163     {
1164       /* FIXME: this could be dangerous if window doesn't still
1165          exist */
1166       
1167 #ifdef DEBUG_SELECTION
1168       g_message ("Starting INCR...");
1169 #endif
1170       
1171       gdk_window_set_events (info->requestor,
1172                              gdk_window_get_events (info->requestor) |
1173                              GDK_PROPERTY_CHANGE_MASK);
1174       current_incrs = g_list_append (current_incrs, info);
1175       gtk_timeout_add (1000, (GtkFunction)gtk_selection_incr_timeout, info);
1176     }
1177   
1178   /* If it was a MULTIPLE request, set the property to indicate which
1179      conversions succeeded */
1180   if (event->target == gtk_selection_atoms[MULTIPLE])
1181     {
1182       gdk_property_change (info->requestor, event->property,
1183                            gdk_atom_intern ("ATOM_PAIR", FALSE), 32, 
1184                            GDK_PROP_MODE_REPLACE,
1185                            mult_atoms, 2*info->num_conversions);
1186       g_free (mult_atoms);
1187     }
1188
1189   if (info->num_conversions == 1 &&
1190       info->conversions[0].property == GDK_NONE)
1191     {
1192       /* Reject the entire conversion */
1193       gdk_selection_send_notify (event->requestor, event->selection, 
1194                                  event->target, GDK_NONE, event->time);
1195     }
1196   else
1197     {
1198       gdk_selection_send_notify (event->requestor, event->selection, 
1199                                  event->target, event->property, event->time);
1200     }
1201
1202   if (info->num_incrs == 0)
1203     {
1204       g_free (info->conversions);
1205       g_free (info);
1206     }
1207
1208   g_object_unref (widget);
1209   
1210   return TRUE;
1211 }
1212
1213 /*************************************************************
1214  * gtk_selection_incr_event:
1215  *     Called whenever an PropertyNotify event occurs for an 
1216  *     GdkWindow with user_data == NULL. These will be notifications
1217  *     that a window we are sending the selection to via the
1218  *     INCR protocol has deleted a property and is ready for
1219  *     more data.
1220  *
1221  *   arguments:
1222  *     window:  the requestor window
1223  *     event:   the property event structure
1224  *
1225  *   results:
1226  *************************************************************/
1227
1228 gboolean
1229 gtk_selection_incr_event (GdkWindow        *window,
1230                           GdkEventProperty *event)
1231 {
1232   GList *tmp_list;
1233   GtkIncrInfo *info = NULL;
1234   gint num_bytes;
1235   guchar *buffer;
1236   
1237   int i;
1238   
1239   if (event->state != GDK_PROPERTY_DELETE)
1240     return FALSE;
1241   
1242 #ifdef DEBUG_SELECTION
1243   g_message ("PropertyDelete, property %ld", event->atom);
1244 #endif
1245   
1246   /* Now find the appropriate ongoing INCR */
1247   tmp_list = current_incrs;
1248   while (tmp_list)
1249     {
1250       info = (GtkIncrInfo *)tmp_list->data;
1251       if (info->requestor == event->window)
1252         break;
1253       
1254       tmp_list = tmp_list->next;
1255     }
1256   
1257   if (tmp_list == NULL)
1258     return FALSE;
1259   
1260   /* Find out which target this is for */
1261   for (i=0; i<info->num_conversions; i++)
1262     {
1263       if (info->conversions[i].property == event->atom &&
1264           info->conversions[i].offset != -1)
1265         {
1266           int bytes_per_item;
1267           
1268           info->idle_time = 0;
1269           
1270           if (info->conversions[i].offset == -2) /* only the last 0-length
1271                                                     piece*/
1272             {
1273               num_bytes = 0;
1274               buffer = NULL;
1275             }
1276           else
1277             {
1278               num_bytes = info->conversions[i].data.length -
1279                 info->conversions[i].offset;
1280               buffer = info->conversions[i].data.data + 
1281                 info->conversions[i].offset;
1282               
1283               if (num_bytes > GTK_SELECTION_MAX_SIZE)
1284                 {
1285                   num_bytes = GTK_SELECTION_MAX_SIZE;
1286                   info->conversions[i].offset += GTK_SELECTION_MAX_SIZE;
1287                 }
1288               else
1289                 info->conversions[i].offset = -2;
1290             }
1291 #ifdef DEBUG_SELECTION
1292           g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1293                      num_bytes, info->conversions[i].offset, 
1294                      GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1295 #endif
1296
1297           bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1298           gdk_property_change (info->requestor, event->atom,
1299                                info->conversions[i].data.type,
1300                                info->conversions[i].data.format,
1301                                GDK_PROP_MODE_REPLACE,
1302                                buffer,
1303                                num_bytes / bytes_per_item);
1304           
1305           if (info->conversions[i].offset == -2)
1306             {
1307               g_free (info->conversions[i].data.data);
1308               info->conversions[i].data.data = NULL;
1309             }
1310           
1311           if (num_bytes == 0)
1312             {
1313               info->num_incrs--;
1314               info->conversions[i].offset = -1;
1315             }
1316         }
1317       break;
1318     }
1319   
1320   /* Check if we're finished with all the targets */
1321   
1322   if (info->num_incrs == 0)
1323     {
1324       current_incrs = g_list_remove_link (current_incrs, tmp_list);
1325       g_list_free (tmp_list);
1326       /* Let the timeout free it */
1327     }
1328   
1329   return TRUE;
1330 }
1331
1332 /*************************************************************
1333  * gtk_selection_incr_timeout:
1334  *     Timeout callback for the sending portion of the INCR
1335  *     protocol
1336  *   arguments:
1337  *     info:    Information about this incr
1338  *   results:
1339  *************************************************************/
1340
1341 static gint
1342 gtk_selection_incr_timeout (GtkIncrInfo *info)
1343 {
1344   GList *tmp_list;
1345   gboolean retval;
1346
1347   GDK_THREADS_ENTER ();
1348   
1349   /* Determine if retrieval has finished by checking if it still in
1350      list of pending retrievals */
1351   
1352   tmp_list = current_incrs;
1353   while (tmp_list)
1354     {
1355       if (info == (GtkIncrInfo *)tmp_list->data)
1356         break;
1357       tmp_list = tmp_list->next;
1358     }
1359   
1360   /* If retrieval is finished */
1361   if (!tmp_list || info->idle_time >= 5)
1362     {
1363       if (tmp_list && info->idle_time >= 5)
1364         {
1365           current_incrs = g_list_remove_link (current_incrs, tmp_list);
1366           g_list_free (tmp_list);
1367         }
1368       
1369       g_free (info->conversions);
1370       /* FIXME: we should check if requestor window is still in use,
1371          and if not, remove it? */
1372       
1373       g_free (info);
1374       
1375       retval =  FALSE;          /* remove timeout */
1376     }
1377   else
1378     {
1379       info->idle_time++;
1380       
1381       retval = TRUE;            /* timeout will happen again */
1382     }
1383   
1384   GDK_THREADS_LEAVE ();
1385
1386   return retval;
1387 }
1388
1389 /*************************************************************
1390  * gtk_selection_notify:
1391  *     Handler for "selection_notify_event" signals on windows
1392  *     where a retrieval is currently in process. The selection
1393  *     owner has responded to our conversion request.
1394  *   arguments:
1395  *     widget:          Widget getting signal
1396  *     event:           Selection event structure
1397  *     info:            Information about this retrieval
1398  *   results:
1399  *     was event handled?
1400  *************************************************************/
1401
1402 gboolean
1403 gtk_selection_notify (GtkWidget        *widget,
1404                       GdkEventSelection *event)
1405 {
1406   GList *tmp_list;
1407   GtkRetrievalInfo *info = NULL;
1408   guchar  *buffer = NULL;
1409   gint length;
1410   GdkAtom type;
1411   gint    format;
1412   
1413 #ifdef DEBUG_SELECTION
1414   g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1415              event->selection, event->target, event->property);
1416 #endif
1417   
1418   tmp_list = current_retrievals;
1419   while (tmp_list)
1420     {
1421       info = (GtkRetrievalInfo *)tmp_list->data;
1422       if (info->widget == widget && info->selection == event->selection)
1423         break;
1424       tmp_list = tmp_list->next;
1425     }
1426   
1427   if (!tmp_list)                /* no retrieval in progress */
1428     return FALSE;
1429
1430   if (event->property != GDK_NONE)
1431     length = gdk_selection_property_get (widget->window, &buffer, 
1432                                          &type, &format);
1433   else
1434     length = 0; /* silence gcc */
1435   
1436   if (event->property == GDK_NONE || buffer == NULL)
1437     {
1438       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1439       g_list_free (tmp_list);
1440       /* structure will be freed in timeout */
1441       gtk_selection_retrieval_report (info,
1442                                       GDK_NONE, 0, NULL, -1, event->time);
1443       
1444       return TRUE;
1445     }
1446   
1447   if (type == gtk_selection_atoms[INCR])
1448     {
1449       /* The remainder of the selection will come through PropertyNotify
1450          events */
1451
1452       info->notify_time = event->time;
1453       info->idle_time = 0;
1454       info->offset = 0;         /* Mark as OK to proceed */
1455       gdk_window_set_events (widget->window,
1456                              gdk_window_get_events (widget->window)
1457                              | GDK_PROPERTY_CHANGE_MASK);
1458     }
1459   else
1460     {
1461       /* We don't delete the info structure - that will happen in timeout */
1462       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1463       g_list_free (tmp_list);
1464       
1465       info->offset = length;
1466       gtk_selection_retrieval_report (info,
1467                                       type, format, 
1468                                       buffer, length, event->time);
1469     }
1470   
1471   gdk_property_delete (widget->window, event->property);
1472   
1473   g_free (buffer);
1474   
1475   return TRUE;
1476 }
1477
1478 /*************************************************************
1479  * gtk_selection_property_notify:
1480  *     Handler for "property_notify_event" signals on windows
1481  *     where a retrieval is currently in process. The selection
1482  *     owner has added more data.
1483  *   arguments:
1484  *     widget:          Widget getting signal
1485  *     event:           Property event structure
1486  *     info:            Information about this retrieval
1487  *   results:
1488  *     was event handled?
1489  *************************************************************/
1490
1491 gboolean
1492 gtk_selection_property_notify (GtkWidget        *widget,
1493                                GdkEventProperty *event)
1494 {
1495   GList *tmp_list;
1496   GtkRetrievalInfo *info = NULL;
1497   guchar *new_buffer;
1498   int length;
1499   GdkAtom type;
1500   gint    format;
1501   
1502   g_return_val_if_fail (widget != NULL, FALSE);
1503   g_return_val_if_fail (event != NULL, FALSE);
1504
1505 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1506   if ((event->state != GDK_PROPERTY_NEW_VALUE) ||  /* property was deleted */
1507       (event->atom != gdk_atom_intern ("GDK_SELECTION", FALSE))) /* not the right property */
1508 #endif
1509     return FALSE;
1510   
1511 #ifdef DEBUG_SELECTION
1512   g_message ("PropertyNewValue, property %ld",
1513              event->atom);
1514 #endif
1515   
1516   tmp_list = current_retrievals;
1517   while (tmp_list)
1518     {
1519       info = (GtkRetrievalInfo *)tmp_list->data;
1520       if (info->widget == widget)
1521         break;
1522       tmp_list = tmp_list->next;
1523     }
1524   
1525   if (!tmp_list)                /* No retrieval in progress */
1526     return FALSE;
1527   
1528   if (info->offset < 0)         /* We haven't got the SelectionNotify
1529                                    for this retrieval yet */
1530     return FALSE;
1531   
1532   info->idle_time = 0;
1533   
1534   length = gdk_selection_property_get (widget->window, &new_buffer, 
1535                                        &type, &format);
1536   gdk_property_delete (widget->window, event->atom);
1537   
1538   /* We could do a lot better efficiency-wise by paying attention to
1539      what length was sent in the initial INCR transaction, instead of
1540      doing memory allocation at every step. But its only guaranteed to
1541      be a _lower bound_ (pretty useless!) */
1542   
1543   if (length == 0 || type == GDK_NONE)          /* final zero length portion */
1544     {
1545       /* Info structure will be freed in timeout */
1546       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1547       g_list_free (tmp_list);
1548       gtk_selection_retrieval_report (info,
1549                                       type, format, 
1550                                       (type == GDK_NONE) ?  NULL : info->buffer,
1551                                       (type == GDK_NONE) ?  -1 : info->offset,
1552                                       info->notify_time);
1553     }
1554   else                          /* append on newly arrived data */
1555     {
1556       if (!info->buffer)
1557         {
1558 #ifdef DEBUG_SELECTION
1559           g_message ("Start - Adding %d bytes at offset 0",
1560                      length);
1561 #endif
1562           info->buffer = new_buffer;
1563           info->offset = length;
1564         }
1565       else
1566         {
1567           
1568 #ifdef DEBUG_SELECTION
1569           g_message ("Appending %d bytes at offset %d",
1570                      length,info->offset);
1571 #endif
1572           /* We copy length+1 bytes to preserve guaranteed null termination */
1573           info->buffer = g_realloc (info->buffer, info->offset+length+1);
1574           memcpy (info->buffer + info->offset, new_buffer, length+1);
1575           info->offset += length;
1576           g_free (new_buffer);
1577         }
1578     }
1579   
1580   return TRUE;
1581 }
1582
1583 /*************************************************************
1584  * gtk_selection_retrieval_timeout:
1585  *     Timeout callback while receiving a selection.
1586  *   arguments:
1587  *     info:    Information about this retrieval
1588  *   results:
1589  *************************************************************/
1590
1591 static gint
1592 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1593 {
1594   GList *tmp_list;
1595   gboolean retval;
1596
1597   GDK_THREADS_ENTER ();
1598   
1599   /* Determine if retrieval has finished by checking if it still in
1600      list of pending retrievals */
1601   
1602   tmp_list = current_retrievals;
1603   while (tmp_list)
1604     {
1605       if (info == (GtkRetrievalInfo *)tmp_list->data)
1606         break;
1607       tmp_list = tmp_list->next;
1608     }
1609   
1610   /* If retrieval is finished */
1611   if (!tmp_list || info->idle_time >= 5)
1612     {
1613       if (tmp_list && info->idle_time >= 5)
1614         {
1615           current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1616           g_list_free (tmp_list);
1617           gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1618         }
1619       
1620       g_free (info->buffer);
1621       g_free (info);
1622       
1623       retval =  FALSE;          /* remove timeout */
1624     }
1625   else
1626     {
1627       info->idle_time++;
1628       
1629       retval =  TRUE;           /* timeout will happen again */
1630     }
1631
1632   GDK_THREADS_LEAVE ();
1633
1634   return retval;
1635 }
1636
1637 /*************************************************************
1638  * gtk_selection_retrieval_report:
1639  *     Emits a "selection_received" signal.
1640  *   arguments:
1641  *     info:      information about the retrieval that completed
1642  *     buffer:    buffer containing data (NULL => errror)
1643  *     time:      timestamp for data in buffer
1644  *   results:
1645  *************************************************************/
1646
1647 static void
1648 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
1649                                 GdkAtom type, gint format, 
1650                                 guchar *buffer, gint length,
1651                                 guint32 time)
1652 {
1653   GtkSelectionData data;
1654   
1655   data.selection = info->selection;
1656   data.target = info->target;
1657   data.type = type;
1658   data.format = format;
1659   
1660   data.length = length;
1661   data.data = buffer;
1662   
1663   gtk_signal_emit_by_name (GTK_OBJECT(info->widget),
1664                            "selection_received", 
1665                            &data, time);
1666 }
1667
1668 /*************************************************************
1669  * gtk_selection_invoke_handler:
1670  *     Finds and invokes handler for specified
1671  *     widget/selection/target combination, calls 
1672  *     gtk_selection_default_handler if none exists.
1673  *
1674  *   arguments:
1675  *     widget:      selection owner
1676  *     data:        selection data [INOUT]
1677  *     time:        time from requeset
1678  *     
1679  *   results:
1680  *     Number of bytes written to buffer, -1 if error
1681  *************************************************************/
1682
1683 static void
1684 gtk_selection_invoke_handler (GtkWidget        *widget,
1685                               GtkSelectionData *data,
1686                               guint             time)
1687 {
1688   GtkTargetList *target_list;
1689   guint info;
1690   
1691
1692   g_return_if_fail (widget != NULL);
1693
1694   target_list = gtk_selection_target_list_get (widget, data->selection);
1695   if (target_list && 
1696       gtk_target_list_find (target_list, data->target, &info))
1697     {
1698       gtk_signal_emit_by_name (GTK_OBJECT (widget), 
1699                                "selection_get",
1700                                data,
1701                                info, time);
1702     }
1703   else
1704     gtk_selection_default_handler (widget, data);
1705 }
1706
1707 /*************************************************************
1708  * gtk_selection_default_handler:
1709  *     Handles some default targets that exist for any widget
1710  *     If it can't fit results into buffer, returns -1. This
1711  *     won't happen in any conceivable case, since it would
1712  *     require 1000 selection targets!
1713  *
1714  *   arguments:
1715  *     widget:      selection owner
1716  *     data:        selection data [INOUT]
1717  *
1718  *************************************************************/
1719
1720 static void
1721 gtk_selection_default_handler (GtkWidget        *widget,
1722                                GtkSelectionData *data)
1723 {
1724   if (data->target == gtk_selection_atoms[TIMESTAMP])
1725     {
1726       /* Time which was used to obtain selection */
1727       GList *tmp_list;
1728       GtkSelectionInfo *selection_info;
1729       
1730       tmp_list = current_selections;
1731       while (tmp_list)
1732         {
1733           selection_info = (GtkSelectionInfo *)tmp_list->data;
1734           if ((selection_info->widget == widget) &&
1735               (selection_info->selection == data->selection))
1736             {
1737               gulong time = selection_info->time;
1738
1739               gtk_selection_data_set (data,
1740                                       GDK_SELECTION_TYPE_INTEGER,
1741                                       32,
1742                                       (guchar *)&time,
1743                                       sizeof (time));
1744               return;
1745             }
1746           
1747           tmp_list = tmp_list->next;
1748         }
1749       
1750       data->length = -1;
1751     }
1752   else if (data->target == gtk_selection_atoms[TARGETS])
1753     {
1754       /* List of all targets supported for this widget/selection pair */
1755       GdkAtom *p;
1756       guint count;
1757       GList *tmp_list;
1758       GtkTargetList *target_list;
1759       GtkTargetPair *pair;
1760       
1761       target_list = gtk_selection_target_list_get (widget,
1762                                                    data->selection);
1763       count = g_list_length (target_list->list) + 3;
1764       
1765       data->type = GDK_SELECTION_TYPE_ATOM;
1766       data->format = 32;
1767       data->length = count * sizeof (GdkAtom);
1768       
1769       p = g_new (GdkAtom, count);
1770       data->data = (guchar *)p;
1771       
1772       *p++ = gtk_selection_atoms[TIMESTAMP];
1773       *p++ = gtk_selection_atoms[TARGETS];
1774       *p++ = gtk_selection_atoms[MULTIPLE];
1775       
1776       tmp_list = target_list->list;
1777       while (tmp_list)
1778         {
1779           pair = (GtkTargetPair *)tmp_list->data;
1780           *p++ = pair->target;
1781           
1782           tmp_list = tmp_list->next;
1783         }
1784     }
1785   else
1786     {
1787       data->length = -1;
1788     }
1789 }
1790
1791
1792 GtkSelectionData*
1793 gtk_selection_data_copy (GtkSelectionData *selection_data)
1794 {
1795   GtkSelectionData *new_data;
1796   
1797   g_return_val_if_fail (selection_data != NULL, NULL);
1798   
1799   new_data = g_new (GtkSelectionData, 1);
1800   *new_data = *selection_data;
1801
1802   if (selection_data->data)
1803     {
1804       new_data->data = g_malloc (selection_data->length + 1);
1805       memcpy (new_data->data, selection_data->data, selection_data->length + 1);
1806     }
1807   
1808   return new_data;
1809 }
1810
1811 void
1812 gtk_selection_data_free (GtkSelectionData *data)
1813 {
1814   g_return_if_fail (data != NULL);
1815   
1816   if (data->data)
1817     g_free (data->data);
1818   
1819   g_free (data);
1820 }
1821
1822 GType
1823 gtk_selection_data_get_type (void)
1824 {
1825   static GType our_type = 0;
1826   
1827   if (our_type == 0)
1828     our_type = g_boxed_type_register_static ("GtkTypeSelectionData",
1829                                              (GBoxedCopyFunc) gtk_selection_data_copy,
1830                                              (GBoxedFreeFunc) gtk_selection_data_free);
1831
1832   return our_type;
1833 }
1834
1835 static int 
1836 gtk_selection_bytes_per_item (gint format)
1837 {
1838   switch (format)
1839     {
1840     case 8:
1841       return sizeof (char);
1842       break;
1843     case 16:
1844       return sizeof (short);
1845       break;
1846     case 32:
1847       return sizeof (long);
1848       break;
1849     default:
1850       g_assert_not_reached();
1851     }
1852   return 0;
1853 }