]> Pileus Git - ~andy/gtk/blob - gtk/gtkselection.c
d3f963dfe328ef7c679b88c29c4d50c78c6d4952
[~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 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 
23  * same way.
24  *
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.
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 <config.h>
55 #include <stdarg.h>
56 #include <string.h>
57 #include "gdk.h"
58
59 #include "gtkalias.h"
60 #include "gtkmain.h"
61 #include "gtkselection.h"
62 #include "gdk-pixbuf/gdk-pixbuf.h"
63
64 #ifdef GDK_WINDOWING_X11
65 #include "x11/gdkx.h"
66 #endif
67
68 #undef DEBUG_SELECTION
69
70 /* Maximum size of a sent chunk, in bytes. Also the default size of
71    our buffers */
72 #ifdef GDK_WINDOWING_X11
73 #define GTK_SELECTION_MAX_SIZE(display)                                 \
74   MIN(262144,                                                           \
75       XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) == 0     \
76        ? XMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100         \
77        : XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100)
78 #else
79 /* No chunks on Win32 */
80 #define GTK_SELECTION_MAX_SIZE(display) G_MAXINT
81 #endif
82
83 #define IDLE_ABORT_TIME 30
84
85 enum {
86   INCR,
87   MULTIPLE,
88   TARGETS,
89   TIMESTAMP,
90   LAST_ATOM
91 };
92
93 typedef struct _GtkSelectionInfo GtkSelectionInfo;
94 typedef struct _GtkIncrConversion GtkIncrConversion;
95 typedef struct _GtkIncrInfo GtkIncrInfo;
96 typedef struct _GtkRetrievalInfo GtkRetrievalInfo;
97
98 struct _GtkSelectionInfo
99 {
100   GdkAtom        selection;
101   GtkWidget     *widget;        /* widget that owns selection */
102   guint32        time;          /* time used to acquire selection */
103   GdkDisplay    *display;       /* needed in gtk_selection_remove_all */    
104 };
105
106 struct _GtkIncrConversion 
107 {
108   GdkAtom           target;     /* Requested target */
109   GdkAtom           property;   /* Property to store in */
110   GtkSelectionData  data;       /* The data being supplied */
111   gint              offset;     /* Current offset in sent selection.
112                                  *  -1 => All done
113                                  *  -2 => Only the final (empty) portion
114                                  *        left to send */
115 };
116
117 struct _GtkIncrInfo
118 {
119   GdkWindow *requestor;         /* Requestor window - we create a GdkWindow
120                                    so we can receive events */
121   GdkAtom    selection;         /* Selection we're sending */
122   
123   GtkIncrConversion *conversions; /* Information about requested conversions -
124                                    * With MULTIPLE requests (benighted 1980's
125                                    * hardware idea), there can be more than
126                                    * one */
127   gint num_conversions;
128   gint num_incrs;               /* number of remaining INCR style transactions */
129   guint32 idle_time;
130 };
131
132
133 struct _GtkRetrievalInfo
134 {
135   GtkWidget *widget;
136   GdkAtom selection;            /* Selection being retrieved. */
137   GdkAtom target;               /* Form of selection that we requested */
138   guint32 idle_time;            /* Number of seconds since we last heard
139                                    from selection owner */
140   guchar   *buffer;             /* Buffer in which to accumulate results */
141   gint     offset;              /* Current offset in buffer, -1 indicates
142                                    not yet started */
143   guint32 notify_time;          /* Timestamp from SelectionNotify */
144 };
145
146 /* Local Functions */
147 static void gtk_selection_init              (void);
148 static gint gtk_selection_incr_timeout      (GtkIncrInfo      *info);
149 static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info);
150 static void gtk_selection_retrieval_report  (GtkRetrievalInfo *info,
151                                              GdkAtom           type,
152                                              gint              format,
153                                              guchar           *buffer,
154                                              gint              length,
155                                              guint32           time);
156 static void gtk_selection_invoke_handler    (GtkWidget        *widget,
157                                              GtkSelectionData *data,
158                                              guint             time);
159 static void gtk_selection_default_handler   (GtkWidget        *widget,
160                                              GtkSelectionData *data);
161 static int  gtk_selection_bytes_per_item    (gint              format);
162
163 /* Local Data */
164 static gint initialize = TRUE;
165 static GList *current_retrievals = NULL;
166 static GList *current_incrs = NULL;
167 static GList *current_selections = NULL;
168
169 static GdkAtom gtk_selection_atoms[LAST_ATOM];
170 static const char gtk_selection_handler_key[] = "gtk-selection-handlers";
171
172 /****************
173  * Target Lists *
174  ****************/
175
176 /*
177  * Target lists
178  */
179
180 GtkTargetList *
181 gtk_target_list_new (const GtkTargetEntry *targets,
182                      guint                 ntargets)
183 {
184   GtkTargetList *result = g_new (GtkTargetList, 1);
185   result->list = NULL;
186   result->ref_count = 1;
187
188   if (targets)
189     gtk_target_list_add_table (result, targets, ntargets);
190   
191   return result;
192 }
193
194 void               
195 gtk_target_list_ref (GtkTargetList *list)
196 {
197   g_return_if_fail (list != NULL);
198
199   list->ref_count++;
200 }
201
202 void               
203 gtk_target_list_unref (GtkTargetList *list)
204 {
205   g_return_if_fail (list != NULL);
206   g_return_if_fail (list->ref_count > 0);
207
208   list->ref_count--;
209   if (list->ref_count == 0)
210     {
211       GList *tmp_list = list->list;
212       while (tmp_list)
213         {
214           GtkTargetPair *pair = tmp_list->data;
215           g_free (pair);
216
217           tmp_list = tmp_list->next;
218         }
219       
220       g_list_free (list->list);
221       g_free (list);
222     }
223 }
224
225 void 
226 gtk_target_list_add (GtkTargetList *list,
227                      GdkAtom        target,
228                      guint          flags,
229                      guint          info)
230 {
231   GtkTargetPair *pair;
232
233   g_return_if_fail (list != NULL);
234   
235   pair = g_new (GtkTargetPair, 1);
236   pair->target = target;
237   pair->flags = flags;
238   pair->info = info;
239
240   list->list = g_list_append (list->list, pair);
241 }
242
243 static GdkAtom utf8_atom;
244 static GdkAtom text_atom;
245 static GdkAtom ctext_atom;
246 static GdkAtom text_plain_atom;
247 static GdkAtom text_plain_utf8_atom;
248 static GdkAtom text_plain_locale_atom;
249 static GdkAtom text_uri_list_atom;
250
251 static void 
252 init_atoms (void)
253 {
254   gchar *tmp;
255   const gchar *charset;
256
257   if (!utf8_atom)
258     {
259       utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
260       text_atom = gdk_atom_intern ("TEXT", FALSE);
261       ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
262       text_plain_atom = gdk_atom_intern ("text/plain", FALSE);
263       text_plain_utf8_atom = gdk_atom_intern ("text/plain;charset=utf-8", FALSE);
264       g_get_charset (&charset);
265       tmp = g_strdup_printf ("text/plain;charset=%s", charset);
266       text_plain_locale_atom = gdk_atom_intern (tmp, FALSE);
267       g_free (tmp);
268
269       text_uri_list_atom = gdk_atom_intern ("text/uri-list", FALSE);
270     }
271 }
272
273 /**
274  * gtk_target_list_add_text_targets:
275  * @list: a #GtkTargetList
276  * @info: an ID that will be passed back to the application
277  * 
278  * Adds the text targets supported by #GtkSelection to
279  * the target list. All targets are added with the same @info.
280  * 
281  * Since: 2.6
282  **/
283 void 
284 gtk_target_list_add_text_targets (GtkTargetList *list,
285                                   guint          info)
286 {
287   g_return_if_fail (list != NULL);
288   
289   init_atoms ();
290
291   /* Keep in sync with gtk_selection_data_targets_include_text()
292    */
293   gtk_target_list_add (list, utf8_atom, 0, info);  
294   gtk_target_list_add (list, ctext_atom, 0, info);  
295   gtk_target_list_add (list, text_atom, 0, info);  
296   gtk_target_list_add (list, GDK_TARGET_STRING, 0, info);  
297   gtk_target_list_add (list, text_plain_utf8_atom, 0, info);  
298   gtk_target_list_add (list, text_plain_locale_atom, 0, info);  
299   gtk_target_list_add (list, text_plain_atom, 0, info);  
300 }
301
302 /**
303  * gtk_target_list_add_image_targets:
304  * @list: a #GtkTargetList
305  * @info: an ID that will be passed back to the application
306  * @writable: whether to add only targets for which GTK+ knows
307  *   how to convert a pixbuf into the format
308  * 
309  * Adds the image targets supported by #GtkSelection to
310  * the target list. All targets are added with the same @info.
311  * 
312  * Since: 2.6
313  **/
314 void 
315 gtk_target_list_add_image_targets (GtkTargetList *list,
316                                    guint          info,
317                                    gboolean       writable)
318 {
319   GSList *formats, *f;
320   gchar **mimes, **m;
321   GdkAtom atom;
322
323   g_return_if_fail (list != NULL);
324
325   formats = gdk_pixbuf_get_formats ();
326
327   for (f = formats; f; f = f->next)
328     {
329       GdkPixbufFormat *fmt = f->data;
330
331       if (writable && !gdk_pixbuf_format_is_writable (fmt))
332         continue;
333       
334       mimes = gdk_pixbuf_format_get_mime_types (fmt);
335       for (m = mimes; *m; m++)
336         {
337           atom = gdk_atom_intern (*m, FALSE);
338           gtk_target_list_add (list, atom, 0, info);  
339         }
340       g_strfreev (mimes);
341     }
342
343   g_slist_free (formats);
344 }
345
346 /**
347  * gtk_target_list_add_uri_targets:
348  * @list: a #GtkTargetList
349  * @info: an ID that will be passed back to the application
350  * 
351  * Adds the URI targets supported by #GtkSelection to
352  * the target list. All targets are added with the same @info.
353  * 
354  * Since: 2.6
355  **/
356 void 
357 gtk_target_list_add_uri_targets (GtkTargetList *list,
358                                  guint          info)
359 {
360   g_return_if_fail (list != NULL);
361   
362   init_atoms ();
363
364   gtk_target_list_add (list, text_uri_list_atom, 0, info);  
365 }
366
367 void               
368 gtk_target_list_add_table (GtkTargetList        *list,
369                            const GtkTargetEntry *targets,
370                            guint                 ntargets)
371 {
372   gint i;
373
374   for (i=ntargets-1; i >= 0; i--)
375     {
376       GtkTargetPair *pair = g_new (GtkTargetPair, 1);
377       pair->target = gdk_atom_intern (targets[i].target, FALSE);
378       pair->flags = targets[i].flags;
379       pair->info = targets[i].info;
380       
381       list->list = g_list_prepend (list->list, pair);
382     }
383 }
384
385 void 
386 gtk_target_list_remove (GtkTargetList *list,
387                         GdkAtom            target)
388 {
389   GList *tmp_list;
390
391   g_return_if_fail (list != NULL);
392
393   tmp_list = list->list;
394   while (tmp_list)
395     {
396       GtkTargetPair *pair = tmp_list->data;
397       
398       if (pair->target == target)
399         {
400           g_free (pair);
401
402           list->list = g_list_remove_link (list->list, tmp_list);
403           g_list_free_1 (tmp_list);
404
405           return;
406         }
407       
408       tmp_list = tmp_list->next;
409     }
410 }
411
412 gboolean
413 gtk_target_list_find (GtkTargetList *list,
414                       GdkAtom        target,
415                       guint         *info)
416 {
417   GList *tmp_list = list->list;
418   while (tmp_list)
419     {
420       GtkTargetPair *pair = tmp_list->data;
421
422       if (pair->target == target)
423         {
424           *info = pair->info;
425           return TRUE;
426         }
427       tmp_list = tmp_list->next;
428     }
429
430   return FALSE;
431 }
432
433 /**
434  * gtk_selection_owner_set_for_display:
435  * @display: the #Gdkdisplay where the selection is set 
436  * @widget: new selection owner (a #GdkWidget), or %NULL.
437  * @selection: an interned atom representing the selection to claim.
438  * @time_: timestamp with which to claim the selection
439  *
440  * Claim ownership of a given selection for a particular widget, or,
441  * if @widget is %NULL, release ownership of the selection.
442  *
443  * Return value: TRUE if the operation succeeded 
444  * 
445  * Since: 2.2
446  */
447 gboolean
448 gtk_selection_owner_set_for_display (GdkDisplay   *display,
449                                      GtkWidget    *widget,
450                                      GdkAtom       selection,
451                                      guint32       time)
452 {
453   GList *tmp_list;
454   GtkWidget *old_owner;
455   GtkSelectionInfo *selection_info = NULL;
456   GdkWindow *window;
457
458   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
459   g_return_val_if_fail (selection != GDK_NONE, FALSE);
460   g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
461   g_return_val_if_fail (widget == NULL || gtk_widget_get_display (widget) == display, FALSE);
462   
463   if (widget == NULL)
464     window = NULL;
465   else
466     window = widget->window;
467
468   tmp_list = current_selections;
469   while (tmp_list)
470     {
471       if (((GtkSelectionInfo *)tmp_list->data)->selection == selection)
472         {
473           selection_info = tmp_list->data;
474           break;
475         }
476       
477       tmp_list = tmp_list->next;
478     }
479   
480   if (gdk_selection_owner_set_for_display (display, window, selection, time, TRUE))
481     {
482       old_owner = NULL;
483       
484       if (widget == NULL)
485         {
486           if (selection_info)
487             {
488               old_owner = selection_info->widget;
489               current_selections = g_list_remove_link (current_selections,
490                                                        tmp_list);
491               g_list_free (tmp_list);
492               g_free (selection_info);
493             }
494         }
495       else
496         {
497           if (selection_info == NULL)
498             {
499               selection_info = g_new (GtkSelectionInfo, 1);
500               selection_info->selection = selection;
501               selection_info->widget = widget;
502               selection_info->time = time;
503               selection_info->display = display;
504               current_selections = g_list_prepend (current_selections,
505                                                    selection_info);
506             }
507           else
508             {
509               old_owner = selection_info->widget;
510               selection_info->widget = widget;
511               selection_info->time = time;
512               selection_info->display = display;
513             }
514         }
515       /* If another widget in the application lost the selection,
516        *  send it a GDK_SELECTION_CLEAR event.
517        */
518       if (old_owner && old_owner != widget)
519         {
520           GdkEvent *event = gdk_event_new (GDK_SELECTION_CLEAR);
521           
522           event->selection.window = g_object_ref (old_owner->window);
523           event->selection.selection = selection;
524           event->selection.time = time;
525           
526           gtk_widget_event (old_owner, event);
527
528           gdk_event_free (event);
529         }
530       return TRUE;
531     }
532   else
533     return FALSE;
534 }
535
536 /**
537  * gtk_selection_owner_set:
538  * @widget:  a #GtkWidget, or %NULL.
539  * @selection:  an interned atom representing the selection to claim
540  * @time_: timestamp with which to claim the selection
541  * 
542  * Claims ownership of a given selection for a particular widget,
543  * or, if @widget is %NULL, release ownership of the selection.
544  * 
545  * Return value: %TRUE if the operation succeeded
546  **/
547 gboolean
548 gtk_selection_owner_set (GtkWidget *widget,
549                          GdkAtom    selection,
550                          guint32    time)
551 {
552   GdkDisplay *display;
553   
554   g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
555   g_return_val_if_fail (selection != GDK_NONE, FALSE);
556
557   if (widget)
558     display = gtk_widget_get_display (widget);
559   else
560     {
561       GTK_NOTE (MULTIHEAD,
562                 g_warning ("gtk_selection_owner_set (NULL,...) is not multihead safe"));
563                  
564       display = gdk_display_get_default ();
565     }
566   
567   return gtk_selection_owner_set_for_display (display, widget,
568                                               selection, time);
569 }
570
571 /*************************************************************
572  * gtk_selection_add_target
573  *     Add specified target to list of supported targets
574  *
575  *   arguments:
576  *     widget:     The widget for which this target applies
577  *     selection:
578  *     target:
579  *     info:       guint to pass to to the selection_get signal 
580  *
581  *   results:
582  *************************************************************/
583
584 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
585
586 struct _GtkSelectionTargetList {
587   GdkAtom selection;
588   GtkTargetList *list;
589 };
590
591 static GtkTargetList *
592 gtk_selection_target_list_get (GtkWidget    *widget,
593                                GdkAtom       selection)
594 {
595   GtkSelectionTargetList *sellist;
596   GList *tmp_list;
597   GList *lists;
598
599   lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
600   
601   tmp_list = lists;
602   while (tmp_list)
603     {
604       sellist = tmp_list->data;
605       if (sellist->selection == selection)
606         return sellist->list;
607       tmp_list = tmp_list->next;
608     }
609
610   sellist = g_new (GtkSelectionTargetList, 1);
611   sellist->selection = selection;
612   sellist->list = gtk_target_list_new (NULL, 0);
613
614   lists = g_list_prepend (lists, sellist);
615   g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
616
617   return sellist->list;
618 }
619
620 static void
621 gtk_selection_target_list_remove (GtkWidget    *widget)
622 {
623   GtkSelectionTargetList *sellist;
624   GList *tmp_list;
625   GList *lists;
626
627   lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
628   
629   tmp_list = lists;
630   while (tmp_list)
631     {
632       sellist = tmp_list->data;
633
634       gtk_target_list_unref (sellist->list);
635
636       g_free (sellist);
637       tmp_list = tmp_list->next;
638     }
639
640   g_list_free (lists);
641   g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, NULL);
642 }
643
644 /**
645  * gtk_selection_clear_targets:
646  * @widget:    a #GtkWidget
647  * @selection: an atom representing a selection
648  *
649  * Remove all targets registered for the given selection for the
650  * widget.
651  **/
652 void 
653 gtk_selection_clear_targets (GtkWidget *widget,
654                              GdkAtom    selection)
655 {
656   GtkSelectionTargetList *sellist;
657   GList *tmp_list;
658   GList *lists;
659
660   g_return_if_fail (GTK_IS_WIDGET (widget));
661   g_return_if_fail (selection != GDK_NONE);
662
663   lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);
664   
665   tmp_list = lists;
666   while (tmp_list)
667     {
668       sellist = tmp_list->data;
669       if (sellist->selection == selection)
670         {
671           lists = g_list_delete_link (lists, tmp_list);
672           gtk_target_list_unref (sellist->list);
673           g_free (sellist);
674
675           break;
676         }
677       
678       tmp_list = tmp_list->next;
679     }
680   
681   g_object_set_data (G_OBJECT (widget), gtk_selection_handler_key, lists);
682 }
683
684 void 
685 gtk_selection_add_target (GtkWidget         *widget, 
686                           GdkAtom            selection,
687                           GdkAtom            target,
688                           guint              info)
689 {
690   GtkTargetList *list;
691
692   g_return_if_fail (GTK_IS_WIDGET (widget));
693   g_return_if_fail (selection != GDK_NONE);
694
695   list = gtk_selection_target_list_get (widget, selection);
696   gtk_target_list_add (list, target, 0, info);
697 }
698
699 void 
700 gtk_selection_add_targets (GtkWidget            *widget, 
701                            GdkAtom               selection,
702                            const GtkTargetEntry *targets,
703                            guint                 ntargets)
704 {
705   GtkTargetList *list;
706
707   g_return_if_fail (GTK_IS_WIDGET (widget));
708   g_return_if_fail (selection != GDK_NONE);
709   g_return_if_fail (targets != NULL);
710   
711   list = gtk_selection_target_list_get (widget, selection);
712   gtk_target_list_add_table (list, targets, ntargets);
713 }
714
715
716 /*************************************************************
717  * gtk_selection_remove_all:
718  *     Removes all handlers and unsets ownership of all 
719  *     selections for a widget. Called when widget is being
720  *     destroyed
721  *     
722  *   arguments:
723  *     widget:    The widget
724  *   results:
725  *************************************************************/
726
727 void
728 gtk_selection_remove_all (GtkWidget *widget)
729 {
730   GList *tmp_list;
731   GList *next;
732   GtkSelectionInfo *selection_info;
733   
734   /* Remove pending requests/incrs for this widget */
735   
736   tmp_list = current_retrievals;
737   while (tmp_list)
738     {
739       next = tmp_list->next;
740       if (((GtkRetrievalInfo *)tmp_list->data)->widget == widget)
741         {
742           current_retrievals = g_list_remove_link (current_retrievals, 
743                                                    tmp_list);
744           /* structure will be freed in timeout */
745           g_list_free (tmp_list);
746         }
747       tmp_list = next;
748     }
749   
750   /* Disclaim ownership of any selections */
751   
752   tmp_list = current_selections;
753   while (tmp_list)
754     {
755       next = tmp_list->next;
756       selection_info = (GtkSelectionInfo *)tmp_list->data;
757       
758       if (selection_info->widget == widget)
759         {       
760           gdk_selection_owner_set_for_display (selection_info->display,
761                                                NULL, 
762                                                selection_info->selection,
763                                                GDK_CURRENT_TIME, FALSE);
764           current_selections = g_list_remove_link (current_selections,
765                                                    tmp_list);
766           g_list_free (tmp_list);
767           g_free (selection_info);
768         }
769       
770       tmp_list = next;
771     }
772
773   /* Remove all selection lists */
774   gtk_selection_target_list_remove (widget);
775 }
776
777 /*************************************************************
778  * gtk_selection_convert:
779  *     Request the contents of a selection. When received, 
780  *     a "selection_received" signal will be generated.
781  *
782  *   arguments:
783  *     widget:     The widget which acts as requestor
784  *     selection:  Which selection to get
785  *     target:     Form of information desired (e.g., STRING)
786  *     time:       Time of request (usually of triggering event)
787  *                 In emergency, you could use GDK_CURRENT_TIME
788  *
789  *   results:
790  *     TRUE if requested succeeded. FALSE if we could not process
791  *     request. (e.g., there was already a request in process for
792  *     this widget). 
793  *************************************************************/
794
795 gboolean
796 gtk_selection_convert (GtkWidget *widget, 
797                        GdkAtom    selection, 
798                        GdkAtom    target,
799                        guint32    time)
800 {
801   GtkRetrievalInfo *info;
802   GList *tmp_list;
803   GdkWindow *owner_window;
804   GdkDisplay *display;
805   
806   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
807   g_return_val_if_fail (selection != GDK_NONE, FALSE);
808   
809   if (initialize)
810     gtk_selection_init ();
811   
812   if (!GTK_WIDGET_REALIZED (widget))
813     gtk_widget_realize (widget);
814   
815   /* Check to see if there are already any retrievals in progress for
816      this widget. If we changed GDK to use the selection for the 
817      window property in which to store the retrieved information, then
818      we could support multiple retrievals for different selections.
819      This might be useful for DND. */
820   
821   tmp_list = current_retrievals;
822   while (tmp_list)
823     {
824       info = (GtkRetrievalInfo *)tmp_list->data;
825       if (info->widget == widget)
826         return FALSE;
827       tmp_list = tmp_list->next;
828     }
829   
830   info = g_new (GtkRetrievalInfo, 1);
831   
832   info->widget = widget;
833   info->selection = selection;
834   info->target = target;
835   info->idle_time = 0;
836   info->buffer = NULL;
837   info->offset = -1;
838   
839   /* Check if this process has current owner. If so, call handler
840      procedure directly to avoid deadlocks with INCR. */
841
842   display = gtk_widget_get_display (widget);
843   owner_window = gdk_selection_owner_get_for_display (display, selection);
844   
845   if (owner_window != NULL)
846     {
847       GtkWidget *owner_widget;
848       GtkSelectionData selection_data;
849       
850       selection_data.selection = selection;
851       selection_data.target = target;
852       selection_data.data = NULL;
853       selection_data.length = -1;
854       selection_data.display = display;
855       
856       gdk_window_get_user_data (owner_window, (gpointer *)&owner_widget);
857       
858       if (owner_widget != NULL)
859         {
860           gtk_selection_invoke_handler (owner_widget, 
861                                         &selection_data,
862                                         time);
863           
864           gtk_selection_retrieval_report (info,
865                                           selection_data.type, 
866                                           selection_data.format,
867                                           selection_data.data,
868                                           selection_data.length,
869                                           time);
870           
871           g_free (selection_data.data);
872           
873           g_free (info);
874           return TRUE;
875         }
876     }
877   
878   /* Otherwise, we need to go through X */
879   
880   current_retrievals = g_list_append (current_retrievals, info);
881   gdk_selection_convert (widget->window, selection, target, time);
882   g_timeout_add (1000, (GSourceFunc) gtk_selection_retrieval_timeout, info);
883   
884   return TRUE;
885 }
886
887 /*************************************************************
888  * gtk_selection_data_set:
889  *     Store new data into a GtkSelectionData object. Should
890  *     _only_ by called from a selection handler callback.
891  *     Null terminates the stored data.
892  *   arguments:
893  *     type:    the type of selection data
894  *     format:  format (number of bits in a unit)
895  *     data:    pointer to the data (will be copied)
896  *     length:  length of the data
897  *   results:
898  *************************************************************/
899
900 void 
901 gtk_selection_data_set (GtkSelectionData *selection_data,
902                         GdkAtom           type,
903                         gint              format,
904                         const guchar     *data,
905                         gint              length)
906 {
907   if (selection_data->data)
908     g_free (selection_data->data);
909   
910   selection_data->type = type;
911   selection_data->format = format;
912   
913   if (data)
914     {
915       selection_data->data = g_new (guchar, length+1);
916       memcpy (selection_data->data, data, length);
917       selection_data->data[length] = 0;
918     }
919   else
920     {
921       g_return_if_fail (length <= 0);
922       
923       if (length < 0)
924         selection_data->data = NULL;
925       else
926         selection_data->data = g_strdup("");
927     }
928   
929   selection_data->length = length;
930 }
931
932 static gboolean
933 selection_set_string (GtkSelectionData *selection_data,
934                       const gchar      *str,
935                       gint              len)
936 {
937   gchar *tmp = g_strndup (str, len);
938   gchar *latin1 = gdk_utf8_to_string_target (tmp);
939   g_free (tmp);
940   
941   if (latin1)
942     {
943       gtk_selection_data_set (selection_data,
944                               GDK_SELECTION_TYPE_STRING,
945                               8, latin1, strlen (latin1));
946       g_free (latin1);
947       
948       return TRUE;
949     }
950   else
951     return FALSE;
952 }
953
954 static gboolean
955 selection_set_compound_text (GtkSelectionData *selection_data,
956                              const gchar      *str,
957                              gint              len)
958 {
959   gchar *tmp;
960   guchar *text;
961   GdkAtom encoding;
962   gint format;
963   gint new_length;
964   gboolean result = FALSE;
965   
966   tmp = g_strndup (str, len);
967   if (gdk_utf8_to_compound_text_for_display (selection_data->display, tmp,
968                                              &encoding, &format, &text, &new_length))
969     {
970       gtk_selection_data_set (selection_data, encoding, format, text, new_length);
971       gdk_free_compound_text (text);
972       
973       result = TRUE;
974     }
975
976   g_free (tmp);
977
978   return result;
979 }
980
981 /* Normalize \r and \n into \r\n
982  */
983 static gchar *
984 normalize_to_crlf (const gchar *str, 
985                    gint         len)
986 {
987   GString *result = g_string_sized_new (len);
988   const gchar *p = str;
989
990   while (1)
991     {
992       if (*p == '\n')
993         g_string_append_c (result, '\r');
994
995       if (*p == '\r')
996         {
997           g_string_append_c (result, *p);
998           p++;
999           if (*p != '\n')
1000             g_string_append_c (result, '\n');
1001         }
1002
1003       if (*p == '\0')
1004         break;
1005
1006       g_string_append_c (result, *p);
1007       p++;
1008     }
1009
1010   return g_string_free (result, FALSE);  
1011 }
1012
1013 /* Normalize \r and \r\n into \n
1014  */
1015 static gchar *
1016 normalize_to_lf (gchar *str, 
1017                  gint   len)
1018 {
1019   GString *result = g_string_sized_new (len);
1020   const gchar *p = str;
1021
1022   while (1)
1023     {
1024       if (*p == '\r')
1025         {
1026           p++;
1027           if (*p != '\n')
1028             g_string_append_c (result, '\n');
1029         }
1030
1031       if (*p == '\0')
1032         break;
1033
1034       g_string_append_c (result, *p);
1035       p++;
1036     }
1037
1038   return g_string_free (result, FALSE);  
1039 }
1040
1041 static gboolean
1042 selection_set_text_plain (GtkSelectionData *selection_data,
1043                           const gchar      *str,
1044                           gint              len)
1045 {
1046   const gchar *charset = NULL;
1047   gchar *result;
1048   GError *error = NULL;
1049
1050   result = normalize_to_crlf (str, len);
1051   if (selection_data->target == text_plain_atom)
1052     charset = "ASCII";
1053   else if (selection_data->target == text_plain_locale_atom)
1054     g_get_charset (&charset);
1055
1056   if (charset)
1057     {
1058       gchar *tmp = result;
1059       result = g_convert_with_fallback (tmp, -1, 
1060                                         charset, "UTF-8", 
1061                                         NULL, NULL, NULL, &error);
1062       g_free (tmp);
1063     }
1064
1065   if (!result)
1066     {
1067       g_warning ("Error converting from UTF-8 to %s: %s",
1068                  charset, error->message);
1069       g_error_free (error);
1070       
1071       return FALSE;
1072     }
1073   
1074   gtk_selection_data_set (selection_data,
1075                           selection_data->target, 
1076                           8, result, strlen (result));
1077   g_free (result);
1078   
1079   return TRUE;
1080 }
1081
1082 static gchar *
1083 selection_get_text_plain (GtkSelectionData *selection_data)
1084 {
1085   const gchar *charset = NULL;
1086   gchar *str, *result;
1087   gsize len;
1088   GError *error = NULL;
1089
1090   str = g_strdup (selection_data->data);
1091   len = selection_data->length;
1092   
1093   if (selection_data->type == text_plain_atom)
1094     charset = "ISO-8859-1";
1095   else if (selection_data->type == text_plain_locale_atom)
1096     g_get_charset (&charset);
1097
1098   if (charset)
1099     {
1100       gchar *tmp = str;
1101       str = g_convert_with_fallback (tmp, len, 
1102                                      charset, "UTF-8", 
1103                                      NULL, NULL, &len, &error);
1104       g_free (tmp);
1105
1106       if (!str)
1107         {
1108           g_warning ("Error converting from %s to UTF-8: %s",
1109                       charset, error->message);
1110           g_error_free (error);
1111
1112           return NULL;
1113         }
1114     }
1115   else if (!g_utf8_validate (str, -1, NULL))
1116     {
1117       g_warning ("Error converting from text/plain;charset=utf-8 to UTF-8");
1118       g_free (str);
1119
1120       return NULL;
1121     }
1122
1123   result = normalize_to_lf (str, len);
1124   g_free (str);
1125
1126   return result;
1127 }
1128
1129 /**
1130  * gtk_selection_data_set_text:
1131  * @selection_data: a #GtkSelectionData
1132  * @str: a UTF-8 string
1133  * @len: the length of @str, or -1 if @str is nul-terminated.
1134  * 
1135  * Sets the contents of the selection from a UTF-8 encoded string.
1136  * The string is converted to the form determined by
1137  * @selection_data->target.
1138  * 
1139  * Return value: %TRUE if the selection was successfully set,
1140  *   otherwise %FALSE.
1141  **/
1142 gboolean
1143 gtk_selection_data_set_text (GtkSelectionData     *selection_data,
1144                              const gchar          *str,
1145                              gint                  len)
1146 {
1147   if (len < 0)
1148     len = strlen (str);
1149   
1150   init_atoms ();
1151
1152   if (selection_data->target == utf8_atom)
1153     {
1154       gtk_selection_data_set (selection_data,
1155                               utf8_atom,
1156                               8, (guchar *)str, len);
1157       return TRUE;
1158     }
1159   else if (selection_data->target == GDK_TARGET_STRING)
1160     {
1161       return selection_set_string (selection_data, str, len);
1162     }
1163   else if (selection_data->target == ctext_atom ||
1164            selection_data->target == text_atom)
1165     {
1166       if (selection_set_compound_text (selection_data, str, len))
1167         return TRUE;
1168       else if (selection_data->target == text_atom)
1169         return selection_set_string (selection_data, str, len);
1170     }
1171   else if (selection_data->target == text_plain_atom ||
1172            selection_data->target == text_plain_utf8_atom ||
1173            selection_data->target == text_plain_locale_atom)
1174     {
1175       return selection_set_text_plain (selection_data, str, len);
1176     }
1177
1178   return FALSE;
1179 }
1180
1181 /**
1182  * gtk_selection_data_get_text:
1183  * @selection_data: a #GtkSelectionData
1184  * 
1185  * Gets the contents of the selection data as a UTF-8 string.
1186  * 
1187  * Return value: if the selection data contained a recognized
1188  *   text type and it could be converted to UTF-8, a newly allocated
1189  *   string containing the converted text, otherwise %NULL.
1190  *   If the result is non-%NULL it must be freed with g_free().
1191  **/
1192 guchar *
1193 gtk_selection_data_get_text (GtkSelectionData *selection_data)
1194 {
1195   guchar *result = NULL;
1196
1197   init_atoms ();
1198   
1199   if (selection_data->length >= 0 &&
1200       (selection_data->type == GDK_TARGET_STRING ||
1201        selection_data->type == ctext_atom ||
1202        selection_data->type == utf8_atom))
1203     {
1204       gchar **list;
1205       gint i;
1206       gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
1207                                                                selection_data->type,
1208                                                                selection_data->format, 
1209                                                                selection_data->data,
1210                                                                selection_data->length,
1211                                                                &list);
1212       if (count > 0)
1213         result = list[0];
1214
1215       for (i = 1; i < count; i++)
1216         g_free (list[i]);
1217       g_free (list);
1218     }
1219   else if (selection_data->length >= 0 &&
1220            (selection_data->type == text_plain_atom ||
1221             selection_data->type == text_plain_utf8_atom ||
1222             selection_data->type == text_plain_locale_atom))
1223     {
1224       result = selection_get_text_plain (selection_data);
1225     }
1226
1227   return result;
1228 }
1229
1230 /**
1231  * gtk_selection_data_set_pixbuf:
1232  * @selection_data: a #GtkSelectionData
1233  * @pixbuf: a #GdkPixbuf
1234  * 
1235  * Sets the contents of the selection from a #GdkPixbuf
1236  * The pixbuf is converted to the form determined by
1237  * @selection_data->target.
1238  * 
1239  * Return value: %TRUE if the selection was successfully set,
1240  *   otherwise %FALSE.
1241  *
1242  * Since: 2.6
1243  **/
1244 gboolean
1245 gtk_selection_data_set_pixbuf (GtkSelectionData *selection_data,
1246                                GdkPixbuf        *pixbuf)
1247 {
1248   GSList *formats, *f;
1249   gchar **mimes, **m;
1250   GdkAtom atom;
1251   gboolean result;
1252   gchar *str, *type;
1253   gsize len;
1254
1255   formats = gdk_pixbuf_get_formats ();
1256
1257   for (f = formats; f; f = f->next)
1258     {
1259       GdkPixbufFormat *fmt = f->data;
1260
1261       mimes = gdk_pixbuf_format_get_mime_types (fmt);
1262       for (m = mimes; *m; m++)
1263         {
1264           atom = gdk_atom_intern (*m, FALSE);
1265           if (selection_data->target == atom)
1266             {
1267               str = NULL;
1268               type = gdk_pixbuf_format_get_name (fmt),
1269               result = gdk_pixbuf_save_to_buffer (pixbuf,
1270                                                   &str,
1271                                                   &len,
1272                                                   type,
1273                                                   NULL);
1274               if (result) 
1275                 gtk_selection_data_set (selection_data,
1276                                         atom, 8, (guchar *)str, len);
1277               g_free (type);
1278               g_free (str);
1279               g_strfreev (mimes);
1280               g_slist_free (formats);
1281               
1282               return result;
1283             }
1284         }
1285
1286       g_strfreev (mimes);
1287     }
1288
1289   g_slist_free (formats);
1290  
1291   return FALSE;
1292 }
1293
1294 /**
1295  * gtk_selection_data_get_pixbuf:
1296  * @selection_data: a #GtkSelectionData
1297  * 
1298  * Gets the contents of the selection data as a #GdkPixbuf.
1299  * 
1300  * Return value: if the selection data contained a recognized
1301  *   image type and it could be converted to a #GdkPixbuf, a 
1302  *   newly allocated pixbuf is returned, otherwise %NULL.
1303  *   If the result is non-%NULL it must be freed with g_object_unref().
1304  *
1305  * Since: 2.6
1306  **/
1307 GdkPixbuf *
1308 gtk_selection_data_get_pixbuf (GtkSelectionData *selection_data)
1309 {
1310   GdkPixbufLoader *loader;
1311   GdkPixbuf *result = NULL;
1312
1313   loader = gdk_pixbuf_loader_new ();
1314   
1315   if (gdk_pixbuf_loader_write (loader, 
1316                                selection_data->data,
1317                                selection_data->length,
1318                                NULL))
1319     result = gdk_pixbuf_loader_get_pixbuf (loader);
1320   
1321   if (result)
1322     g_object_ref (result);
1323
1324   gdk_pixbuf_loader_close (loader, NULL);
1325   g_object_unref (loader);
1326   
1327   return result;
1328 }
1329
1330 /**
1331  * gtk_selection_data_set_uris:
1332  * @selection_data: a #GtkSelectionData
1333  * @uris: a %NULL-terminated array of strings hilding URIs
1334  * 
1335  * Sets the contents of the selection from a list of URIs.
1336  * The string is converted to the form determined by
1337  * @selection_data->target.
1338  * 
1339  * Return value: %TRUE if the selection was successfully set,
1340  *   otherwise %FALSE.
1341  *
1342  * Since: 2.6
1343  **/
1344 gboolean
1345 gtk_selection_data_set_uris (GtkSelectionData  *selection_data,
1346                              gchar            **uris)
1347 {
1348   init_atoms ();
1349
1350   if (selection_data->target == text_uri_list_atom)
1351     {
1352       GString *list;
1353       gint i;
1354       gchar *result;
1355       gsize length;
1356       
1357       list = g_string_new (NULL);
1358       for (i = 0; uris[i]; i++)
1359         {
1360           g_string_append (list, uris[i]);
1361           g_string_append (list, "\r\n");
1362         }
1363
1364       result = g_convert (list->str, list->len,
1365                           "ASCII", "UTF-8", 
1366                           NULL, &length, NULL);
1367       g_string_free (list, TRUE);
1368       
1369       if (result)
1370         {
1371           gtk_selection_data_set (selection_data,
1372                                   text_uri_list_atom,
1373                                   8, (guchar *)result, length);
1374           
1375           return TRUE;
1376         }
1377     }
1378
1379   return FALSE;
1380 }
1381
1382 /**
1383  * gtk_selection_data_get_uris:
1384  * @selection_data: a #GtkSelectionData
1385  * 
1386  * Gets the contents of the selection data as array of URIs.
1387  * 
1388  * Return value: if the selection data contains a list of
1389  *   URIs, a newly allocated %NULL-terminated string array
1390  *   containing the URIs, otherwise %NULL. If the result is 
1391  *   non-%NULL it must be freed with g_strfreev().
1392  *
1393  * Since: 2.6
1394  **/
1395 gchar **
1396 gtk_selection_data_get_uris (GtkSelectionData *selection_data)
1397 {
1398   gchar **result = NULL;
1399
1400   init_atoms ();
1401   
1402   if (selection_data->length >= 0 &&
1403       selection_data->type == text_uri_list_atom)
1404     {
1405       gchar **list;
1406       gint i;
1407       gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
1408                                                                utf8_atom,
1409                                                                selection_data->format, 
1410                                                                selection_data->data,
1411                                                                selection_data->length,
1412                                                                &list);
1413       if (count > 0)
1414         result = g_uri_list_extract_uris (list[0]);
1415       
1416       for (i = 1; i < count; i++)
1417         g_free (list[i]);
1418       g_free (list);
1419     }
1420
1421   return result;
1422 }
1423
1424
1425 /**
1426  * gtk_selection_data_get_targets:
1427  * @selection_data: a #GtkSelectionData object
1428  * @targets: location to store an array of targets. The result
1429  *           stored here must be freed with g_free().
1430  * @n_atoms: location to store number of items in @targets.
1431  * 
1432  * Gets the contents of @selection_data as an array of targets.
1433  * This can be used to interpret the results of getting
1434  * the standard TARGETS target that is always supplied for
1435  * any selection.
1436  * 
1437  * Return value: %TRUE if @selection_data contains a valid
1438  *    array of targets, otherwise %FALSE.
1439  **/
1440 gboolean
1441 gtk_selection_data_get_targets (GtkSelectionData  *selection_data,
1442                                 GdkAtom          **targets,
1443                                 gint              *n_atoms)
1444 {
1445   if (selection_data->length >= 0 &&
1446       selection_data->format == 32 &&
1447       selection_data->type == GDK_SELECTION_TYPE_ATOM)
1448     {
1449       if (targets)
1450         *targets = g_memdup (selection_data->data, selection_data->length);
1451       if (n_atoms)
1452         *n_atoms = selection_data->length / sizeof (GdkAtom);
1453
1454       return TRUE;
1455     }
1456   else
1457     {
1458       if (targets)
1459         *targets = NULL;
1460       if (n_atoms)
1461         *n_atoms = -1;
1462
1463       return FALSE;
1464     }
1465 }
1466
1467 /**
1468  * gtk_selection_data_targets_include_text:
1469  * @selection_data: a #GtkSelectionData object
1470  * 
1471  * Given a #GtkSelectionData object holding a list of targets,
1472  * determines if any of the targets in @targets can be used to
1473  * provide text.
1474  * 
1475  * Return value: %TRUE if @selection_data holds a list of targets,
1476  *   and a suitable target for text is included, otherwise %FALSE.
1477  **/
1478 gboolean
1479 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
1480 {
1481   GdkAtom *targets;
1482   gint n_targets;
1483   gint i;
1484   gboolean result = FALSE;
1485
1486   init_atoms ();
1487
1488   if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
1489     {
1490       for (i=0; i < n_targets; i++)
1491         {
1492           if (targets[i] == utf8_atom ||
1493               targets[i] == text_atom ||
1494               targets[i] == GDK_TARGET_STRING ||
1495               targets[i] == ctext_atom ||
1496               targets[i] == text_plain_atom ||
1497               targets[i] == text_plain_utf8_atom ||
1498               targets[i] == text_plain_locale_atom)
1499             {
1500               result = TRUE;
1501               break;
1502             }
1503         }
1504
1505       g_free (targets);
1506     }
1507
1508   return result;
1509 }
1510
1511 /**
1512  * gtk_selection_data_targets_include_image:
1513  * @selection_data: a #GtkSelectionData object
1514  * @writable: whether to accept only targets for which GTK+ knows
1515  *   how to convert a pixbuf into the format
1516  * 
1517  * Given a #GtkSelectionData object holding a list of targets,
1518  * determines if any of the targets in @targets can be used to
1519  * provide a #GdkPixbuf.
1520  * 
1521  * Return value: %TRUE if @selection_data holds a list of targets,
1522  *   and a suitable target for images is included, otherwise %FALSE.
1523  *
1524  * Since: 2.6
1525  **/
1526 gboolean 
1527 gtk_selection_data_targets_include_image (GtkSelectionData *selection_data,
1528                                           gboolean          writable)
1529 {
1530   GdkAtom *targets;
1531   gint n_targets;
1532   gint i;
1533   gboolean result = FALSE;
1534   GtkTargetList *list;
1535   GList *l;
1536
1537   init_atoms ();
1538
1539   if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
1540     {
1541       list = gtk_target_list_new (NULL, 0);
1542       gtk_target_list_add_image_targets (list, 0, writable);
1543       for (i=0; i < n_targets && !result; i++)
1544         {
1545           for (l = list->list; l && !result; l = l->next)
1546             {
1547               GtkTargetPair *pair = (GtkTargetPair *)l->data;
1548               if (pair->target == targets[i])
1549                 result = TRUE;
1550             }
1551         }
1552       gtk_target_list_unref (list);
1553       g_free (targets);
1554     }
1555
1556   return result;
1557 }
1558           
1559 /*************************************************************
1560  * gtk_selection_init:
1561  *     Initialize local variables
1562  *   arguments:
1563  *     
1564  *   results:
1565  *************************************************************/
1566
1567 static void
1568 gtk_selection_init (void)
1569 {
1570   gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
1571   gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
1572   gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
1573   gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
1574
1575   initialize = FALSE;
1576 }
1577
1578 /**
1579  * gtk_selection_clear:
1580  * @widget: a #GtkWidget
1581  * @event: the event
1582  * 
1583  * The default handler for the GtkWidget::selection_clear_event
1584  * signal. 
1585  * 
1586  * Return value: %TRUE if the event was handled, otherwise false
1587  * 
1588  * Since: 2.2
1589  *
1590  * Deprecated: Instead of calling this function, chain up from
1591  * your selection_clear_event handler. Calling this function
1592  * from any other context is illegal. 
1593  **/
1594 gboolean
1595 gtk_selection_clear (GtkWidget         *widget,
1596                      GdkEventSelection *event)
1597 {
1598   /* Note that we filter clear events in gdkselection-x11.c, so
1599    * that we only will get here if the clear event actually
1600    * represents a change that we didn't do ourself.
1601    */
1602   GList *tmp_list;
1603   GtkSelectionInfo *selection_info = NULL;
1604   
1605   tmp_list = current_selections;
1606   while (tmp_list)
1607     {
1608       selection_info = (GtkSelectionInfo *)tmp_list->data;
1609       
1610       if ((selection_info->selection == event->selection) &&
1611           (selection_info->widget == widget))
1612         break;
1613       
1614       tmp_list = tmp_list->next;
1615     }
1616   
1617   if (tmp_list)
1618     {
1619       current_selections = g_list_remove_link (current_selections, tmp_list);
1620       g_list_free (tmp_list);
1621       g_free (selection_info);
1622     }
1623   
1624   return TRUE;
1625 }
1626
1627
1628 /*************************************************************
1629  * _gtk_selection_request:
1630  *     Handler for "selection_request_event" 
1631  *   arguments:
1632  *     widget:
1633  *     event:
1634  *   results:
1635  *************************************************************/
1636
1637 gboolean
1638 _gtk_selection_request (GtkWidget *widget,
1639                         GdkEventSelection *event)
1640 {
1641   GdkDisplay *display = gtk_widget_get_display (widget);
1642   GtkIncrInfo *info;
1643   GList *tmp_list;
1644   int i;
1645   gulong selection_max_size;
1646
1647   if (initialize)
1648     gtk_selection_init ();
1649   
1650   selection_max_size = GTK_SELECTION_MAX_SIZE (display);
1651
1652   /* Check if we own selection */
1653   
1654   tmp_list = current_selections;
1655   while (tmp_list)
1656     {
1657       GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1658       
1659       if ((selection_info->selection == event->selection) &&
1660           (selection_info->widget == widget))
1661         break;
1662       
1663       tmp_list = tmp_list->next;
1664     }
1665   
1666   if (tmp_list == NULL)
1667     return FALSE;
1668   
1669   info = g_new (GtkIncrInfo, 1);
1670
1671   g_object_ref (widget);
1672   
1673   info->selection = event->selection;
1674   info->num_incrs = 0;
1675   
1676   /* Create GdkWindow structure for the requestor */
1677   
1678   info->requestor = gdk_window_lookup_for_display (display,
1679                                                    event->requestor);
1680   if (!info->requestor)
1681     info->requestor = gdk_window_foreign_new_for_display (display,
1682                                                           event->requestor);
1683   
1684   /* Determine conversions we need to perform */
1685   
1686   if (event->target == gtk_selection_atoms[MULTIPLE])
1687     {
1688       GdkAtom  type;
1689       guchar  *mult_atoms;
1690       gint     format;
1691       gint     length;
1692       
1693       mult_atoms = NULL;
1694       
1695       gdk_error_trap_push ();
1696       if (!gdk_property_get (info->requestor, event->property, GDK_NONE, /* AnyPropertyType */
1697                              0, selection_max_size, FALSE,
1698                              &type, &format, &length, &mult_atoms))
1699         {
1700           gdk_selection_send_notify_for_display (display,
1701                                                  event->requestor, 
1702                                                  event->selection,
1703                                                  event->target, 
1704                                                  GDK_NONE, 
1705                                                  event->time);
1706           g_free (mult_atoms);
1707           g_free (info);
1708           return TRUE;
1709         }
1710       gdk_error_trap_pop ();
1711
1712       /* This is annoying; the ICCCM doesn't specify the property type
1713        * used for the property contents, so the autoconversion for
1714        * ATOM / ATOM_PAIR in GDK doesn't work properly.
1715        */
1716 #ifdef GDK_WINDOWING_X11
1717       if (type != GDK_SELECTION_TYPE_ATOM &&
1718           type != gdk_atom_intern ("ATOM_PAIR", FALSE))
1719         {
1720           info->num_conversions = length / (2*sizeof (glong));
1721           info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1722           
1723           for (i=0; i<info->num_conversions; i++)
1724             {
1725               info->conversions[i].target = gdk_x11_xatom_to_atom_for_display (display,
1726                                                                                ((glong *)mult_atoms)[2*i]);
1727               info->conversions[i].property = gdk_x11_xatom_to_atom_for_display (display,
1728                                                                                  ((glong *)mult_atoms)[2*i + 1]);
1729             }
1730         }
1731       else
1732 #endif
1733         {
1734           info->num_conversions = length / (2*sizeof (GdkAtom));
1735           info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1736           
1737           for (i=0; i<info->num_conversions; i++)
1738             {
1739               info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1740               info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1741             }
1742         }
1743     }
1744   else                          /* only a single conversion */
1745     {
1746       info->conversions = g_new (GtkIncrConversion, 1);
1747       info->num_conversions = 1;
1748       info->conversions[0].target = event->target;
1749       info->conversions[0].property = event->property;
1750     }
1751   
1752   /* Loop through conversions and determine which of these are big
1753      enough to require doing them via INCR */
1754   for (i=0; i<info->num_conversions; i++)
1755     {
1756       GtkSelectionData data;
1757       glong items;
1758       
1759       data.selection = event->selection;
1760       data.target = info->conversions[i].target;
1761       data.data = NULL;
1762       data.length = -1;
1763       data.display = gtk_widget_get_display (widget);
1764       
1765 #ifdef DEBUG_SELECTION
1766       g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1767                  event->selection, 
1768                  info->conversions[i].target,
1769                  gdk_atom_name (info->conversions[i].target),
1770                  event->requestor, info->conversions[i].property);
1771 #endif
1772       
1773       gtk_selection_invoke_handler (widget, &data, event->time);
1774       
1775       if (data.length < 0)
1776         {
1777           info->conversions[i].property = GDK_NONE;
1778           continue;
1779         }
1780       
1781       g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1782       
1783       items = data.length / gtk_selection_bytes_per_item (data.format);
1784       
1785       if (data.length > selection_max_size)
1786         {
1787           /* Sending via INCR */
1788 #ifdef DEBUG_SELECTION
1789           g_message ("Target larger (%d) than max. request size (%ld), sending incrementally\n",
1790                      data.length, selection_max_size);
1791 #endif
1792           
1793           info->conversions[i].offset = 0;
1794           info->conversions[i].data = data;
1795           info->num_incrs++;
1796           
1797           gdk_property_change (info->requestor, 
1798                                info->conversions[i].property,
1799                                gtk_selection_atoms[INCR],
1800                                32,
1801                                GDK_PROP_MODE_REPLACE,
1802                                (guchar *)&items, 1);
1803         }
1804       else
1805         {
1806           info->conversions[i].offset = -1;
1807           
1808           gdk_property_change (info->requestor, 
1809                                info->conversions[i].property,
1810                                data.type,
1811                                data.format,
1812                                GDK_PROP_MODE_REPLACE,
1813                                data.data, items);
1814           
1815           g_free (data.data);
1816         }
1817     }
1818   
1819   /* If we have some INCR's, we need to send the rest of the data in
1820      a callback */
1821   
1822   if (info->num_incrs > 0)
1823     {
1824       /* FIXME: this could be dangerous if window doesn't still
1825          exist */
1826       
1827 #ifdef DEBUG_SELECTION
1828       g_message ("Starting INCR...");
1829 #endif
1830       
1831       gdk_window_set_events (info->requestor,
1832                              gdk_window_get_events (info->requestor) |
1833                              GDK_PROPERTY_CHANGE_MASK);
1834       current_incrs = g_list_append (current_incrs, info);
1835       g_timeout_add (1000, (GSourceFunc) gtk_selection_incr_timeout, info);
1836     }
1837   
1838   /* If it was a MULTIPLE request, set the property to indicate which
1839      conversions succeeded */
1840   if (event->target == gtk_selection_atoms[MULTIPLE])
1841     {
1842       GdkAtom *mult_atoms = g_new (GdkAtom, 2 * info->num_conversions);
1843       for (i = 0; i < info->num_conversions; i++)
1844         {
1845           mult_atoms[2*i] = info->conversions[i].target;
1846           mult_atoms[2*i+1] = info->conversions[i].property;
1847         }
1848       
1849       gdk_property_change (info->requestor, event->property,
1850                            gdk_atom_intern ("ATOM_PAIR", FALSE), 32, 
1851                            GDK_PROP_MODE_REPLACE,
1852                            (guchar *)mult_atoms, 2*info->num_conversions);
1853       g_free (mult_atoms);
1854     }
1855
1856   if (info->num_conversions == 1 &&
1857       info->conversions[0].property == GDK_NONE)
1858     {
1859       /* Reject the entire conversion */
1860       gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1861                                              event->requestor, 
1862                                              event->selection, 
1863                                              event->target, 
1864                                              GDK_NONE, 
1865                                              event->time);
1866     }
1867   else
1868     {
1869       gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1870                                              event->requestor, 
1871                                              event->selection,
1872                                              event->target,
1873                                              event->property, 
1874                                              event->time);
1875     }
1876
1877   if (info->num_incrs == 0)
1878     {
1879       g_free (info->conversions);
1880       g_free (info);
1881     }
1882
1883   g_object_unref (widget);
1884   
1885   return TRUE;
1886 }
1887
1888 /*************************************************************
1889  * _gtk_selection_incr_event:
1890  *     Called whenever an PropertyNotify event occurs for an 
1891  *     GdkWindow with user_data == NULL. These will be notifications
1892  *     that a window we are sending the selection to via the
1893  *     INCR protocol has deleted a property and is ready for
1894  *     more data.
1895  *
1896  *   arguments:
1897  *     window:  the requestor window
1898  *     event:   the property event structure
1899  *
1900  *   results:
1901  *************************************************************/
1902
1903 gboolean
1904 _gtk_selection_incr_event (GdkWindow       *window,
1905                            GdkEventProperty *event)
1906 {
1907   GList *tmp_list;
1908   GtkIncrInfo *info = NULL;
1909   gint num_bytes;
1910   guchar *buffer;
1911   gulong selection_max_size;
1912   
1913   int i;
1914   
1915   if (event->state != GDK_PROPERTY_DELETE)
1916     return FALSE;
1917   
1918 #ifdef DEBUG_SELECTION
1919   g_message ("PropertyDelete, property %ld", event->atom);
1920 #endif
1921
1922   selection_max_size = GTK_SELECTION_MAX_SIZE (gdk_drawable_get_display (window));  
1923
1924   /* Now find the appropriate ongoing INCR */
1925   tmp_list = current_incrs;
1926   while (tmp_list)
1927     {
1928       info = (GtkIncrInfo *)tmp_list->data;
1929       if (info->requestor == event->window)
1930         break;
1931       
1932       tmp_list = tmp_list->next;
1933     }
1934   
1935   if (tmp_list == NULL)
1936     return FALSE;
1937   
1938   /* Find out which target this is for */
1939   for (i=0; i<info->num_conversions; i++)
1940     {
1941       if (info->conversions[i].property == event->atom &&
1942           info->conversions[i].offset != -1)
1943         {
1944           int bytes_per_item;
1945           
1946           info->idle_time = 0;
1947           
1948           if (info->conversions[i].offset == -2) /* only the last 0-length
1949                                                     piece*/
1950             {
1951               num_bytes = 0;
1952               buffer = NULL;
1953             }
1954           else
1955             {
1956               num_bytes = info->conversions[i].data.length -
1957                 info->conversions[i].offset;
1958               buffer = info->conversions[i].data.data + 
1959                 info->conversions[i].offset;
1960               
1961               if (num_bytes > selection_max_size)
1962                 {
1963                   num_bytes = selection_max_size;
1964                   info->conversions[i].offset += selection_max_size;
1965                 }
1966               else
1967                 info->conversions[i].offset = -2;
1968             }
1969 #ifdef DEBUG_SELECTION
1970           g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1971                      num_bytes, info->conversions[i].offset, 
1972                      GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1973 #endif
1974
1975           bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1976           gdk_property_change (info->requestor, event->atom,
1977                                info->conversions[i].data.type,
1978                                info->conversions[i].data.format,
1979                                GDK_PROP_MODE_REPLACE,
1980                                buffer,
1981                                num_bytes / bytes_per_item);
1982           
1983           if (info->conversions[i].offset == -2)
1984             {
1985               g_free (info->conversions[i].data.data);
1986               info->conversions[i].data.data = NULL;
1987             }
1988           
1989           if (num_bytes == 0)
1990             {
1991               info->num_incrs--;
1992               info->conversions[i].offset = -1;
1993             }
1994         }
1995     }
1996   
1997   /* Check if we're finished with all the targets */
1998   
1999   if (info->num_incrs == 0)
2000     {
2001       current_incrs = g_list_remove_link (current_incrs, tmp_list);
2002       g_list_free (tmp_list);
2003       /* Let the timeout free it */
2004     }
2005   
2006   return TRUE;
2007 }
2008
2009 /*************************************************************
2010  * gtk_selection_incr_timeout:
2011  *     Timeout callback for the sending portion of the INCR
2012  *     protocol
2013  *   arguments:
2014  *     info:    Information about this incr
2015  *   results:
2016  *************************************************************/
2017
2018 static gint
2019 gtk_selection_incr_timeout (GtkIncrInfo *info)
2020 {
2021   GList *tmp_list;
2022   gboolean retval;
2023
2024   GDK_THREADS_ENTER ();
2025   
2026   /* Determine if retrieval has finished by checking if it still in
2027      list of pending retrievals */
2028   
2029   tmp_list = current_incrs;
2030   while (tmp_list)
2031     {
2032       if (info == (GtkIncrInfo *)tmp_list->data)
2033         break;
2034       tmp_list = tmp_list->next;
2035     }
2036   
2037   /* If retrieval is finished */
2038   if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
2039     {
2040       if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
2041         {
2042           current_incrs = g_list_remove_link (current_incrs, tmp_list);
2043           g_list_free (tmp_list);
2044         }
2045       
2046       g_free (info->conversions);
2047       /* FIXME: we should check if requestor window is still in use,
2048          and if not, remove it? */
2049       
2050       g_free (info);
2051       
2052       retval =  FALSE;          /* remove timeout */
2053     }
2054   else
2055     {
2056       info->idle_time++;
2057       
2058       retval = TRUE;            /* timeout will happen again */
2059     }
2060   
2061   GDK_THREADS_LEAVE ();
2062
2063   return retval;
2064 }
2065
2066 /*************************************************************
2067  * _gtk_selection_notify:
2068  *     Handler for "selection_notify_event" signals on windows
2069  *     where a retrieval is currently in process. The selection
2070  *     owner has responded to our conversion request.
2071  *   arguments:
2072  *     widget:          Widget getting signal
2073  *     event:           Selection event structure
2074  *     info:            Information about this retrieval
2075  *   results:
2076  *     was event handled?
2077  *************************************************************/
2078
2079 gboolean
2080 _gtk_selection_notify (GtkWidget               *widget,
2081                        GdkEventSelection *event)
2082 {
2083   GList *tmp_list;
2084   GtkRetrievalInfo *info = NULL;
2085   guchar  *buffer = NULL;
2086   gint length;
2087   GdkAtom type;
2088   gint    format;
2089   
2090 #ifdef DEBUG_SELECTION
2091   g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
2092              event->selection, event->target, event->property);
2093 #endif
2094   
2095   tmp_list = current_retrievals;
2096   while (tmp_list)
2097     {
2098       info = (GtkRetrievalInfo *)tmp_list->data;
2099       if (info->widget == widget && info->selection == event->selection)
2100         break;
2101       tmp_list = tmp_list->next;
2102     }
2103   
2104   if (!tmp_list)                /* no retrieval in progress */
2105     return FALSE;
2106
2107   if (event->property != GDK_NONE)
2108     length = gdk_selection_property_get (widget->window, &buffer, 
2109                                          &type, &format);
2110   else
2111     length = 0; /* silence gcc */
2112   
2113   if (event->property == GDK_NONE || buffer == NULL)
2114     {
2115       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
2116       g_list_free (tmp_list);
2117       /* structure will be freed in timeout */
2118       gtk_selection_retrieval_report (info,
2119                                       GDK_NONE, 0, NULL, -1, event->time);
2120       
2121       return TRUE;
2122     }
2123   
2124   if (type == gtk_selection_atoms[INCR])
2125     {
2126       /* The remainder of the selection will come through PropertyNotify
2127          events */
2128
2129       info->notify_time = event->time;
2130       info->idle_time = 0;
2131       info->offset = 0;         /* Mark as OK to proceed */
2132       gdk_window_set_events (widget->window,
2133                              gdk_window_get_events (widget->window)
2134                              | GDK_PROPERTY_CHANGE_MASK);
2135     }
2136   else
2137     {
2138       /* We don't delete the info structure - that will happen in timeout */
2139       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
2140       g_list_free (tmp_list);
2141       
2142       info->offset = length;
2143       gtk_selection_retrieval_report (info,
2144                                       type, format, 
2145                                       buffer, length, event->time);
2146     }
2147   
2148   gdk_property_delete (widget->window, event->property);
2149   
2150   g_free (buffer);
2151   
2152   return TRUE;
2153 }
2154
2155 /*************************************************************
2156  * _gtk_selection_property_notify:
2157  *     Handler for "property_notify_event" signals on windows
2158  *     where a retrieval is currently in process. The selection
2159  *     owner has added more data.
2160  *   arguments:
2161  *     widget:          Widget getting signal
2162  *     event:           Property event structure
2163  *     info:            Information about this retrieval
2164  *   results:
2165  *     was event handled?
2166  *************************************************************/
2167
2168 gboolean
2169 _gtk_selection_property_notify (GtkWidget       *widget,
2170                                 GdkEventProperty *event)
2171 {
2172   GList *tmp_list;
2173   GtkRetrievalInfo *info = NULL;
2174   guchar *new_buffer;
2175   int length;
2176   GdkAtom type;
2177   gint    format;
2178   
2179   g_return_val_if_fail (widget != NULL, FALSE);
2180   g_return_val_if_fail (event != NULL, FALSE);
2181
2182 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
2183   if ((event->state != GDK_PROPERTY_NEW_VALUE) ||  /* property was deleted */
2184       (event->atom != gdk_atom_intern ("GDK_SELECTION", FALSE))) /* not the right property */
2185 #endif
2186     return FALSE;
2187   
2188 #ifdef DEBUG_SELECTION
2189   g_message ("PropertyNewValue, property %ld",
2190              event->atom);
2191 #endif
2192   
2193   tmp_list = current_retrievals;
2194   while (tmp_list)
2195     {
2196       info = (GtkRetrievalInfo *)tmp_list->data;
2197       if (info->widget == widget)
2198         break;
2199       tmp_list = tmp_list->next;
2200     }
2201   
2202   if (!tmp_list)                /* No retrieval in progress */
2203     return FALSE;
2204   
2205   if (info->offset < 0)         /* We haven't got the SelectionNotify
2206                                    for this retrieval yet */
2207     return FALSE;
2208   
2209   info->idle_time = 0;
2210   
2211   length = gdk_selection_property_get (widget->window, &new_buffer, 
2212                                        &type, &format);
2213   gdk_property_delete (widget->window, event->atom);
2214   
2215   /* We could do a lot better efficiency-wise by paying attention to
2216      what length was sent in the initial INCR transaction, instead of
2217      doing memory allocation at every step. But its only guaranteed to
2218      be a _lower bound_ (pretty useless!) */
2219   
2220   if (length == 0 || type == GDK_NONE)          /* final zero length portion */
2221     {
2222       /* Info structure will be freed in timeout */
2223       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
2224       g_list_free (tmp_list);
2225       gtk_selection_retrieval_report (info,
2226                                       type, format, 
2227                                       (type == GDK_NONE) ?  NULL : info->buffer,
2228                                       (type == GDK_NONE) ?  -1 : info->offset,
2229                                       info->notify_time);
2230     }
2231   else                          /* append on newly arrived data */
2232     {
2233       if (!info->buffer)
2234         {
2235 #ifdef DEBUG_SELECTION
2236           g_message ("Start - Adding %d bytes at offset 0",
2237                      length);
2238 #endif
2239           info->buffer = new_buffer;
2240           info->offset = length;
2241         }
2242       else
2243         {
2244           
2245 #ifdef DEBUG_SELECTION
2246           g_message ("Appending %d bytes at offset %d",
2247                      length,info->offset);
2248 #endif
2249           /* We copy length+1 bytes to preserve guaranteed null termination */
2250           info->buffer = g_realloc (info->buffer, info->offset+length+1);
2251           memcpy (info->buffer + info->offset, new_buffer, length+1);
2252           info->offset += length;
2253           g_free (new_buffer);
2254         }
2255     }
2256   
2257   return TRUE;
2258 }
2259
2260 /*************************************************************
2261  * gtk_selection_retrieval_timeout:
2262  *     Timeout callback while receiving a selection.
2263  *   arguments:
2264  *     info:    Information about this retrieval
2265  *   results:
2266  *************************************************************/
2267
2268 static gint
2269 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
2270 {
2271   GList *tmp_list;
2272   gboolean retval;
2273
2274   GDK_THREADS_ENTER ();
2275   
2276   /* Determine if retrieval has finished by checking if it still in
2277      list of pending retrievals */
2278   
2279   tmp_list = current_retrievals;
2280   while (tmp_list)
2281     {
2282       if (info == (GtkRetrievalInfo *)tmp_list->data)
2283         break;
2284       tmp_list = tmp_list->next;
2285     }
2286   
2287   /* If retrieval is finished */
2288   if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
2289     {
2290       if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
2291         {
2292           current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
2293           g_list_free (tmp_list);
2294           gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
2295         }
2296       
2297       g_free (info->buffer);
2298       g_free (info);
2299       
2300       retval =  FALSE;          /* remove timeout */
2301     }
2302   else
2303     {
2304       info->idle_time++;
2305       
2306       retval =  TRUE;           /* timeout will happen again */
2307     }
2308
2309   GDK_THREADS_LEAVE ();
2310
2311   return retval;
2312 }
2313
2314 /*************************************************************
2315  * gtk_selection_retrieval_report:
2316  *     Emits a "selection_received" signal.
2317  *   arguments:
2318  *     info:      information about the retrieval that completed
2319  *     buffer:    buffer containing data (NULL => errror)
2320  *     time:      timestamp for data in buffer
2321  *   results:
2322  *************************************************************/
2323
2324 static void
2325 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
2326                                 GdkAtom type, gint format, 
2327                                 guchar *buffer, gint length,
2328                                 guint32 time)
2329 {
2330   GtkSelectionData data;
2331   
2332   data.selection = info->selection;
2333   data.target = info->target;
2334   data.type = type;
2335   data.format = format;
2336   
2337   data.length = length;
2338   data.data = buffer;
2339   data.display = gtk_widget_get_display (info->widget);
2340   
2341   g_signal_emit_by_name (info->widget,
2342                          "selection_received", 
2343                          &data, time);
2344 }
2345
2346 /*************************************************************
2347  * gtk_selection_invoke_handler:
2348  *     Finds and invokes handler for specified
2349  *     widget/selection/target combination, calls 
2350  *     gtk_selection_default_handler if none exists.
2351  *
2352  *   arguments:
2353  *     widget:      selection owner
2354  *     data:        selection data [INOUT]
2355  *     time:        time from requeset
2356  *     
2357  *   results:
2358  *     Number of bytes written to buffer, -1 if error
2359  *************************************************************/
2360
2361 static void
2362 gtk_selection_invoke_handler (GtkWidget        *widget,
2363                               GtkSelectionData *data,
2364                               guint             time)
2365 {
2366   GtkTargetList *target_list;
2367   guint info;
2368   
2369
2370   g_return_if_fail (widget != NULL);
2371
2372   target_list = gtk_selection_target_list_get (widget, data->selection);
2373   if (target_list && 
2374       gtk_target_list_find (target_list, data->target, &info))
2375     {
2376       g_signal_emit_by_name (widget,
2377                              "selection_get",
2378                              data,
2379                              info, time);
2380     }
2381   else
2382     gtk_selection_default_handler (widget, data);
2383 }
2384
2385 /*************************************************************
2386  * gtk_selection_default_handler:
2387  *     Handles some default targets that exist for any widget
2388  *     If it can't fit results into buffer, returns -1. This
2389  *     won't happen in any conceivable case, since it would
2390  *     require 1000 selection targets!
2391  *
2392  *   arguments:
2393  *     widget:      selection owner
2394  *     data:        selection data [INOUT]
2395  *
2396  *************************************************************/
2397
2398 static void
2399 gtk_selection_default_handler (GtkWidget        *widget,
2400                                GtkSelectionData *data)
2401 {
2402   if (data->target == gtk_selection_atoms[TIMESTAMP])
2403     {
2404       /* Time which was used to obtain selection */
2405       GList *tmp_list;
2406       GtkSelectionInfo *selection_info;
2407       
2408       tmp_list = current_selections;
2409       while (tmp_list)
2410         {
2411           selection_info = (GtkSelectionInfo *)tmp_list->data;
2412           if ((selection_info->widget == widget) &&
2413               (selection_info->selection == data->selection))
2414             {
2415               gulong time = selection_info->time;
2416
2417               gtk_selection_data_set (data,
2418                                       GDK_SELECTION_TYPE_INTEGER,
2419                                       32,
2420                                       (guchar *)&time,
2421                                       sizeof (time));
2422               return;
2423             }
2424           
2425           tmp_list = tmp_list->next;
2426         }
2427       
2428       data->length = -1;
2429     }
2430   else if (data->target == gtk_selection_atoms[TARGETS])
2431     {
2432       /* List of all targets supported for this widget/selection pair */
2433       GdkAtom *p;
2434       guint count;
2435       GList *tmp_list;
2436       GtkTargetList *target_list;
2437       GtkTargetPair *pair;
2438       
2439       target_list = gtk_selection_target_list_get (widget,
2440                                                    data->selection);
2441       count = g_list_length (target_list->list) + 3;
2442       
2443       data->type = GDK_SELECTION_TYPE_ATOM;
2444       data->format = 32;
2445       data->length = count * sizeof (GdkAtom);
2446
2447       /* selection data is always terminated by a trailing \0
2448        */
2449       p = g_malloc (data->length + 1);
2450       data->data = (guchar *)p;
2451       data->data[data->length] = '\0';
2452       
2453       *p++ = gtk_selection_atoms[TIMESTAMP];
2454       *p++ = gtk_selection_atoms[TARGETS];
2455       *p++ = gtk_selection_atoms[MULTIPLE];
2456       
2457       tmp_list = target_list->list;
2458       while (tmp_list)
2459         {
2460           pair = (GtkTargetPair *)tmp_list->data;
2461           *p++ = pair->target;
2462           
2463           tmp_list = tmp_list->next;
2464         }
2465     }
2466   else
2467     {
2468       data->length = -1;
2469     }
2470 }
2471
2472
2473 GtkSelectionData*
2474 gtk_selection_data_copy (GtkSelectionData *selection_data)
2475 {
2476   GtkSelectionData *new_data;
2477   
2478   g_return_val_if_fail (selection_data != NULL, NULL);
2479   
2480   new_data = g_new (GtkSelectionData, 1);
2481   *new_data = *selection_data;
2482
2483   if (selection_data->data)
2484     {
2485       new_data->data = g_malloc (selection_data->length + 1);
2486       memcpy (new_data->data, selection_data->data, selection_data->length + 1);
2487     }
2488   
2489   return new_data;
2490 }
2491
2492 void
2493 gtk_selection_data_free (GtkSelectionData *data)
2494 {
2495   g_return_if_fail (data != NULL);
2496   
2497   if (data->data)
2498     g_free (data->data);
2499   
2500   g_free (data);
2501 }
2502
2503 GType
2504 gtk_selection_data_get_type (void)
2505 {
2506   static GType our_type = 0;
2507   
2508   if (our_type == 0)
2509     our_type = g_boxed_type_register_static ("GtkSelectionData",
2510                                              (GBoxedCopyFunc) gtk_selection_data_copy,
2511                                              (GBoxedFreeFunc) gtk_selection_data_free);
2512
2513   return our_type;
2514 }
2515
2516 static int 
2517 gtk_selection_bytes_per_item (gint format)
2518 {
2519   switch (format)
2520     {
2521     case 8:
2522       return sizeof (char);
2523       break;
2524     case 16:
2525       return sizeof (short);
2526       break;
2527     case 32:
2528       return sizeof (long);
2529       break;
2530     default:
2531       g_assert_not_reached();
2532     }
2533   return 0;
2534 }