]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkselection-x11.c
Set the property as type ATOM_PAIR, not ATOM. (#72074, Gregory Merchan.)
[~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 <X11/Xlib.h>
28 #include <X11/Xatom.h>
29 #include <string.h>
30
31 #include "gdkproperty.h"
32 #include "gdkselection.h"
33 #include "gdkprivate.h"
34 #include "gdkprivate-x11.h"
35
36 typedef struct _OwnerInfo OwnerInfo;
37
38 struct _OwnerInfo
39 {
40   GdkAtom    selection;
41   GdkWindow *owner;
42   gulong     serial;
43 };
44
45 static GSList *owner_list;
46
47 /* When a window is destroyed we check if it is the owner
48  * of any selections. This is somewhat inefficient, but
49  * owner_list is typically short, and it is a low memory,
50  * low code solution
51  */
52 void
53 _gdk_selection_window_destroyed (GdkWindow *window)
54 {
55   GSList *tmp_list = owner_list;
56   while (tmp_list)
57     {
58       OwnerInfo *info = tmp_list->data;
59       tmp_list = tmp_list->next;
60       
61       if (info->owner == window)
62         {
63           owner_list = g_slist_remove (owner_list, info);
64           g_free (info);
65         }
66     }
67 }
68
69 /* We only pass through those SelectionClear events that actually
70  * reflect changes to the selection owner that we didn't make ourself.
71  */
72 gboolean
73 _gdk_selection_filter_clear_event (XSelectionClearEvent *event)
74 {
75   GSList *tmp_list = owner_list;
76
77   while (tmp_list)
78     {
79       OwnerInfo *info = tmp_list->data;
80       if (info->selection == gdk_x11_xatom_to_atom (event->selection))
81         {
82           if ((GDK_DRAWABLE_XID (info->owner) == event->window &&
83                event->serial >= info->serial))
84             {
85               owner_list = g_slist_remove (owner_list, info);
86               g_free (info);
87               return TRUE;
88             }
89           else
90             return FALSE;
91         }
92       tmp_list = tmp_list->next;
93     }
94
95   return FALSE;
96 }
97
98 gboolean
99 gdk_selection_owner_set (GdkWindow *owner,
100                          GdkAtom    selection,
101                          guint32    time,
102                          gboolean   send_event)
103 {
104   Display *xdisplay;
105   Window xwindow;
106   Atom xselection;
107   GSList *tmp_list;
108   OwnerInfo *info;
109
110   xselection = gdk_x11_atom_to_xatom (selection);
111   
112   if (owner)
113     {
114       if (GDK_WINDOW_DESTROYED (owner))
115         return FALSE;
116
117       xdisplay = GDK_WINDOW_XDISPLAY (owner);
118       xwindow = GDK_WINDOW_XID (owner);
119     }
120   else
121     {
122       xdisplay = gdk_display;
123       xwindow = None;
124     }
125   
126   tmp_list = owner_list;
127   while (tmp_list)
128     {
129       info = tmp_list->data;
130       if (info->selection == selection)
131         {
132           owner_list = g_slist_remove (owner_list, info);
133           g_free (info);
134           break;
135         }
136       tmp_list = tmp_list->next;
137     }
138
139   if (owner)
140     {
141       info = g_new (OwnerInfo, 1);
142       info->owner = owner;
143       info->serial = NextRequest (GDK_WINDOW_XDISPLAY (owner));
144       info->selection = selection;
145
146       owner_list = g_slist_prepend (owner_list, info);
147     }
148
149   XSetSelectionOwner (xdisplay, xselection, xwindow, time);
150
151   return (XGetSelectionOwner (xdisplay, xselection) == xwindow);
152 }
153
154 GdkWindow*
155 gdk_selection_owner_get (GdkAtom selection)
156 {
157   Window xwindow;
158
159   xwindow = XGetSelectionOwner (gdk_display, gdk_x11_atom_to_xatom (selection));
160   if (xwindow == None)
161     return NULL;
162
163   return gdk_window_lookup (xwindow);
164 }
165
166 void
167 gdk_selection_convert (GdkWindow *requestor,
168                        GdkAtom    selection,
169                        GdkAtom    target,
170                        guint32    time)
171 {
172   if (GDK_WINDOW_DESTROYED (requestor))
173     return;
174
175   XConvertSelection (GDK_WINDOW_XDISPLAY (requestor),
176                      gdk_x11_atom_to_xatom (selection),
177                      gdk_x11_atom_to_xatom (target),
178                      gdk_x11_atom_to_xatom (_gdk_selection_property),
179                      GDK_WINDOW_XID (requestor), time);
180 }
181
182 gint
183 gdk_selection_property_get (GdkWindow  *requestor,
184                             guchar    **data,
185                             GdkAtom    *ret_type,
186                             gint       *ret_format)
187 {
188   gulong nitems;
189   gulong nbytes;
190   gulong length;
191   Atom prop_type;
192   gint prop_format;
193   guchar *t = NULL;
194
195   g_return_val_if_fail (requestor != NULL, 0);
196   g_return_val_if_fail (GDK_IS_WINDOW (requestor), 0);
197
198   /* If retrieved chunks are typically small, (and the ICCCM says the
199      should be) it would be a win to try first with a buffer of
200      moderate length, to avoid two round trips to the server */
201
202   if (GDK_WINDOW_DESTROYED (requestor))
203     return 0;
204
205   t = NULL;
206   XGetWindowProperty (GDK_WINDOW_XDISPLAY (requestor),
207                       GDK_WINDOW_XID (requestor),
208                       gdk_x11_atom_to_xatom (_gdk_selection_property),
209                       0, 0, False,
210                       AnyPropertyType, &prop_type, &prop_format,
211                       &nitems, &nbytes, &t);
212
213   if (ret_type)
214     *ret_type = gdk_x11_xatom_to_atom (prop_type);
215   if (ret_format)
216     *ret_format = prop_format;
217
218   if (prop_type == None)
219     {
220       *data = NULL;
221       return 0;
222     }
223   
224   if (t)
225     {
226       XFree (t);
227       t = NULL;
228     }
229
230   /* Add on an extra byte to handle null termination.  X guarantees
231      that t will be 1 longer than nbytes and null terminated */
232   length = nbytes + 1;
233
234   /* We can't delete the selection here, because it might be the INCR
235      protocol, in which case the client has to make sure they'll be
236      notified of PropertyChange events _before_ the property is deleted.
237      Otherwise there's no guarantee we'll win the race ... */
238   XGetWindowProperty (GDK_DRAWABLE_XDISPLAY (requestor),
239                       GDK_DRAWABLE_XID (requestor),
240                       gdk_x11_atom_to_xatom (_gdk_selection_property),
241                       0, (nbytes + 3) / 4, False,
242                       AnyPropertyType, &prop_type, &prop_format,
243                       &nitems, &nbytes, &t);
244
245   if (prop_type != None)
246     {
247       *data = g_new (guchar, length);
248
249       if (prop_type == XA_ATOM || prop_type == gdk_x11_get_xatom_by_name ("ATOM_PAIR"))
250         {
251           Atom* atoms = (Atom*) t;
252           GdkAtom* atoms_dest = (GdkAtom*) *data;
253           gint num_atom, i;
254           
255           num_atom = (length - 1) / sizeof (GdkAtom);
256           for (i=0; i < num_atom; i++)
257             atoms_dest[i] = gdk_x11_xatom_to_atom (atoms[i]);
258         }
259       else
260         {
261           memcpy (*data, t, length);
262         }
263       
264       if (t)
265         XFree (t);
266       return length-1;
267     }
268   else
269     {
270       *data = NULL;
271       return 0;
272     }
273 }
274
275
276 void
277 gdk_selection_send_notify (guint32  requestor,
278                            GdkAtom  selection,
279                            GdkAtom  target,
280                            GdkAtom  property,
281                            guint32  time)
282 {
283   XSelectionEvent xevent;
284
285   xevent.type = SelectionNotify;
286   xevent.serial = 0;
287   xevent.send_event = True;
288   xevent.display = gdk_display;
289   xevent.requestor = requestor;
290   xevent.selection = gdk_x11_atom_to_xatom (selection);
291   xevent.target = gdk_x11_atom_to_xatom (target);
292   xevent.property = gdk_x11_atom_to_xatom (property);
293   xevent.time = time;
294
295   gdk_send_xevent (requestor, False, NoEventMask, (XEvent*) &xevent);
296 }
297
298 gint
299 gdk_text_property_to_text_list (GdkAtom       encoding,
300                                 gint          format, 
301                                 const guchar *text,
302                                 gint          length,
303                                 gchar      ***list)
304 {
305   XTextProperty property;
306   gint count = 0;
307   gint res;
308   gchar **local_list;
309
310   property.value = (guchar *)text;
311   property.encoding = gdk_x11_atom_to_xatom (encoding);
312   property.format = format;
313   property.nitems = length;
314   res = XmbTextPropertyToTextList (GDK_DISPLAY(), &property, &local_list, &count);
315
316   if (res == XNoMemory || res == XLocaleNotSupported || res == XConverterNotFound)
317     {
318       if (list)
319         *list = NULL;
320
321       return 0;
322     }
323   else
324     {
325       if (list)
326         *list = local_list;
327       else
328         XFreeStringList (local_list);
329       
330       return count;
331     }
332 }
333
334 void
335 gdk_free_text_list (gchar **list)
336 {
337   g_return_if_fail (list != NULL);
338
339   XFreeStringList (list);
340 }
341
342 static gint
343 make_list (const gchar  *text,
344            gint          length,
345            gboolean      latin1,
346            gchar      ***list)
347 {
348   GSList *strings = NULL;
349   gint n_strings = 0;
350   gint i;
351   const gchar *p = text;
352   const gchar *q;
353   GSList *tmp_list;
354   GError *error = NULL;
355
356   while (p < text + length)
357     {
358       gchar *str;
359       
360       q = p;
361       while (*q && q < text + length)
362         q++;
363
364       if (latin1)
365         {
366           str = g_convert (p, q - p,
367                            "UTF-8", "ISO-8859-1",
368                            NULL, NULL, &error);
369
370           if (!str)
371             {
372               g_warning ("Error converting selection from STRING: %s",
373                          error->message);
374               g_error_free (error);
375             }
376         }
377       else
378         str = g_strndup (p, q - p);
379
380       if (str)
381         {
382           strings = g_slist_prepend (strings, str);
383           n_strings++;
384         }
385
386       p = q + 1;
387     }
388
389   if (list)
390     *list = g_new (gchar *, n_strings + 1);
391
392   (*list)[n_strings] = NULL;
393   
394   i = n_strings;
395   tmp_list = strings;
396   while (tmp_list)
397     {
398       if (list)
399         (*list)[--i] = tmp_list->data;
400       else
401         g_free (tmp_list->data);
402
403       tmp_list = tmp_list->next;
404     }
405
406   g_slist_free (strings);
407
408   return n_strings;
409 }
410
411 /**
412  * gdk_text_property_to_utf8_list:
413  * @encoding: an atom representing the encoding of the text
414  * @format:   the format of the property
415  * @text:     the text to convert
416  * @length:   the length of @text, in bytes
417  * @list:     location to store the list of strings or %NULL. The
418  *            list should be freed with g_strfreev().
419  * 
420  * Converts a text property in the giving encoding to
421  * a list of UTF-8 strings. 
422  * 
423  * Return value: the number of strings in the resulting
424  *               list.
425  **/
426 gint 
427 gdk_text_property_to_utf8_list (GdkAtom        encoding,
428                                 gint           format,
429                                 const guchar  *text,
430                                 gint           length,
431                                 gchar       ***list)
432 {
433   g_return_val_if_fail (text != NULL, 0);
434   g_return_val_if_fail (length >= 0, 0);
435   
436   if (encoding == GDK_TARGET_STRING)
437     {
438       return make_list ((gchar *)text, length, TRUE, list);
439     }
440   else if (encoding == gdk_atom_intern ("UTF8_STRING", FALSE))
441     {
442       return make_list ((gchar *)text, length, FALSE, list);
443     }
444   else
445     {
446       gchar **local_list;
447       gint local_count;
448       gint i;
449       const gchar *charset = NULL;
450       gboolean need_conversion = !g_get_charset (&charset);
451       gint count = 0;
452       GError *error = NULL;
453       
454       /* Probably COMPOUND text, we fall back to Xlib routines
455        */
456       local_count = gdk_text_property_to_text_list (encoding,
457                                                     format, 
458                                                     text,
459                                                     length,
460                                                     &local_list);
461       if (list)
462         *list = g_new (gchar *, local_count + 1);
463       
464       for (i=0; i<local_count; i++)
465         {
466           /* list contains stuff in our default encoding
467            */
468           if (need_conversion)
469             {
470               gchar *utf = g_convert (local_list[i], -1,
471                                       "UTF-8", charset,
472                                       NULL, NULL, &error);
473               if (utf)
474                 {
475                   if (list)
476                     (*list)[count++] = utf;
477                   else
478                     g_free (utf);
479                 }
480               else
481                 {
482                   g_warning ("Error converting to UTF-8 from '%s': %s",
483                              charset, error->message);
484                   g_error_free (error);
485                   error = NULL;
486                 }
487             }
488           else
489             {
490               if (list)
491                 (*list)[count++] = g_strdup (local_list[i]);
492             }
493         }
494
495       if (local_count)
496         gdk_free_text_list (local_list);
497       
498       (*list)[count] = NULL;
499
500       return count;
501     }
502 }
503
504 gint
505 gdk_string_to_compound_text (const gchar *str,
506                              GdkAtom     *encoding,
507                              gint        *format,
508                              guchar     **ctext,
509                              gint        *length)
510 {
511   gint res;
512   XTextProperty property;
513
514   res = XmbTextListToTextProperty (GDK_DISPLAY(), 
515                                    (char **)&str, 1, XCompoundTextStyle,
516                                    &property);
517   if (res != Success)
518     {
519       property.encoding = None;
520       property.format = None;
521       property.value = NULL;
522       property.nitems = 0;
523     }
524
525   if (encoding)
526     *encoding = gdk_x11_xatom_to_atom (property.encoding);
527   if (format)
528     *format = property.format;
529   if (ctext)
530     *ctext = property.value;
531   if (length)
532     *length = property.nitems;
533
534   return res;
535 }
536
537 /* The specifications for COMPOUND_TEXT and STRING specify that C0 and
538  * C1 are not allowed except for \n and \t, however the X conversions
539  * routines for COMPOUND_TEXT only enforce this in one direction,
540  * causing cut-and-paste of \r and \r\n separated text to fail.
541  * This routine strips out all non-allowed C0 and C1 characters
542  * from the input string and also canonicalizes \r, and \r\n to \n
543  */
544 static gchar * 
545 sanitize_utf8 (const gchar *src)
546 {
547   gint len = strlen (src);
548   GString *result = g_string_sized_new (len);
549   const gchar *p = src;
550
551   while (*p)
552     {
553       if (*p == '\r')
554         {
555           p++;
556           if (*p == '\n')
557             p++;
558
559           g_string_append_c (result, '\n');
560         }
561       else
562         {
563           gunichar ch = g_utf8_get_char (p);
564           char buf[7];
565           gint buflen;
566           
567           if (!((ch < 0x20 && ch != '\t' && ch != '\n') || (ch >= 0x7f && ch < 0xa0)))
568             {
569               buflen = g_unichar_to_utf8 (ch, buf);
570               g_string_append_len (result, buf, buflen);
571             }
572
573           p = g_utf8_next_char (p);
574         }
575     }
576
577   return g_string_free (result, FALSE);
578 }
579
580 /**
581  * gdk_utf8_to_string_target:
582  * @str: a UTF-8 string
583  * 
584  * Converts an UTF-8 string into the best possible representation
585  * as a STRING. The representation of characters not in STRING
586  * is not specified; it may be as pseudo-escape sequences
587  * \x{ABCD}, or it may be in some other form of approximation.
588  * 
589  * Return value: the newly-allocated string, or %NULL if the
590  *               conversion failed. (It should not fail for
591  *               any properly formed UTF-8 string.)
592  **/
593 gchar *
594 gdk_utf8_to_string_target (const gchar *str)
595 {
596   GError *error = NULL;
597   
598   gchar *tmp_str = sanitize_utf8 (str);
599   gchar *result =  g_convert_with_fallback (tmp_str, -1,
600                                             "ISO-8859-1", "UTF-8",
601                                             NULL, NULL, NULL, &error);
602   if (!result)
603     {
604       g_warning ("Error converting from UTF-8 to STRING: %s",
605                  error->message);
606       g_error_free (error);
607     }
608   
609   g_free (tmp_str);
610   return result;
611 }
612
613 /**
614  * gdk_utf8_to_compound_text:
615  * @str:      a UTF-8 string
616  * @encoding: location to store resulting encoding
617  * @format:   location to store format of the result
618  * @ctext:    location to store the data of the result
619  * @length:   location to store the length of the data
620  *            stored in @ctext
621  * 
622  * Converts from UTF-8 to compound text. 
623  * 
624  * Return value: %TRUE if the conversion succeeded, otherwise
625  *               %FALSE.
626  **/
627 gboolean
628 gdk_utf8_to_compound_text (const gchar *str,
629                            GdkAtom     *encoding,
630                            gint        *format,
631                            guchar     **ctext,
632                            gint        *length)
633 {
634   gboolean need_conversion;
635   const gchar *charset;
636   gchar *locale_str, *tmp_str;
637   GError *error = NULL;
638   gboolean result;
639
640   g_return_val_if_fail (str != NULL, FALSE);
641
642   need_conversion = !g_get_charset (&charset);
643
644   tmp_str = sanitize_utf8 (str);
645
646   if (need_conversion)
647     {
648       locale_str = g_convert_with_fallback (tmp_str, -1,
649                                             charset, "UTF-8",
650                                             NULL, NULL, NULL, &error);
651       g_free (tmp_str);
652
653       if (!locale_str)
654         {
655           g_warning ("Error converting from UTF-8 to '%s': %s",
656                      charset, error->message);
657           g_error_free (error);
658
659           if (encoding)
660             *encoding = None;
661           if (format)
662             *format = None;
663           if (ctext)
664             *ctext = NULL;
665           if (length)
666             *length = 0;
667
668           return FALSE;
669         }
670     }
671   else
672     locale_str = tmp_str;
673     
674   result = gdk_string_to_compound_text (locale_str,
675                                         encoding, format, ctext, length);
676   result = (result == Success? TRUE : FALSE);
677   
678   g_free (locale_str);
679
680   return result;
681 }
682
683 void gdk_free_compound_text (guchar *ctext)
684 {
685   if (ctext)
686     XFree (ctext);
687 }