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