]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkselection-x11.c
Don't access GdkDisplay fields directly
[~andy/gtk] / gdk / x11 / gdkselection-x11.c
1 /* GDK - The GIMP Drawing Kit
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 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28
29 #include "gdkselection.h"
30
31 #include "gdkx.h"
32 #include "gdkproperty.h"
33 #include "gdkprivate.h"
34 #include "gdkprivate-x11.h"
35 #include "gdkdisplay-x11.h"
36
37 #include <X11/Xlib.h>
38 #include <X11/Xatom.h>
39 #include <string.h>
40
41
42 typedef struct _OwnerInfo OwnerInfo;
43
44 struct _OwnerInfo
45 {
46   GdkAtom    selection;
47   GdkWindow *owner;
48   gulong     serial;
49 };
50
51 static GSList *owner_list;
52
53 /* When a window is destroyed we check if it is the owner
54  * of any selections. This is somewhat inefficient, but
55  * owner_list is typically short, and it is a low memory,
56  * low code solution
57  */
58 void
59 _gdk_selection_window_destroyed (GdkWindow *window)
60 {
61   GSList *tmp_list = owner_list;
62   while (tmp_list)
63     {
64       OwnerInfo *info = tmp_list->data;
65       tmp_list = tmp_list->next;
66       
67       if (info->owner == window)
68         {
69           owner_list = g_slist_remove (owner_list, info);
70           g_free (info);
71         }
72     }
73 }
74
75 /* We only pass through those SelectionClear events that actually
76  * reflect changes to the selection owner that we didn't make ourself.
77  */
78 gboolean
79 _gdk_selection_filter_clear_event (XSelectionClearEvent *event)
80 {
81   GSList *tmp_list = owner_list;
82   GdkDisplay *display = gdk_x11_lookup_xdisplay (event->display);
83   
84   while (tmp_list)
85     {
86       OwnerInfo *info = tmp_list->data;
87
88       if (gdk_window_get_display (info->owner) == display &&
89           info->selection == gdk_x11_xatom_to_atom_for_display (display, event->selection))
90         {
91           if ((GDK_WINDOW_XID (info->owner) == event->window &&
92                event->serial >= info->serial))
93             {
94               owner_list = g_slist_remove (owner_list, info);
95               g_free (info);
96               return TRUE;
97             }
98           else
99             return FALSE;
100         }
101       tmp_list = tmp_list->next;
102     }
103
104   return FALSE;
105 }
106 /**
107  * gdk_selection_owner_set_for_display:
108  * @display: the #GdkDisplay.
109  * @owner: a #GdkWindow or %NULL to indicate that the owner for
110  *         the given should be unset.
111  * @selection: an atom identifying a selection.
112  * @time_: timestamp to use when setting the selection. 
113  *         If this is older than the timestamp given last time the owner was 
114  *         set for the given selection, the request will be ignored.
115  * @send_event: if %TRUE, and the new owner is different from the current
116  *              owner, the current owner will be sent a SelectionClear event.
117  *
118  * Sets the #GdkWindow @owner as the current owner of the selection @selection.
119  * 
120  * Returns: %TRUE if the selection owner was successfully changed to owner,
121  *          otherwise %FALSE. 
122  *
123  * Since: 2.2
124  */
125 gboolean
126 gdk_selection_owner_set_for_display (GdkDisplay *display,
127                                      GdkWindow  *owner,
128                                      GdkAtom     selection,
129                                      guint32     time, 
130                                      gboolean    send_event)
131 {
132   Display *xdisplay;
133   Window xwindow;
134   Atom xselection;
135   GSList *tmp_list;
136   OwnerInfo *info;
137
138   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
139   g_return_val_if_fail (selection != GDK_NONE, FALSE);
140
141   if (gdk_display_is_closed (display))
142     return FALSE;
143
144   if (owner) 
145     {
146       if (GDK_WINDOW_DESTROYED (owner) || !GDK_WINDOW_IS_X11 (owner))
147         return FALSE;
148       
149       gdk_window_ensure_native (owner);
150       xdisplay = GDK_WINDOW_XDISPLAY (owner);
151       xwindow = GDK_WINDOW_XID (owner);
152     }
153   else 
154     {
155       xdisplay = GDK_DISPLAY_XDISPLAY (display);
156       xwindow = None;
157     }
158   
159   xselection = gdk_x11_atom_to_xatom_for_display (display, selection);
160
161   tmp_list = owner_list;
162   while (tmp_list)
163     {
164       info = tmp_list->data;
165       if (info->selection == selection) 
166         {
167           owner_list = g_slist_remove (owner_list, info);
168           g_free (info);
169           break;
170         }
171       tmp_list = tmp_list->next;
172     }
173
174   if (owner)
175     {
176       info = g_new (OwnerInfo, 1);
177       info->owner = owner;
178       info->serial = NextRequest (GDK_WINDOW_XDISPLAY (owner));
179       info->selection = selection;
180
181       owner_list = g_slist_prepend (owner_list, info);
182     }
183
184   XSetSelectionOwner (xdisplay, xselection, xwindow, time);
185
186   return (XGetSelectionOwner (xdisplay, xselection) == xwindow);
187 }
188
189 /**
190  * gdk_selection_owner_get_for_display:
191  * @display: a #GdkDisplay.
192  * @selection: an atom indentifying a selection.
193  *
194  * Determine the owner of the given selection.
195  *
196  * Note that the return value may be owned by a different 
197  * process if a foreign window was previously created for that
198  * window, but a new foreign window will never be created by this call. 
199  *
200  * Returns: (transfer none): if there is a selection owner for this window,
201  *    and it is a window known to the current process, the #GdkWindow that
202  *    owns the selection, otherwise %NULL.
203  *
204  * Since: 2.2
205  */ 
206 GdkWindow *
207 gdk_selection_owner_get_for_display (GdkDisplay *display,
208                                      GdkAtom     selection)
209 {
210   Window xwindow;
211   
212   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
213   g_return_val_if_fail (selection != GDK_NONE, NULL);
214
215   if (gdk_display_is_closed (display))
216     return NULL;
217   
218   xwindow = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
219                                 gdk_x11_atom_to_xatom_for_display (display, 
220                                                                    selection));
221   if (xwindow == None)
222     return NULL;
223
224   return gdk_window_lookup_for_display (display, xwindow);
225 }
226
227 void
228 gdk_selection_convert (GdkWindow *requestor,
229                        GdkAtom    selection,
230                        GdkAtom    target,
231                        guint32    time)
232 {
233   GdkDisplay *display;
234
235   g_return_if_fail (selection != GDK_NONE);
236
237   if (GDK_WINDOW_DESTROYED (requestor) || !GDK_WINDOW_IS_X11 (requestor))
238     return;
239
240   gdk_window_ensure_native (requestor);
241   display = GDK_WINDOW_DISPLAY (requestor);
242
243   XConvertSelection (GDK_WINDOW_XDISPLAY (requestor),
244                      gdk_x11_atom_to_xatom_for_display (display, selection),
245                      gdk_x11_atom_to_xatom_for_display (display, target),
246                      gdk_x11_atom_to_xatom_for_display (display, _gdk_selection_property), 
247                      GDK_WINDOW_XID (requestor), time);
248 }
249
250 /**
251  * gdk_selection_property_get:
252  * @requestor: the window on which the data is stored
253  * @data: location to store a pointer to the retrieved data.
254        If the retrieval failed, %NULL we be stored here, otherwise, it
255        will be non-%NULL and the returned data should be freed with g_free()
256        when you are finished using it. The length of the
257        allocated memory is one more than the length
258        of the returned data, and the final byte will always
259        be zero, to ensure nul-termination of strings.
260  * @prop_type: location to store the type of the property.
261  * @prop_format: location to store the format of the property.
262  * 
263  * Retrieves selection data that was stored by the selection
264  * data in response to a call to gdk_selection_convert(). This function
265  * will not be used by applications, who should use the #GtkClipboard
266  * API instead.
267  * 
268  * Return value: the length of the retrieved data.
269  **/
270 gint
271 gdk_selection_property_get (GdkWindow  *requestor,
272                             guchar    **data,
273                             GdkAtom    *ret_type,
274                             gint       *ret_format)
275 {
276   gulong nitems;
277   gulong nbytes;
278   gulong length = 0;            /* Quiet GCC */
279   Atom prop_type;
280   gint prop_format;
281   guchar *t = NULL;
282   GdkDisplay *display; 
283
284   g_return_val_if_fail (requestor != NULL, 0);
285   g_return_val_if_fail (GDK_IS_WINDOW (requestor), 0);
286   g_return_val_if_fail (GDK_WINDOW_IS_X11 (requestor), 0);
287   
288   display = GDK_WINDOW_DISPLAY (requestor);
289
290   if (GDK_WINDOW_DESTROYED (requestor) || !GDK_WINDOW_IS_X11 (requestor))
291     goto err;
292
293   t = NULL;
294
295   /* We can't delete the selection here, because it might be the INCR
296      protocol, in which case the client has to make sure they'll be
297      notified of PropertyChange events _before_ the property is deleted.
298      Otherwise there's no guarantee we'll win the race ... */
299   if (XGetWindowProperty (GDK_WINDOW_XDISPLAY (requestor),
300                           GDK_WINDOW_XID (requestor),
301                           gdk_x11_atom_to_xatom_for_display (display, _gdk_selection_property),
302                           0, 0x1FFFFFFF /* MAXINT32 / 4 */, False, 
303                           AnyPropertyType, &prop_type, &prop_format,
304                           &nitems, &nbytes, &t) != Success)
305     goto err;
306     
307   if (prop_type != None)
308     {
309       if (ret_type)
310         *ret_type = gdk_x11_xatom_to_atom_for_display (display, prop_type);
311       if (ret_format)
312         *ret_format = prop_format;
313
314       if (prop_type == XA_ATOM ||
315           prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
316         {
317           Atom* atoms = (Atom*) t;
318           GdkAtom* atoms_dest;
319           gint num_atom, i;
320
321           if (prop_format != 32)
322             goto err;
323           
324           num_atom = nitems;
325           length = sizeof (GdkAtom) * num_atom + 1;
326
327           if (data)
328             {
329               *data = g_malloc (length);
330               (*data)[length - 1] = '\0';
331               atoms_dest = (GdkAtom *)(*data);
332           
333               for (i=0; i < num_atom; i++)
334                 atoms_dest[i] = gdk_x11_xatom_to_atom_for_display (display, atoms[i]);
335             }
336         }
337       else
338         {
339           switch (prop_format)
340             {
341             case 8:
342               length = nitems;
343               break;
344             case 16:
345               length = sizeof(short) * nitems;
346               break;
347             case 32:
348               length = sizeof(long) * nitems;
349               break;
350             default:
351               g_assert_not_reached ();
352               break;
353             }
354           
355           /* Add on an extra byte to handle null termination.  X guarantees
356              that t will be 1 longer than nitems and null terminated */
357           length += 1;
358
359           if (data)
360             *data = g_memdup (t, length);
361         }
362       
363       if (t)
364         XFree (t);
365       
366       return length - 1;
367     }
368
369  err:
370   if (ret_type)
371     *ret_type = GDK_NONE;
372   if (ret_format)
373     *ret_format = 0;
374   if (data)
375     *data = NULL;
376   
377   return 0;
378 }
379
380 /**
381  * gdk_selection_send_notify_for_display:
382  * @display: the #GdkDisplay where @requestor is realized
383  * @requestor: window to which to deliver response.
384  * @selection: selection that was requested.
385  * @target: target that was selected.
386  * @property: property in which the selection owner stored the data,
387  *            or %GDK_NONE to indicate that the request was rejected.
388  * @time_: timestamp. 
389  *
390  * Send a response to SelectionRequest event.
391  *
392  * Since: 2.2
393  **/
394 void
395 gdk_selection_send_notify_for_display (GdkDisplay       *display,
396                                        GdkNativeWindow  requestor,
397                                        GdkAtom          selection,
398                                        GdkAtom          target,
399                                        GdkAtom          property, 
400                                        guint32          time)
401 {
402   XSelectionEvent xevent;
403   
404   g_return_if_fail (GDK_IS_DISPLAY (display));
405   
406   xevent.type = SelectionNotify;
407   xevent.serial = 0;
408   xevent.send_event = True;
409   xevent.requestor = requestor;
410   xevent.selection = gdk_x11_atom_to_xatom_for_display (display, selection);
411   xevent.target = gdk_x11_atom_to_xatom_for_display (display, target);
412   if (property == GDK_NONE)
413     xevent.property = None;
414   else
415     xevent.property = gdk_x11_atom_to_xatom_for_display (display, property);
416   xevent.time = time;
417
418   _gdk_send_xevent (display, requestor, False, NoEventMask, (XEvent*) & xevent);
419 }
420
421 /**
422  * gdk_text_property_to_text_list_for_display:
423  * @display: The #GdkDisplay where the encoding is defined.
424  * @encoding: an atom representing the encoding. The most 
425  *    common values for this are STRING, or COMPOUND_TEXT. 
426  *    This is value used as the type for the property.
427  * @format: the format of the property.
428  * @text: The text data.
429  * @length: The number of items to transform.
430  * @list: location to store a terminated array of strings in 
431  *    the encoding of the current locale. This array should be 
432  *    freed using gdk_free_text_list().
433  *
434  * Convert a text string from the encoding as it is stored 
435  * in a property into an array of strings in the encoding of
436  * the current locale. (The elements of the array represent the
437  * nul-separated elements of the original text string.)
438  *
439  * Returns: the number of strings stored in list, or 0, 
440  * if the conversion failed. 
441  *
442  * Since: 2.2
443  */
444 gint
445 gdk_text_property_to_text_list_for_display (GdkDisplay   *display,
446                                             GdkAtom       encoding,
447                                             gint          format, 
448                                             const guchar *text,
449                                             gint          length,
450                                             gchar      ***list)
451 {
452   XTextProperty property;
453   gint count = 0;
454   gint res;
455   gchar **local_list;
456   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
457
458   if (list)
459     *list = NULL;
460
461   if (gdk_display_is_closed (display))
462     return 0;
463
464   property.value = (guchar *)text;
465   property.encoding = gdk_x11_atom_to_xatom_for_display (display, encoding);
466   property.format = format;
467   property.nitems = length;
468   res = XmbTextPropertyToTextList (GDK_DISPLAY_XDISPLAY (display), &property, 
469                                    &local_list, &count);
470   if (res == XNoMemory || res == XLocaleNotSupported || res == XConverterNotFound)
471     return 0;
472   else
473     {
474       if (list)
475         *list = local_list;
476       else
477         XFreeStringList (local_list);
478       
479       return count;
480     }
481 }
482
483 /**
484  * gdk_free_text_list:
485  * @list: the value stored in the @list parameter by
486  *   a call to gdk_text_property_to_text_list().
487  *
488  * Frees the array of strings created by
489  * gdk_text_property_to_text_list().
490  */
491 void
492 gdk_free_text_list (gchar **list)
493 {
494   g_return_if_fail (list != NULL);
495
496   XFreeStringList (list);
497 }
498
499 static gint
500 make_list (const gchar  *text,
501            gint          length,
502            gboolean      latin1,
503            gchar      ***list)
504 {
505   GSList *strings = NULL;
506   gint n_strings = 0;
507   gint i;
508   const gchar *p = text;
509   const gchar *q;
510   GSList *tmp_list;
511   GError *error = NULL;
512
513   while (p < text + length)
514     {
515       gchar *str;
516       
517       q = p;
518       while (*q && q < text + length)
519         q++;
520
521       if (latin1)
522         {
523           str = g_convert (p, q - p,
524                            "UTF-8", "ISO-8859-1",
525                            NULL, NULL, &error);
526
527           if (!str)
528             {
529               g_warning ("Error converting selection from STRING: %s",
530                          error->message);
531               g_error_free (error);
532             }
533         }
534       else
535         {
536           str = g_strndup (p, q - p);
537           if (!g_utf8_validate (str, -1, NULL))
538             {
539               g_warning ("Error converting selection from UTF8_STRING");
540               g_free (str);
541               str = NULL;
542             }
543         }
544
545       if (str)
546         {
547           strings = g_slist_prepend (strings, str);
548           n_strings++;
549         }
550
551       p = q + 1;
552     }
553
554   if (list)
555     {
556       *list = g_new (gchar *, n_strings + 1);
557       (*list)[n_strings] = NULL;
558     }
559      
560   i = n_strings;
561   tmp_list = strings;
562   while (tmp_list)
563     {
564       if (list)
565         (*list)[--i] = tmp_list->data;
566       else
567         g_free (tmp_list->data);
568       
569       tmp_list = tmp_list->next;
570     }
571   
572   g_slist_free (strings);
573
574   return n_strings;
575 }
576
577 /**
578  * gdk_text_property_to_utf8_list_for_display:
579  * @display:  a #GdkDisplay
580  * @encoding: an atom representing the encoding of the text
581  * @format:   the format of the property
582  * @text:     the text to convert
583  * @length:   the length of @text, in bytes
584  * @list:     location to store the list of strings or %NULL. The
585  *            list should be freed with g_strfreev().
586  * 
587  * Converts a text property in the given encoding to
588  * a list of UTF-8 strings. 
589  * 
590  * Return value: the number of strings in the resulting
591  *               list.
592  *
593  * Since: 2.2
594  **/
595 gint 
596 gdk_text_property_to_utf8_list_for_display (GdkDisplay    *display,
597                                             GdkAtom        encoding,
598                                             gint           format,
599                                             const guchar  *text,
600                                             gint           length,
601                                             gchar       ***list)
602 {
603   g_return_val_if_fail (text != NULL, 0);
604   g_return_val_if_fail (length >= 0, 0);
605   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
606   
607   if (encoding == GDK_TARGET_STRING)
608     {
609       return make_list ((gchar *)text, length, TRUE, list);
610     }
611   else if (encoding == gdk_atom_intern_static_string ("UTF8_STRING"))
612     {
613       return make_list ((gchar *)text, length, FALSE, list);
614     }
615   else
616     {
617       gchar **local_list;
618       gint local_count;
619       gint i;
620       const gchar *charset = NULL;
621       gboolean need_conversion = !g_get_charset (&charset);
622       gint count = 0;
623       GError *error = NULL;
624       
625       /* Probably COMPOUND text, we fall back to Xlib routines
626        */
627       local_count = gdk_text_property_to_text_list_for_display (display,
628                                                                 encoding,
629                                                                 format, 
630                                                                 text,
631                                                                 length,
632                                                                 &local_list);
633       if (list)
634         *list = g_new (gchar *, local_count + 1);
635       
636       for (i=0; i<local_count; i++)
637         {
638           /* list contains stuff in our default encoding
639            */
640           if (need_conversion)
641             {
642               gchar *utf = g_convert (local_list[i], -1,
643                                       "UTF-8", charset,
644                                       NULL, NULL, &error);
645               if (utf)
646                 {
647                   if (list)
648                     (*list)[count++] = utf;
649                   else
650                     g_free (utf);
651                 }
652               else
653                 {
654                   g_warning ("Error converting to UTF-8 from '%s': %s",
655                              charset, error->message);
656                   g_error_free (error);
657                   error = NULL;
658                 }
659             }
660           else
661             {
662               if (list)
663                 {
664                   if (g_utf8_validate (local_list[i], -1, NULL))
665                     (*list)[count++] = g_strdup (local_list[i]);
666                   else
667                     g_warning ("Error converting selection");
668                 }
669             }
670         }
671
672       if (local_count)
673         gdk_free_text_list (local_list);
674       
675       if (list)
676         (*list)[count] = NULL;
677
678       return count;
679     }
680 }
681
682 /**
683  * gdk_string_to_compound_text_for_display:
684  * @display:  the #GdkDisplay where the encoding is defined.
685  * @str:      a nul-terminated string.
686  * @encoding: location to store the encoding atom 
687  *            (to be used as the type for the property).
688  * @format:   location to store the format of the property
689  * @ctext:    location to store newly allocated data for the property.
690  * @length:   the length of @text, in bytes
691  * 
692  * Convert a string from the encoding of the current 
693  * locale into a form suitable for storing in a window property.
694  * 
695  * Returns: 0 upon success, non-zero upon failure. 
696  *
697  * Since: 2.2
698  **/
699 gint
700 gdk_string_to_compound_text_for_display (GdkDisplay  *display,
701                                          const gchar *str,
702                                          GdkAtom     *encoding,
703                                          gint        *format,
704                                          guchar     **ctext,
705                                          gint        *length)
706 {
707   gint res;
708   XTextProperty property;
709
710   g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
711
712   if (gdk_display_is_closed (display))
713     res = XLocaleNotSupported;
714   else
715     res = XmbTextListToTextProperty (GDK_DISPLAY_XDISPLAY (display), 
716                                      (char **)&str, 1, XCompoundTextStyle,
717                                      &property);
718   if (res != Success)
719     {
720       property.encoding = None;
721       property.format = None;
722       property.value = NULL;
723       property.nitems = 0;
724     }
725
726   if (encoding)
727     *encoding = gdk_x11_xatom_to_atom_for_display (display, property.encoding);
728   if (format)
729     *format = property.format;
730   if (ctext)
731     *ctext = property.value;
732   if (length)
733     *length = property.nitems;
734
735   return res;
736 }
737
738 /* The specifications for COMPOUND_TEXT and STRING specify that C0 and
739  * C1 are not allowed except for \n and \t, however the X conversions
740  * routines for COMPOUND_TEXT only enforce this in one direction,
741  * causing cut-and-paste of \r and \r\n separated text to fail.
742  * This routine strips out all non-allowed C0 and C1 characters
743  * from the input string and also canonicalizes \r, and \r\n to \n
744  */
745 static gchar * 
746 sanitize_utf8 (const gchar *src,
747                gboolean return_latin1)
748 {
749   gint len = strlen (src);
750   GString *result = g_string_sized_new (len);
751   const gchar *p = src;
752
753   while (*p)
754     {
755       if (*p == '\r')
756         {
757           p++;
758           if (*p == '\n')
759             p++;
760
761           g_string_append_c (result, '\n');
762         }
763       else
764         {
765           gunichar ch = g_utf8_get_char (p);
766           
767           if (!((ch < 0x20 && ch != '\t' && ch != '\n') || (ch >= 0x7f && ch < 0xa0)))
768             {
769               if (return_latin1)
770                 {
771                   if (ch <= 0xff)
772                     g_string_append_c (result, ch);
773                   else
774                     g_string_append_printf (result,
775                                             ch < 0x10000 ? "\\u%04x" : "\\U%08x",
776                                             ch);
777                 }
778               else
779                 {
780                   char buf[7];
781                   gint buflen;
782                   
783                   buflen = g_unichar_to_utf8 (ch, buf);
784                   g_string_append_len (result, buf, buflen);
785                 }
786             }
787
788           p = g_utf8_next_char (p);
789         }
790     }
791
792   return g_string_free (result, FALSE);
793 }
794
795 /**
796  * gdk_utf8_to_string_target:
797  * @str: a UTF-8 string
798  * 
799  * Converts an UTF-8 string into the best possible representation
800  * as a STRING. The representation of characters not in STRING
801  * is not specified; it may be as pseudo-escape sequences
802  * \x{ABCD}, or it may be in some other form of approximation.
803  * 
804  * Return value: the newly-allocated string, or %NULL if the
805  *               conversion failed. (It should not fail for
806  *               any properly formed UTF-8 string unless system
807  *               limits like memory or file descriptors are exceeded.)
808  **/
809 gchar *
810 gdk_utf8_to_string_target (const gchar *str)
811 {
812   return sanitize_utf8 (str, TRUE);
813 }
814
815 /**
816  * gdk_utf8_to_compound_text_for_display:
817  * @display:  a #GdkDisplay
818  * @str:      a UTF-8 string
819  * @encoding: location to store resulting encoding
820  * @format:   location to store format of the result
821  * @ctext:    location to store the data of the result
822  * @length:   location to store the length of the data
823  *            stored in @ctext
824  * 
825  * Converts from UTF-8 to compound text. 
826  * 
827  * Return value: %TRUE if the conversion succeeded, otherwise
828  *               %FALSE.
829  *
830  * Since: 2.2
831  **/
832 gboolean
833 gdk_utf8_to_compound_text_for_display (GdkDisplay  *display,
834                                        const gchar *str,
835                                        GdkAtom     *encoding,
836                                        gint        *format,
837                                        guchar     **ctext,
838                                        gint        *length)
839 {
840   gboolean need_conversion;
841   const gchar *charset;
842   gchar *locale_str, *tmp_str;
843   GError *error = NULL;
844   gboolean result;
845
846   g_return_val_if_fail (str != NULL, FALSE);
847   g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
848
849   need_conversion = !g_get_charset (&charset);
850
851   tmp_str = sanitize_utf8 (str, FALSE);
852
853   if (need_conversion)
854     {
855       locale_str = g_convert (tmp_str, -1,
856                               charset, "UTF-8",
857                               NULL, NULL, &error);
858       g_free (tmp_str);
859
860       if (!locale_str)
861         {
862           if (!(error->domain = G_CONVERT_ERROR &&
863                 error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
864             {
865               g_warning ("Error converting from UTF-8 to '%s': %s",
866                          charset, error->message);
867             }
868           g_error_free (error);
869
870           if (encoding)
871             *encoding = None;
872           if (format)
873             *format = None;
874           if (ctext)
875             *ctext = NULL;
876           if (length)
877             *length = 0;
878
879           return FALSE;
880         }
881     }
882   else
883     locale_str = tmp_str;
884     
885   result = gdk_string_to_compound_text_for_display (display, locale_str,
886                                                     encoding, format, 
887                                                     ctext, length);
888   result = (result == Success? TRUE : FALSE);
889   
890   g_free (locale_str);
891
892   return result;
893 }
894
895 /**
896  * gdk_free_compound_text:
897  * @ctext: The pointer stored in @ctext from a call to
898  *   gdk_string_to_compound_text().
899  *
900  * Frees the data returned from gdk_string_to_compound_text().
901  */
902 void gdk_free_compound_text (guchar *ctext)
903 {
904   if (ctext)
905     XFree (ctext);
906 }