]> Pileus Git - ~andy/gtk/blob - gtk/gtkselection.c
Warn if a UTF8_STRING or text/plain;charset=utf-8 roperty contains invalid
[~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   gsize 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       if (!str)
1037         {
1038           g_warning ("Error converting from %s to UTF-8: %s",
1039                       charset, error->message);
1040           g_error_free (error);
1041
1042           return NULL;
1043         }
1044     }
1045   else if (!g_utf8_validate (str, -1, NULL))
1046     {
1047       g_warning ("Error converting from text/plain;charset=utf-8 to UTF-8");
1048       g_free (str);
1049
1050       return NULL;
1051     }
1052
1053   result = normalize_to_lf (str, len);
1054   g_free (str);
1055
1056   return result;
1057 }
1058
1059 /**
1060  * gtk_selection_data_set_text:
1061  * @selection_data: a #GtkSelectionData
1062  * @str: a UTF-8 string
1063  * @len: the length of @str, or -1 if @str is nul-terminated.
1064  * 
1065  * Sets the contents of the selection from a UTF-8 encoded string.
1066  * The string is converted to the form determined by
1067  * @selection_data->target.
1068  * 
1069  * Return value: %TRUE if the selection was successfully set,
1070  *   otherwise %FALSE.
1071  **/
1072 gboolean
1073 gtk_selection_data_set_text (GtkSelectionData     *selection_data,
1074                              const gchar          *str,
1075                              gint                  len)
1076 {
1077   if (len < 0)
1078     len = strlen (str);
1079   
1080   init_atoms ();
1081
1082   if (selection_data->target == utf8_atom)
1083     {
1084       gtk_selection_data_set (selection_data,
1085                               utf8_atom,
1086                               8, (guchar *)str, len);
1087       return TRUE;
1088     }
1089   else if (selection_data->target == GDK_TARGET_STRING)
1090     {
1091       return selection_set_string (selection_data, str, len);
1092     }
1093   else if (selection_data->target == ctext_atom ||
1094            selection_data->target == text_atom)
1095     {
1096       if (selection_set_compound_text (selection_data, str, len))
1097         return TRUE;
1098       else if (selection_data->target == text_atom)
1099         return selection_set_string (selection_data, str, len);
1100     }
1101   else if (selection_data->target == text_plain_atom ||
1102            selection_data->target == text_plain_utf8_atom ||
1103            selection_data->target == text_plain_locale_atom)
1104     {
1105       return selection_set_text_plain (selection_data, str, len);
1106     }
1107
1108   return FALSE;
1109 }
1110
1111 /**
1112  * gtk_selection_data_get_text:
1113  * @selection_data: a #GtkSelectionData
1114  * 
1115  * Gets the contents of the selection data as a UTF-8 string.
1116  * 
1117  * Return value: if the selection data contained a recognized
1118  *   text type and it could be converted to UTF-8, a newly allocated
1119  *   string containing the converted text, otherwise %NULL.
1120  *   If the result is non-%NULL it must be freed with g_free().
1121  **/
1122 guchar *
1123 gtk_selection_data_get_text (GtkSelectionData *selection_data)
1124 {
1125   guchar *result = NULL;
1126
1127   init_atoms ();
1128   
1129   if (selection_data->length >= 0 &&
1130       (selection_data->type == GDK_TARGET_STRING ||
1131        selection_data->type == ctext_atom ||
1132        selection_data->type == utf8_atom))
1133     {
1134       gchar **list;
1135       gint i;
1136       gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
1137                                                                selection_data->type,
1138                                                                selection_data->format, 
1139                                                                selection_data->data,
1140                                                                selection_data->length,
1141                                                                &list);
1142       if (count > 0)
1143         result = list[0];
1144
1145       for (i = 1; i < count; i++)
1146         g_free (list[i]);
1147       g_free (list);
1148     }
1149   else if (selection_data->length >= 0 &&
1150            (selection_data->type == text_plain_atom ||
1151             selection_data->type == text_plain_utf8_atom ||
1152             selection_data->type == text_plain_locale_atom))
1153     {
1154       result = selection_get_text_plain (selection_data);
1155     }
1156
1157   return result;
1158 }
1159
1160 /**
1161  * gtk_selection_data_get_targets:
1162  * @selection_data: a #GtkSelectionData object
1163  * @targets: location to store an array of targets. The result
1164  *           stored here must be freed with g_free().
1165  * @n_atoms: location to store number of items in @targets.
1166  * 
1167  * Gets the contents of @selection_data as an array of targets.
1168  * This can be used to interpret the results of getting
1169  * the standard TARGETS target that is always supplied for
1170  * any selection.
1171  * 
1172  * Return value: %TRUE if @selection_data contains a valid
1173  *    array of targets, otherwise %FALSE.
1174  **/
1175 gboolean
1176 gtk_selection_data_get_targets (GtkSelectionData  *selection_data,
1177                                 GdkAtom          **targets,
1178                                 gint              *n_atoms)
1179 {
1180   if (selection_data->length >= 0 &&
1181       selection_data->format == 32 &&
1182       selection_data->type == GDK_SELECTION_TYPE_ATOM)
1183     {
1184       if (targets)
1185         *targets = g_memdup (selection_data->data, selection_data->length);
1186       if (n_atoms)
1187         *n_atoms = selection_data->length / sizeof (GdkAtom);
1188
1189       return TRUE;
1190     }
1191   else
1192     {
1193       if (targets)
1194         *targets = NULL;
1195       if (n_atoms)
1196         *n_atoms = -1;
1197
1198       return FALSE;
1199     }
1200 }
1201
1202 /**
1203  * gtk_selection_data_targets_include_text:
1204  * @selection_data: a #GtkSelectionData object
1205  * 
1206  * Given a #GtkSelectionData object holding a list of targets,
1207  * determines if any of the targets in @targets can be used to
1208  * provide text.
1209  * 
1210  * Return value: %TRUE if @selection_data holds a list of targets,
1211  *   and a suitable target for text is included, otherwise %FALSE.
1212  **/
1213 gboolean
1214 gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
1215 {
1216   GdkAtom *targets;
1217   gint n_targets;
1218   gint i;
1219   gboolean result = FALSE;
1220
1221   init_atoms ();
1222
1223   if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
1224     {
1225       for (i=0; i < n_targets; i++)
1226         {
1227           if (targets[i] == utf8_atom ||
1228               targets[i] == text_atom ||
1229               targets[i] == GDK_TARGET_STRING ||
1230               targets[i] == ctext_atom ||
1231               targets[i] == text_plain_atom ||
1232               targets[i] == text_plain_utf8_atom ||
1233               targets[i] == text_plain_locale_atom)
1234             result = TRUE;
1235         }
1236
1237       g_free (targets);
1238     }
1239
1240   return result;
1241 }
1242           
1243 /*************************************************************
1244  * gtk_selection_init:
1245  *     Initialize local variables
1246  *   arguments:
1247  *     
1248  *   results:
1249  *************************************************************/
1250
1251 static void
1252 gtk_selection_init (void)
1253 {
1254   gtk_selection_atoms[INCR] = gdk_atom_intern ("INCR", FALSE);
1255   gtk_selection_atoms[MULTIPLE] = gdk_atom_intern ("MULTIPLE", FALSE);
1256   gtk_selection_atoms[TIMESTAMP] = gdk_atom_intern ("TIMESTAMP", FALSE);
1257   gtk_selection_atoms[TARGETS] = gdk_atom_intern ("TARGETS", FALSE);
1258
1259   initialize = FALSE;
1260 }
1261
1262 /**
1263  * gtk_selection_clear:
1264  * @widget: a #GtkWidget
1265  * @event: the event
1266  * 
1267  * The default handler for the GtkWidget::selection_clear_event
1268  * signal. 
1269  * 
1270  * Return value: %TRUE if the event was handled, otherwise false
1271  * 
1272  * Since: 2.2
1273  *
1274  * Deprecated: Instead of calling this function, chain up from
1275  * your selection_clear_event handler. Calling this function
1276  * from any other context is illegal. 
1277  **/
1278 gboolean
1279 gtk_selection_clear (GtkWidget         *widget,
1280                      GdkEventSelection *event)
1281 {
1282   /* Note that we filter clear events in gdkselection-x11.c, so
1283    * that we only will get here if the clear event actually
1284    * represents a change that we didn't do ourself.
1285    */
1286   GList *tmp_list;
1287   GtkSelectionInfo *selection_info = NULL;
1288   
1289   tmp_list = current_selections;
1290   while (tmp_list)
1291     {
1292       selection_info = (GtkSelectionInfo *)tmp_list->data;
1293       
1294       if ((selection_info->selection == event->selection) &&
1295           (selection_info->widget == widget))
1296         break;
1297       
1298       tmp_list = tmp_list->next;
1299     }
1300   
1301   if (tmp_list)
1302     {
1303       current_selections = g_list_remove_link (current_selections, tmp_list);
1304       g_list_free (tmp_list);
1305       g_free (selection_info);
1306     }
1307   
1308   return TRUE;
1309 }
1310
1311
1312 /*************************************************************
1313  * _gtk_selection_request:
1314  *     Handler for "selection_request_event" 
1315  *   arguments:
1316  *     widget:
1317  *     event:
1318  *   results:
1319  *************************************************************/
1320
1321 gboolean
1322 _gtk_selection_request (GtkWidget *widget,
1323                         GdkEventSelection *event)
1324 {
1325   GdkDisplay *display = gtk_widget_get_display (widget);
1326   GtkIncrInfo *info;
1327   GList *tmp_list;
1328   int i;
1329   gulong selection_max_size;
1330
1331   if (initialize)
1332     gtk_selection_init ();
1333   
1334   selection_max_size = GTK_SELECTION_MAX_SIZE (display);
1335
1336   /* Check if we own selection */
1337   
1338   tmp_list = current_selections;
1339   while (tmp_list)
1340     {
1341       GtkSelectionInfo *selection_info = (GtkSelectionInfo *)tmp_list->data;
1342       
1343       if ((selection_info->selection == event->selection) &&
1344           (selection_info->widget == widget))
1345         break;
1346       
1347       tmp_list = tmp_list->next;
1348     }
1349   
1350   if (tmp_list == NULL)
1351     return FALSE;
1352   
1353   info = g_new (GtkIncrInfo, 1);
1354
1355   g_object_ref (widget);
1356   
1357   info->selection = event->selection;
1358   info->num_incrs = 0;
1359   
1360   /* Create GdkWindow structure for the requestor */
1361   
1362   info->requestor = gdk_window_lookup_for_display (display,
1363                                                    event->requestor);
1364   if (!info->requestor)
1365     info->requestor = gdk_window_foreign_new_for_display (display,
1366                                                           event->requestor);
1367   
1368   /* Determine conversions we need to perform */
1369   
1370   if (event->target == gtk_selection_atoms[MULTIPLE])
1371     {
1372       GdkAtom  type;
1373       guchar  *mult_atoms;
1374       gint     format;
1375       gint     length;
1376       
1377       mult_atoms = NULL;
1378       
1379       gdk_error_trap_push ();
1380       if (!gdk_property_get (info->requestor, event->property, GDK_NONE, /* AnyPropertyType */
1381                              0, selection_max_size, FALSE,
1382                              &type, &format, &length, &mult_atoms))
1383         {
1384           gdk_selection_send_notify_for_display (display,
1385                                                  event->requestor, 
1386                                                  event->selection,
1387                                                  event->target, 
1388                                                  GDK_NONE, 
1389                                                  event->time);
1390           g_free (mult_atoms);
1391           g_free (info);
1392           return TRUE;
1393         }
1394       gdk_error_trap_pop ();
1395
1396       /* This is annoying; the ICCCM doesn't specify the property type
1397        * used for the property contents, so the autoconversion for
1398        * ATOM / ATOM_PAIR in GDK doesn't work properly.
1399        */
1400 #ifdef GDK_WINDOWING_X11
1401       if (type != GDK_SELECTION_TYPE_ATOM &&
1402           type != gdk_atom_intern ("ATOM_PAIR", FALSE))
1403         {
1404           info->num_conversions = length / (2*sizeof (glong));
1405           info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1406           
1407           for (i=0; i<info->num_conversions; i++)
1408             {
1409               info->conversions[i].target = gdk_x11_xatom_to_atom_for_display (display,
1410                                                                                ((glong *)mult_atoms)[2*i]);
1411               info->conversions[i].property = gdk_x11_xatom_to_atom_for_display (display,
1412                                                                                  ((glong *)mult_atoms)[2*i + 1]);
1413             }
1414         }
1415       else
1416 #endif
1417         {
1418           info->num_conversions = length / (2*sizeof (GdkAtom));
1419           info->conversions = g_new (GtkIncrConversion, info->num_conversions);
1420           
1421           for (i=0; i<info->num_conversions; i++)
1422             {
1423               info->conversions[i].target = ((GdkAtom *)mult_atoms)[2*i];
1424               info->conversions[i].property = ((GdkAtom *)mult_atoms)[2*i+1];
1425             }
1426         }
1427     }
1428   else                          /* only a single conversion */
1429     {
1430       info->conversions = g_new (GtkIncrConversion, 1);
1431       info->num_conversions = 1;
1432       info->conversions[0].target = event->target;
1433       info->conversions[0].property = event->property;
1434     }
1435   
1436   /* Loop through conversions and determine which of these are big
1437      enough to require doing them via INCR */
1438   for (i=0; i<info->num_conversions; i++)
1439     {
1440       GtkSelectionData data;
1441       glong items;
1442       
1443       data.selection = event->selection;
1444       data.target = info->conversions[i].target;
1445       data.data = NULL;
1446       data.length = -1;
1447       data.display = gtk_widget_get_display (widget);
1448       
1449 #ifdef DEBUG_SELECTION
1450       g_message ("Selection %ld, target %ld (%s) requested by 0x%x (property = %ld)",
1451                  event->selection, 
1452                  info->conversions[i].target,
1453                  gdk_atom_name (info->conversions[i].target),
1454                  event->requestor, info->conversions[i].property);
1455 #endif
1456       
1457       gtk_selection_invoke_handler (widget, &data, event->time);
1458       
1459       if (data.length < 0)
1460         {
1461           info->conversions[i].property = GDK_NONE;
1462           continue;
1463         }
1464       
1465       g_return_val_if_fail ((data.format >= 8) && (data.format % 8 == 0), FALSE);
1466       
1467       items = data.length / gtk_selection_bytes_per_item (data.format);
1468       
1469       if (data.length > selection_max_size)
1470         {
1471           /* Sending via INCR */
1472 #ifdef DEBUG_SELECTION
1473           g_message ("Target larger (%d) than max. request size (%ld), sending incrementally\n",
1474                      data.length, selection_max_size);
1475 #endif
1476           
1477           info->conversions[i].offset = 0;
1478           info->conversions[i].data = data;
1479           info->num_incrs++;
1480           
1481           gdk_property_change (info->requestor, 
1482                                info->conversions[i].property,
1483                                gtk_selection_atoms[INCR],
1484                                32,
1485                                GDK_PROP_MODE_REPLACE,
1486                                (guchar *)&items, 1);
1487         }
1488       else
1489         {
1490           info->conversions[i].offset = -1;
1491           
1492           gdk_property_change (info->requestor, 
1493                                info->conversions[i].property,
1494                                data.type,
1495                                data.format,
1496                                GDK_PROP_MODE_REPLACE,
1497                                data.data, items);
1498           
1499           g_free (data.data);
1500         }
1501     }
1502   
1503   /* If we have some INCR's, we need to send the rest of the data in
1504      a callback */
1505   
1506   if (info->num_incrs > 0)
1507     {
1508       /* FIXME: this could be dangerous if window doesn't still
1509          exist */
1510       
1511 #ifdef DEBUG_SELECTION
1512       g_message ("Starting INCR...");
1513 #endif
1514       
1515       gdk_window_set_events (info->requestor,
1516                              gdk_window_get_events (info->requestor) |
1517                              GDK_PROPERTY_CHANGE_MASK);
1518       current_incrs = g_list_append (current_incrs, info);
1519       g_timeout_add (1000, (GSourceFunc) gtk_selection_incr_timeout, info);
1520     }
1521   
1522   /* If it was a MULTIPLE request, set the property to indicate which
1523      conversions succeeded */
1524   if (event->target == gtk_selection_atoms[MULTIPLE])
1525     {
1526       GdkAtom *mult_atoms = g_new (GdkAtom, 2 * info->num_conversions);
1527       for (i = 0; i < info->num_conversions; i++)
1528         {
1529           mult_atoms[2*i] = info->conversions[i].target;
1530           mult_atoms[2*i+1] = info->conversions[i].property;
1531         }
1532       
1533       gdk_property_change (info->requestor, event->property,
1534                            gdk_atom_intern ("ATOM_PAIR", FALSE), 32, 
1535                            GDK_PROP_MODE_REPLACE,
1536                            (guchar *)mult_atoms, 2*info->num_conversions);
1537       g_free (mult_atoms);
1538     }
1539
1540   if (info->num_conversions == 1 &&
1541       info->conversions[0].property == GDK_NONE)
1542     {
1543       /* Reject the entire conversion */
1544       gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1545                                              event->requestor, 
1546                                              event->selection, 
1547                                              event->target, 
1548                                              GDK_NONE, 
1549                                              event->time);
1550     }
1551   else
1552     {
1553       gdk_selection_send_notify_for_display (gtk_widget_get_display (widget),
1554                                              event->requestor, 
1555                                              event->selection,
1556                                              event->target,
1557                                              event->property, 
1558                                              event->time);
1559     }
1560
1561   if (info->num_incrs == 0)
1562     {
1563       g_free (info->conversions);
1564       g_free (info);
1565     }
1566
1567   g_object_unref (widget);
1568   
1569   return TRUE;
1570 }
1571
1572 /*************************************************************
1573  * _gtk_selection_incr_event:
1574  *     Called whenever an PropertyNotify event occurs for an 
1575  *     GdkWindow with user_data == NULL. These will be notifications
1576  *     that a window we are sending the selection to via the
1577  *     INCR protocol has deleted a property and is ready for
1578  *     more data.
1579  *
1580  *   arguments:
1581  *     window:  the requestor window
1582  *     event:   the property event structure
1583  *
1584  *   results:
1585  *************************************************************/
1586
1587 gboolean
1588 _gtk_selection_incr_event (GdkWindow       *window,
1589                            GdkEventProperty *event)
1590 {
1591   GList *tmp_list;
1592   GtkIncrInfo *info = NULL;
1593   gint num_bytes;
1594   guchar *buffer;
1595   gulong selection_max_size;
1596   
1597   int i;
1598   
1599   if (event->state != GDK_PROPERTY_DELETE)
1600     return FALSE;
1601   
1602 #ifdef DEBUG_SELECTION
1603   g_message ("PropertyDelete, property %ld", event->atom);
1604 #endif
1605
1606   selection_max_size = GTK_SELECTION_MAX_SIZE (gdk_drawable_get_display (window));  
1607
1608   /* Now find the appropriate ongoing INCR */
1609   tmp_list = current_incrs;
1610   while (tmp_list)
1611     {
1612       info = (GtkIncrInfo *)tmp_list->data;
1613       if (info->requestor == event->window)
1614         break;
1615       
1616       tmp_list = tmp_list->next;
1617     }
1618   
1619   if (tmp_list == NULL)
1620     return FALSE;
1621   
1622   /* Find out which target this is for */
1623   for (i=0; i<info->num_conversions; i++)
1624     {
1625       if (info->conversions[i].property == event->atom &&
1626           info->conversions[i].offset != -1)
1627         {
1628           int bytes_per_item;
1629           
1630           info->idle_time = 0;
1631           
1632           if (info->conversions[i].offset == -2) /* only the last 0-length
1633                                                     piece*/
1634             {
1635               num_bytes = 0;
1636               buffer = NULL;
1637             }
1638           else
1639             {
1640               num_bytes = info->conversions[i].data.length -
1641                 info->conversions[i].offset;
1642               buffer = info->conversions[i].data.data + 
1643                 info->conversions[i].offset;
1644               
1645               if (num_bytes > selection_max_size)
1646                 {
1647                   num_bytes = selection_max_size;
1648                   info->conversions[i].offset += selection_max_size;
1649                 }
1650               else
1651                 info->conversions[i].offset = -2;
1652             }
1653 #ifdef DEBUG_SELECTION
1654           g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
1655                      num_bytes, info->conversions[i].offset, 
1656                      GDK_WINDOW_XWINDOW(info->requestor), event->atom);
1657 #endif
1658
1659           bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
1660           gdk_property_change (info->requestor, event->atom,
1661                                info->conversions[i].data.type,
1662                                info->conversions[i].data.format,
1663                                GDK_PROP_MODE_REPLACE,
1664                                buffer,
1665                                num_bytes / bytes_per_item);
1666           
1667           if (info->conversions[i].offset == -2)
1668             {
1669               g_free (info->conversions[i].data.data);
1670               info->conversions[i].data.data = NULL;
1671             }
1672           
1673           if (num_bytes == 0)
1674             {
1675               info->num_incrs--;
1676               info->conversions[i].offset = -1;
1677             }
1678         }
1679     }
1680   
1681   /* Check if we're finished with all the targets */
1682   
1683   if (info->num_incrs == 0)
1684     {
1685       current_incrs = g_list_remove_link (current_incrs, tmp_list);
1686       g_list_free (tmp_list);
1687       /* Let the timeout free it */
1688     }
1689   
1690   return TRUE;
1691 }
1692
1693 /*************************************************************
1694  * gtk_selection_incr_timeout:
1695  *     Timeout callback for the sending portion of the INCR
1696  *     protocol
1697  *   arguments:
1698  *     info:    Information about this incr
1699  *   results:
1700  *************************************************************/
1701
1702 static gint
1703 gtk_selection_incr_timeout (GtkIncrInfo *info)
1704 {
1705   GList *tmp_list;
1706   gboolean retval;
1707
1708   GDK_THREADS_ENTER ();
1709   
1710   /* Determine if retrieval has finished by checking if it still in
1711      list of pending retrievals */
1712   
1713   tmp_list = current_incrs;
1714   while (tmp_list)
1715     {
1716       if (info == (GtkIncrInfo *)tmp_list->data)
1717         break;
1718       tmp_list = tmp_list->next;
1719     }
1720   
1721   /* If retrieval is finished */
1722   if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1723     {
1724       if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1725         {
1726           current_incrs = g_list_remove_link (current_incrs, tmp_list);
1727           g_list_free (tmp_list);
1728         }
1729       
1730       g_free (info->conversions);
1731       /* FIXME: we should check if requestor window is still in use,
1732          and if not, remove it? */
1733       
1734       g_free (info);
1735       
1736       retval =  FALSE;          /* remove timeout */
1737     }
1738   else
1739     {
1740       info->idle_time++;
1741       
1742       retval = TRUE;            /* timeout will happen again */
1743     }
1744   
1745   GDK_THREADS_LEAVE ();
1746
1747   return retval;
1748 }
1749
1750 /*************************************************************
1751  * _gtk_selection_notify:
1752  *     Handler for "selection_notify_event" signals on windows
1753  *     where a retrieval is currently in process. The selection
1754  *     owner has responded to our conversion request.
1755  *   arguments:
1756  *     widget:          Widget getting signal
1757  *     event:           Selection event structure
1758  *     info:            Information about this retrieval
1759  *   results:
1760  *     was event handled?
1761  *************************************************************/
1762
1763 gboolean
1764 _gtk_selection_notify (GtkWidget               *widget,
1765                        GdkEventSelection *event)
1766 {
1767   GList *tmp_list;
1768   GtkRetrievalInfo *info = NULL;
1769   guchar  *buffer = NULL;
1770   gint length;
1771   GdkAtom type;
1772   gint    format;
1773   
1774 #ifdef DEBUG_SELECTION
1775   g_message ("Initial receipt of selection %ld, target %ld (property = %ld)",
1776              event->selection, event->target, event->property);
1777 #endif
1778   
1779   tmp_list = current_retrievals;
1780   while (tmp_list)
1781     {
1782       info = (GtkRetrievalInfo *)tmp_list->data;
1783       if (info->widget == widget && info->selection == event->selection)
1784         break;
1785       tmp_list = tmp_list->next;
1786     }
1787   
1788   if (!tmp_list)                /* no retrieval in progress */
1789     return FALSE;
1790
1791   if (event->property != GDK_NONE)
1792     length = gdk_selection_property_get (widget->window, &buffer, 
1793                                          &type, &format);
1794   else
1795     length = 0; /* silence gcc */
1796   
1797   if (event->property == GDK_NONE || buffer == NULL)
1798     {
1799       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1800       g_list_free (tmp_list);
1801       /* structure will be freed in timeout */
1802       gtk_selection_retrieval_report (info,
1803                                       GDK_NONE, 0, NULL, -1, event->time);
1804       
1805       return TRUE;
1806     }
1807   
1808   if (type == gtk_selection_atoms[INCR])
1809     {
1810       /* The remainder of the selection will come through PropertyNotify
1811          events */
1812
1813       info->notify_time = event->time;
1814       info->idle_time = 0;
1815       info->offset = 0;         /* Mark as OK to proceed */
1816       gdk_window_set_events (widget->window,
1817                              gdk_window_get_events (widget->window)
1818                              | GDK_PROPERTY_CHANGE_MASK);
1819     }
1820   else
1821     {
1822       /* We don't delete the info structure - that will happen in timeout */
1823       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1824       g_list_free (tmp_list);
1825       
1826       info->offset = length;
1827       gtk_selection_retrieval_report (info,
1828                                       type, format, 
1829                                       buffer, length, event->time);
1830     }
1831   
1832   gdk_property_delete (widget->window, event->property);
1833   
1834   g_free (buffer);
1835   
1836   return TRUE;
1837 }
1838
1839 /*************************************************************
1840  * _gtk_selection_property_notify:
1841  *     Handler for "property_notify_event" signals on windows
1842  *     where a retrieval is currently in process. The selection
1843  *     owner has added more data.
1844  *   arguments:
1845  *     widget:          Widget getting signal
1846  *     event:           Property event structure
1847  *     info:            Information about this retrieval
1848  *   results:
1849  *     was event handled?
1850  *************************************************************/
1851
1852 gboolean
1853 _gtk_selection_property_notify (GtkWidget       *widget,
1854                                 GdkEventProperty *event)
1855 {
1856   GList *tmp_list;
1857   GtkRetrievalInfo *info = NULL;
1858   guchar *new_buffer;
1859   int length;
1860   GdkAtom type;
1861   gint    format;
1862   
1863   g_return_val_if_fail (widget != NULL, FALSE);
1864   g_return_val_if_fail (event != NULL, FALSE);
1865
1866 #if defined(GDK_WINDOWING_WIN32) || defined(GDK_WINDOWING_X11)
1867   if ((event->state != GDK_PROPERTY_NEW_VALUE) ||  /* property was deleted */
1868       (event->atom != gdk_atom_intern ("GDK_SELECTION", FALSE))) /* not the right property */
1869 #endif
1870     return FALSE;
1871   
1872 #ifdef DEBUG_SELECTION
1873   g_message ("PropertyNewValue, property %ld",
1874              event->atom);
1875 #endif
1876   
1877   tmp_list = current_retrievals;
1878   while (tmp_list)
1879     {
1880       info = (GtkRetrievalInfo *)tmp_list->data;
1881       if (info->widget == widget)
1882         break;
1883       tmp_list = tmp_list->next;
1884     }
1885   
1886   if (!tmp_list)                /* No retrieval in progress */
1887     return FALSE;
1888   
1889   if (info->offset < 0)         /* We haven't got the SelectionNotify
1890                                    for this retrieval yet */
1891     return FALSE;
1892   
1893   info->idle_time = 0;
1894   
1895   length = gdk_selection_property_get (widget->window, &new_buffer, 
1896                                        &type, &format);
1897   gdk_property_delete (widget->window, event->atom);
1898   
1899   /* We could do a lot better efficiency-wise by paying attention to
1900      what length was sent in the initial INCR transaction, instead of
1901      doing memory allocation at every step. But its only guaranteed to
1902      be a _lower bound_ (pretty useless!) */
1903   
1904   if (length == 0 || type == GDK_NONE)          /* final zero length portion */
1905     {
1906       /* Info structure will be freed in timeout */
1907       current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1908       g_list_free (tmp_list);
1909       gtk_selection_retrieval_report (info,
1910                                       type, format, 
1911                                       (type == GDK_NONE) ?  NULL : info->buffer,
1912                                       (type == GDK_NONE) ?  -1 : info->offset,
1913                                       info->notify_time);
1914     }
1915   else                          /* append on newly arrived data */
1916     {
1917       if (!info->buffer)
1918         {
1919 #ifdef DEBUG_SELECTION
1920           g_message ("Start - Adding %d bytes at offset 0",
1921                      length);
1922 #endif
1923           info->buffer = new_buffer;
1924           info->offset = length;
1925         }
1926       else
1927         {
1928           
1929 #ifdef DEBUG_SELECTION
1930           g_message ("Appending %d bytes at offset %d",
1931                      length,info->offset);
1932 #endif
1933           /* We copy length+1 bytes to preserve guaranteed null termination */
1934           info->buffer = g_realloc (info->buffer, info->offset+length+1);
1935           memcpy (info->buffer + info->offset, new_buffer, length+1);
1936           info->offset += length;
1937           g_free (new_buffer);
1938         }
1939     }
1940   
1941   return TRUE;
1942 }
1943
1944 /*************************************************************
1945  * gtk_selection_retrieval_timeout:
1946  *     Timeout callback while receiving a selection.
1947  *   arguments:
1948  *     info:    Information about this retrieval
1949  *   results:
1950  *************************************************************/
1951
1952 static gint
1953 gtk_selection_retrieval_timeout (GtkRetrievalInfo *info)
1954 {
1955   GList *tmp_list;
1956   gboolean retval;
1957
1958   GDK_THREADS_ENTER ();
1959   
1960   /* Determine if retrieval has finished by checking if it still in
1961      list of pending retrievals */
1962   
1963   tmp_list = current_retrievals;
1964   while (tmp_list)
1965     {
1966       if (info == (GtkRetrievalInfo *)tmp_list->data)
1967         break;
1968       tmp_list = tmp_list->next;
1969     }
1970   
1971   /* If retrieval is finished */
1972   if (!tmp_list || info->idle_time >= IDLE_ABORT_TIME)
1973     {
1974       if (tmp_list && info->idle_time >= IDLE_ABORT_TIME)
1975         {
1976           current_retrievals = g_list_remove_link (current_retrievals, tmp_list);
1977           g_list_free (tmp_list);
1978           gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME);
1979         }
1980       
1981       g_free (info->buffer);
1982       g_free (info);
1983       
1984       retval =  FALSE;          /* remove timeout */
1985     }
1986   else
1987     {
1988       info->idle_time++;
1989       
1990       retval =  TRUE;           /* timeout will happen again */
1991     }
1992
1993   GDK_THREADS_LEAVE ();
1994
1995   return retval;
1996 }
1997
1998 /*************************************************************
1999  * gtk_selection_retrieval_report:
2000  *     Emits a "selection_received" signal.
2001  *   arguments:
2002  *     info:      information about the retrieval that completed
2003  *     buffer:    buffer containing data (NULL => errror)
2004  *     time:      timestamp for data in buffer
2005  *   results:
2006  *************************************************************/
2007
2008 static void
2009 gtk_selection_retrieval_report (GtkRetrievalInfo *info,
2010                                 GdkAtom type, gint format, 
2011                                 guchar *buffer, gint length,
2012                                 guint32 time)
2013 {
2014   GtkSelectionData data;
2015   
2016   data.selection = info->selection;
2017   data.target = info->target;
2018   data.type = type;
2019   data.format = format;
2020   
2021   data.length = length;
2022   data.data = buffer;
2023   data.display = gtk_widget_get_display (info->widget);
2024   
2025   g_signal_emit_by_name (info->widget,
2026                          "selection_received", 
2027                          &data, time);
2028 }
2029
2030 /*************************************************************
2031  * gtk_selection_invoke_handler:
2032  *     Finds and invokes handler for specified
2033  *     widget/selection/target combination, calls 
2034  *     gtk_selection_default_handler if none exists.
2035  *
2036  *   arguments:
2037  *     widget:      selection owner
2038  *     data:        selection data [INOUT]
2039  *     time:        time from requeset
2040  *     
2041  *   results:
2042  *     Number of bytes written to buffer, -1 if error
2043  *************************************************************/
2044
2045 static void
2046 gtk_selection_invoke_handler (GtkWidget        *widget,
2047                               GtkSelectionData *data,
2048                               guint             time)
2049 {
2050   GtkTargetList *target_list;
2051   guint info;
2052   
2053
2054   g_return_if_fail (widget != NULL);
2055
2056   target_list = gtk_selection_target_list_get (widget, data->selection);
2057   if (target_list && 
2058       gtk_target_list_find (target_list, data->target, &info))
2059     {
2060       g_signal_emit_by_name (widget,
2061                              "selection_get",
2062                              data,
2063                              info, time);
2064     }
2065   else
2066     gtk_selection_default_handler (widget, data);
2067 }
2068
2069 /*************************************************************
2070  * gtk_selection_default_handler:
2071  *     Handles some default targets that exist for any widget
2072  *     If it can't fit results into buffer, returns -1. This
2073  *     won't happen in any conceivable case, since it would
2074  *     require 1000 selection targets!
2075  *
2076  *   arguments:
2077  *     widget:      selection owner
2078  *     data:        selection data [INOUT]
2079  *
2080  *************************************************************/
2081
2082 static void
2083 gtk_selection_default_handler (GtkWidget        *widget,
2084                                GtkSelectionData *data)
2085 {
2086   if (data->target == gtk_selection_atoms[TIMESTAMP])
2087     {
2088       /* Time which was used to obtain selection */
2089       GList *tmp_list;
2090       GtkSelectionInfo *selection_info;
2091       
2092       tmp_list = current_selections;
2093       while (tmp_list)
2094         {
2095           selection_info = (GtkSelectionInfo *)tmp_list->data;
2096           if ((selection_info->widget == widget) &&
2097               (selection_info->selection == data->selection))
2098             {
2099               gulong time = selection_info->time;
2100
2101               gtk_selection_data_set (data,
2102                                       GDK_SELECTION_TYPE_INTEGER,
2103                                       32,
2104                                       (guchar *)&time,
2105                                       sizeof (time));
2106               return;
2107             }
2108           
2109           tmp_list = tmp_list->next;
2110         }
2111       
2112       data->length = -1;
2113     }
2114   else if (data->target == gtk_selection_atoms[TARGETS])
2115     {
2116       /* List of all targets supported for this widget/selection pair */
2117       GdkAtom *p;
2118       guint count;
2119       GList *tmp_list;
2120       GtkTargetList *target_list;
2121       GtkTargetPair *pair;
2122       
2123       target_list = gtk_selection_target_list_get (widget,
2124                                                    data->selection);
2125       count = g_list_length (target_list->list) + 3;
2126       
2127       data->type = GDK_SELECTION_TYPE_ATOM;
2128       data->format = 32;
2129       data->length = count * sizeof (GdkAtom);
2130
2131       /* selection data is always terminated by a trailing \0
2132        */
2133       p = g_malloc (data->length + 1);
2134       data->data = (guchar *)p;
2135       data->data[data->length] = '\0';
2136       
2137       *p++ = gtk_selection_atoms[TIMESTAMP];
2138       *p++ = gtk_selection_atoms[TARGETS];
2139       *p++ = gtk_selection_atoms[MULTIPLE];
2140       
2141       tmp_list = target_list->list;
2142       while (tmp_list)
2143         {
2144           pair = (GtkTargetPair *)tmp_list->data;
2145           *p++ = pair->target;
2146           
2147           tmp_list = tmp_list->next;
2148         }
2149     }
2150   else
2151     {
2152       data->length = -1;
2153     }
2154 }
2155
2156
2157 GtkSelectionData*
2158 gtk_selection_data_copy (GtkSelectionData *selection_data)
2159 {
2160   GtkSelectionData *new_data;
2161   
2162   g_return_val_if_fail (selection_data != NULL, NULL);
2163   
2164   new_data = g_new (GtkSelectionData, 1);
2165   *new_data = *selection_data;
2166
2167   if (selection_data->data)
2168     {
2169       new_data->data = g_malloc (selection_data->length + 1);
2170       memcpy (new_data->data, selection_data->data, selection_data->length + 1);
2171     }
2172   
2173   return new_data;
2174 }
2175
2176 void
2177 gtk_selection_data_free (GtkSelectionData *data)
2178 {
2179   g_return_if_fail (data != NULL);
2180   
2181   if (data->data)
2182     g_free (data->data);
2183   
2184   g_free (data);
2185 }
2186
2187 GType
2188 gtk_selection_data_get_type (void)
2189 {
2190   static GType our_type = 0;
2191   
2192   if (our_type == 0)
2193     our_type = g_boxed_type_register_static ("GtkSelectionData",
2194                                              (GBoxedCopyFunc) gtk_selection_data_copy,
2195                                              (GBoxedFreeFunc) gtk_selection_data_free);
2196
2197   return our_type;
2198 }
2199
2200 static int 
2201 gtk_selection_bytes_per_item (gint format)
2202 {
2203   switch (format)
2204     {
2205     case 8:
2206       return sizeof (char);
2207       break;
2208     case 16:
2209       return sizeof (short);
2210       break;
2211     case 32:
2212       return sizeof (long);
2213       break;
2214     default:
2215       g_assert_not_reached();
2216     }
2217   return 0;
2218 }