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