gdkselection-x11.c 23.1 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1
2
3
4
/* GDK - The GIMP Drawing Kit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6
7
8
9
10
11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
16
17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19
20

/*
21
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22
23
24
25
26
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

27
#include "config.h"
28
29

#include "gdkselection.h"
Owen Taylor's avatar
Started    
Owen Taylor committed
30

31
#include "gdkx.h"
Owen Taylor's avatar
Started    
Owen Taylor committed
32
#include "gdkproperty.h"
Elliot Lee's avatar
Elliot Lee committed
33
#include "gdkprivate.h"
34
#include "gdkprivate-x11.h"
35
#include "gdkdisplay-x11.h"
36

37
38
39
40
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <string.h>

Elliot Lee's avatar
Elliot Lee committed
41

42
43
44
45
46
47
48
49
50
typedef struct _OwnerInfo OwnerInfo;

struct _OwnerInfo
{
  GdkAtom    selection;
  GdkWindow *owner;
  gulong     serial;
};

51
static GSList *owner_list;
52
53
54
55
56
57
58
59
60
61
62
63
64

/* When a window is destroyed we check if it is the owner
 * of any selections. This is somewhat inefficient, but
 * owner_list is typically short, and it is a low memory,
 * low code solution
 */
void
_gdk_selection_window_destroyed (GdkWindow *window)
{
  GSList *tmp_list = owner_list;
  while (tmp_list)
    {
      OwnerInfo *info = tmp_list->data;
65
66
      tmp_list = tmp_list->next;
      
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
      if (info->owner == window)
	{
	  owner_list = g_slist_remove (owner_list, info);
	  g_free (info);
	}
    }
}

/* We only pass through those SelectionClear events that actually
 * reflect changes to the selection owner that we didn't make ourself.
 */
gboolean
_gdk_selection_filter_clear_event (XSelectionClearEvent *event)
{
  GSList *tmp_list = owner_list;
82
83
  GdkDisplay *display = gdk_x11_lookup_xdisplay (event->display);
  
84
85
86
  while (tmp_list)
    {
      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
93
94
95
96
97
98
99
100
101
102
103
104
105
	       event->serial >= info->serial))
	    {
	      owner_list = g_slist_remove (owner_list, info);
	      g_free (info);
	      return TRUE;
	    }
	  else
	    return FALSE;
	}
      tmp_list = tmp_list->next;
    }

  return FALSE;
}
106
107
/**
 * gdk_selection_owner_set_for_display:
Matthias Clasen's avatar
Matthias Clasen committed
108
 * @display: the #GdkDisplay.
109
 * @owner: a #GdkWindow or %NULL to indicate that the owner for
Matthias Clasen's avatar
Matthias Clasen committed
110
111
112
113
114
115
116
 *         the given should be unset.
 * @selection: an atom identifying a selection.
 * @time_: timestamp to use when setting the selection. 
 *         If this is older than the timestamp given last time the owner was 
 *         set for the given selection, the request will be ignored.
 * @send_event: if %TRUE, and the new owner is different from the current
 *              owner, the current owner will be sent a SelectionClear event.
117
118
119
 *
 * Sets the #GdkWindow @owner as the current owner of the selection @selection.
 * 
Matthias Clasen's avatar
Matthias Clasen committed
120
121
122
123
 * Returns: %TRUE if the selection owner was successfully changed to owner,
 *	    otherwise %FALSE. 
 *
 * Since: 2.2
124
 */
Owen Taylor's avatar
Owen Taylor committed
125
gboolean
126
127
128
129
130
gdk_selection_owner_set_for_display (GdkDisplay *display,
				     GdkWindow  *owner,
				     GdkAtom     selection,
				     guint32     time, 
				     gboolean    send_event)
Elliot Lee's avatar
Elliot Lee committed
131
132
133
{
  Display *xdisplay;
  Window xwindow;
134
  Atom xselection;
135
136
  GSList *tmp_list;
  OwnerInfo *info;
Elliot Lee's avatar
Elliot Lee committed
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
143
    return FALSE;

144
  if (owner) 
Elliot Lee's avatar
Elliot Lee committed
145
    {
146
      if (GDK_WINDOW_DESTROYED (owner) || !GDK_WINDOW_IS_X11 (owner))
147
	return FALSE;
148
      
149
      gdk_window_ensure_native (owner);
150
151
      xdisplay = GDK_WINDOW_XDISPLAY (owner);
      xwindow = GDK_WINDOW_XID (owner);
Elliot Lee's avatar
Elliot Lee committed
152
    }
153
  else 
Elliot Lee's avatar
Elliot Lee committed
154
    {
155
      xdisplay = GDK_DISPLAY_XDISPLAY (display);
Elliot Lee's avatar
Elliot Lee committed
156
157
      xwindow = None;
    }
158
  
159
160
  xselection = gdk_x11_atom_to_xatom_for_display (display, selection);

161
162
163
164
  tmp_list = owner_list;
  while (tmp_list)
    {
      info = tmp_list->data;
165
      if (info->selection == selection) 
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
	{
	  owner_list = g_slist_remove (owner_list, info);
	  g_free (info);
	  break;
	}
      tmp_list = tmp_list->next;
    }

  if (owner)
    {
      info = g_new (OwnerInfo, 1);
      info->owner = owner;
      info->serial = NextRequest (GDK_WINDOW_XDISPLAY (owner));
      info->selection = selection;

      owner_list = g_slist_prepend (owner_list, info);
    }
Elliot Lee's avatar
Elliot Lee committed
183

184
  XSetSelectionOwner (xdisplay, xselection, xwindow, time);
Elliot Lee's avatar
Elliot Lee committed
185

186
  return (XGetSelectionOwner (xdisplay, xselection) == xwindow);
Elliot Lee's avatar
Elliot Lee committed
187
188
}

189
/**
Matthias Clasen's avatar
Matthias Clasen committed
190
191
192
 * gdk_selection_owner_get_for_display:
 * @display: a #GdkDisplay.
 * @selection: an atom indentifying a selection.
193
194
195
 *
 * Determine the owner of the given selection.
 *
Owen Taylor's avatar
Owen Taylor committed
196
 * Note that the return value may be owned by a different 
197
198
199
 * process if a foreign window was previously created for that
 * window, but a new foreign window will never be created by this call. 
 *
200
201
202
 * Returns: (transfer none): if there is a selection owner for this window,
 *    and it is a window known to the current process, the #GdkWindow that
 *    owns the selection, otherwise %NULL.
Matthias Clasen's avatar
Matthias Clasen committed
203
204
 *
 * Since: 2.2
205
206
207
208
 */ 
GdkWindow *
gdk_selection_owner_get_for_display (GdkDisplay *display,
				     GdkAtom     selection)
Elliot Lee's avatar
Elliot Lee committed
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
217
    return NULL;
  
218
219
220
  xwindow = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
				gdk_x11_atom_to_xatom_for_display (display, 
								   selection));
Elliot Lee's avatar
Elliot Lee committed
221
222
223
  if (xwindow == None)
    return NULL;

224
  return gdk_x11_window_lookup_for_display (display, xwindow);
225
226
}

Elliot Lee's avatar
Elliot Lee committed
227
228
229
230
231
232
void
gdk_selection_convert (GdkWindow *requestor,
		       GdkAtom    selection,
		       GdkAtom    target,
		       guint32    time)
{
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;
Elliot Lee's avatar
Elliot Lee committed
239

240
  gdk_window_ensure_native (requestor);
241
242
  display = GDK_WINDOW_DISPLAY (requestor);

243
  XConvertSelection (GDK_WINDOW_XDISPLAY (requestor),
244
245
246
247
                     gdk_x11_atom_to_xatom_for_display (display, selection),
                     gdk_x11_atom_to_xatom_for_display (display, target),
                     gdk_x11_get_xatom_by_name_for_display (display, "GDK_SELECTION"),
                     GDK_WINDOW_XID (requestor), time);
Elliot Lee's avatar
Elliot Lee committed
248
249
}

250
251
252
253
254
255
256
/**
 * gdk_selection_property_get:
 * @requestor: the window on which the data is stored
 * @data: location to store a pointer to the retrieved data.
       If the retrieval failed, %NULL we be stored here, otherwise, it
       will be non-%NULL and the returned data should be freed with g_free()
       when you are finished using it. The length of the
257
       allocated memory is one more than the length
258
259
260
261
262
263
264
265
266
267
268
269
       of the returned data, and the final byte will always
       be zero, to ensure nul-termination of strings.
 * @prop_type: location to store the type of the property.
 * @prop_format: location to store the format of the property.
 * 
 * Retrieves selection data that was stored by the selection
 * data in response to a call to gdk_selection_convert(). This function
 * will not be used by applications, who should use the #GtkClipboard
 * API instead.
 * 
 * Return value: the length of the retrieved data.
 **/
Elliot Lee's avatar
Elliot Lee committed
270
271
272
273
274
275
276
277
gint
gdk_selection_property_get (GdkWindow  *requestor,
			    guchar    **data,
			    GdkAtom    *ret_type,
			    gint       *ret_format)
{
  gulong nitems;
  gulong nbytes;
278
  gulong length = 0;		/* Quiet GCC */
279
  Atom prop_type;
Elliot Lee's avatar
Elliot Lee committed
280
  gint prop_format;
281
  guchar *t = NULL;
282
  GdkDisplay *display; 
Elliot Lee's avatar
Elliot Lee committed
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);
Elliot Lee's avatar
Elliot Lee committed
289

290
  if (GDK_WINDOW_DESTROYED (requestor) || !GDK_WINDOW_IS_X11 (requestor))
291
    goto err;
Elliot Lee's avatar
Elliot Lee committed
292

293
  t = NULL;
Elliot Lee's avatar
Elliot Lee committed
294
295
296
297
298

  /* We can't delete the selection here, because it might be the INCR
     protocol, in which case the client has to make sure they'll be
     notified of PropertyChange events _before_ the property is deleted.
     Otherwise there's no guarantee we'll win the race ... */
299
  if (XGetWindowProperty (GDK_WINDOW_XDISPLAY (requestor),
300
301
302
303
304
                          GDK_WINDOW_XID (requestor),
                          gdk_x11_get_xatom_by_name_for_display (display, "GDK_SELECTION"),
                          0, 0x1FFFFFFF /* MAXINT32 / 4 */, False,
                          AnyPropertyType, &prop_type, &prop_format,
                          &nitems, &nbytes, &t) != Success)
305
306
    goto err;
    
Elliot Lee's avatar
Elliot Lee committed
307
308
  if (prop_type != None)
    {
309
310
311
312
313
      if (ret_type)
	*ret_type = gdk_x11_xatom_to_atom_for_display (display, prop_type);
      if (ret_format)
	*ret_format = prop_format;

314
315
      if (prop_type == XA_ATOM ||
	  prop_type == gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR"))
316
	{
317
318
319
320
321
322
323
324
325
326
327
	  Atom* atoms = (Atom*) t;
	  GdkAtom* atoms_dest;
	  gint num_atom, i;

	  if (prop_format != 32)
	    goto err;
	  
	  num_atom = nitems;
	  length = sizeof (GdkAtom) * num_atom + 1;

	  if (data)
328
329
330
331
	    {
	      *data = g_malloc (length);
	      (*data)[length - 1] = '\0';
	      atoms_dest = (GdkAtom *)(*data);
332
	  
333
334
335
	      for (i=0; i < num_atom; i++)
		atoms_dest[i] = gdk_x11_xatom_to_atom_for_display (display, atoms[i]);
	    }
336
337
338
339
	}
      else
	{
	  switch (prop_format)
340
	    {
341
342
343
344
345
346
347
348
349
350
351
352
	    case 8:
	      length = nitems;
	      break;
	    case 16:
	      length = sizeof(short) * nitems;
	      break;
	    case 32:
	      length = sizeof(long) * nitems;
	      break;
	    default:
	      g_assert_not_reached ();
	      break;
353
	    }
354
355
356
357
358
359
360
	  
	  /* Add on an extra byte to handle null termination.  X guarantees
	     that t will be 1 longer than nitems and null terminated */
	  length += 1;

	  if (data)
	    *data = g_memdup (t, length);
361
362
	}
      
363
364
      if (t)
	XFree (t);
365
366
      
      return length - 1;
Elliot Lee's avatar
Elliot Lee committed
367
    }
368
369
370
371
372
373
374
375
376
377

 err:
  if (ret_type)
    *ret_type = GDK_NONE;
  if (ret_format)
    *ret_format = 0;
  if (data)
    *data = NULL;
  
  return 0;
Elliot Lee's avatar
Elliot Lee committed
378
379
}

380
/**
Matthias Clasen's avatar
Matthias Clasen committed
381
 * gdk_selection_send_notify_for_display:
Matthias Clasen's avatar
Matthias Clasen committed
382
383
384
385
386
387
388
 * @display: the #GdkDisplay where @requestor is realized
 * @requestor: window to which to deliver response.
 * @selection: selection that was requested.
 * @target: target that was selected.
 * @property: property in which the selection owner stored the data,
 *            or %GDK_NONE to indicate that the request was rejected.
 * @time_: timestamp. 
389
390
 *
 * Send a response to SelectionRequest event.
Matthias Clasen's avatar
Matthias Clasen committed
391
392
 *
 * Since: 2.2
393
 **/
Elliot Lee's avatar
Elliot Lee committed
394
void
395
396
397
398
399
400
gdk_selection_send_notify_for_display (GdkDisplay       *display,
				       GdkNativeWindow  requestor,
				       GdkAtom          selection,
				       GdkAtom          target,
				       GdkAtom          property, 
				       guint32          time)
Elliot Lee's avatar
Elliot Lee committed
401
402
{
  XSelectionEvent xevent;
403
404
  
  g_return_if_fail (GDK_IS_DISPLAY (display));
405
  
Elliot Lee's avatar
Elliot Lee committed
406
407
408
409
  xevent.type = SelectionNotify;
  xevent.serial = 0;
  xevent.send_event = True;
  xevent.requestor = requestor;
410
411
  xevent.selection = gdk_x11_atom_to_xatom_for_display (display, selection);
  xevent.target = gdk_x11_atom_to_xatom_for_display (display, target);
Matthias Clasen's avatar
Matthias Clasen committed
412
413
414
415
  if (property == GDK_NONE)
    xevent.property = None;
  else
    xevent.property = gdk_x11_atom_to_xatom_for_display (display, property);
Elliot Lee's avatar
Elliot Lee committed
416
417
  xevent.time = time;

418
  _gdk_x11_display_send_xevent (display, requestor, False, NoEventMask, (XEvent*) & xevent);
Elliot Lee's avatar
Elliot Lee committed
419
}
420

421
422
423
424
/**
 * gdk_text_property_to_text_list_for_display:
 * @display: The #GdkDisplay where the encoding is defined.
 * @encoding: an atom representing the encoding. The most 
Matthias Clasen's avatar
Matthias Clasen committed
425
426
 *    common values for this are STRING, or COMPOUND_TEXT. 
 *    This is value used as the type for the property.
427
428
429
430
 * @format: the format of the property.
 * @text: The text data.
 * @length: The number of items to transform.
 * @list: location to store a terminated array of strings in 
Matthias Clasen's avatar
Matthias Clasen committed
431
432
 *    the encoding of the current locale. This array should be 
 *    freed using gdk_free_text_list().
433
434
435
 *
 * Convert a text string from the encoding as it is stored 
 * in a property into an array of strings in the encoding of
Matthias Clasen's avatar
Matthias Clasen committed
436
437
 * the current locale. (The elements of the array represent the
 * nul-separated elements of the original text string.)
438
 *
Matthias Clasen's avatar
Matthias Clasen committed
439
 * Returns: the number of strings stored in list, or 0, 
440
 * if the conversion failed. 
Matthias Clasen's avatar
Matthias Clasen committed
441
442
 *
 * Since: 2.2
443
 */
444
gint
445
446
447
448
449
450
gdk_text_property_to_text_list_for_display (GdkDisplay   *display,
					    GdkAtom       encoding,
					    gint          format, 
					    const guchar *text,
					    gint          length,
					    gchar      ***list)
451
452
453
454
{
  XTextProperty property;
  gint count = 0;
  gint res;
455
  gchar **local_list;
456
  g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
457

458
459
460
  if (list)
    *list = NULL;

461
  if (gdk_display_is_closed (display))
462
463
    return 0;

Owen Taylor's avatar
Owen Taylor committed
464
  property.value = (guchar *)text;
465
  property.encoding = gdk_x11_atom_to_xatom_for_display (display, encoding);
466
467
  property.format = format;
  property.nitems = length;
468
469
  res = XmbTextPropertyToTextList (GDK_DISPLAY_XDISPLAY (display), &property, 
				   &local_list, &count);
470
471
  if (res == XNoMemory || res == XLocaleNotSupported || res == XConverterNotFound)
    return 0;
472
  else
473
474
475
476
477
478
479
480
    {
      if (list)
	*list = local_list;
      else
	XFreeStringList (local_list);
      
      return count;
    }
481
482
}

483
484
485
486
487
488
489
490
/**
 * gdk_free_text_list:
 * @list: the value stored in the @list parameter by
 *   a call to gdk_text_property_to_text_list().
 *
 * Frees the array of strings created by
 * gdk_text_property_to_text_list().
 */
491
492
493
void
gdk_free_text_list (gchar **list)
{
494
495
  g_return_if_fail (list != NULL);

496
497
498
  XFreeStringList (list);
}

499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
static gint
make_list (const gchar  *text,
	   gint          length,
	   gboolean      latin1,
	   gchar      ***list)
{
  GSList *strings = NULL;
  gint n_strings = 0;
  gint i;
  const gchar *p = text;
  const gchar *q;
  GSList *tmp_list;
  GError *error = NULL;

  while (p < text + length)
    {
      gchar *str;
      
      q = p;
      while (*q && q < text + length)
	q++;

      if (latin1)
	{
	  str = g_convert (p, q - p,
			   "UTF-8", "ISO-8859-1",
			   NULL, NULL, &error);

	  if (!str)
	    {
	      g_warning ("Error converting selection from STRING: %s",
			 error->message);
	      g_error_free (error);
	    }
	}
      else
535
536
537
538
539
540
541
542
543
	{
	  str = g_strndup (p, q - p);
	  if (!g_utf8_validate (str, -1, NULL))
	    {
	      g_warning ("Error converting selection from UTF8_STRING");
	      g_free (str);
	      str = NULL;
	    }
	}
544
545
546
547
548
549
550
551
552
553
554

      if (str)
	{
	  strings = g_slist_prepend (strings, str);
	  n_strings++;
	}

      p = q + 1;
    }

  if (list)
555
556
557
558
559
    {
      *list = g_new (gchar *, n_strings + 1);
      (*list)[n_strings] = NULL;
    }
     
560
561
562
563
564
565
566
567
  i = n_strings;
  tmp_list = strings;
  while (tmp_list)
    {
      if (list)
	(*list)[--i] = tmp_list->data;
      else
	g_free (tmp_list->data);
568
      
569
570
      tmp_list = tmp_list->next;
    }
571
  
572
573
574
575
576
577
  g_slist_free (strings);

  return n_strings;
}

/**
578
579
 * gdk_text_property_to_utf8_list_for_display:
 * @display:  a #GdkDisplay
580
581
582
583
584
585
586
 * @encoding: an atom representing the encoding of the text
 * @format:   the format of the property
 * @text:     the text to convert
 * @length:   the length of @text, in bytes
 * @list:     location to store the list of strings or %NULL. The
 *            list should be freed with g_strfreev().
 * 
Matthias Clasen's avatar
Matthias Clasen committed
587
 * Converts a text property in the given encoding to
588
589
590
591
 * a list of UTF-8 strings. 
 * 
 * Return value: the number of strings in the resulting
 *               list.
Matthias Clasen's avatar
Matthias Clasen committed
592
593
 *
 * Since: 2.2
594
595
 **/
gint 
596
597
598
599
600
601
gdk_text_property_to_utf8_list_for_display (GdkDisplay    *display,
					    GdkAtom        encoding,
					    gint           format,
					    const guchar  *text,
					    gint           length,
					    gchar       ***list)
602
603
604
{
  g_return_val_if_fail (text != NULL, 0);
  g_return_val_if_fail (length >= 0, 0);
605
  g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);
606
607
608
609
610
  
  if (encoding == GDK_TARGET_STRING)
    {
      return make_list ((gchar *)text, length, TRUE, list);
    }
Matthias Clasen's avatar
Matthias Clasen committed
611
  else if (encoding == gdk_atom_intern_static_string ("UTF8_STRING"))
612
613
614
615
616
617
618
619
    {
      return make_list ((gchar *)text, length, FALSE, list);
    }
  else
    {
      gchar **local_list;
      gint local_count;
      gint i;
620
      const gchar *charset = NULL;
621
      gboolean need_conversion = !g_get_charset (&charset);
622
623
624
625
626
      gint count = 0;
      GError *error = NULL;
      
      /* Probably COMPOUND text, we fall back to Xlib routines
       */
627
628
629
630
631
632
      local_count = gdk_text_property_to_text_list_for_display (display,
								encoding,
								format, 
								text,
								length,
								&local_list);
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
      if (list)
	*list = g_new (gchar *, local_count + 1);
      
      for (i=0; i<local_count; i++)
	{
	  /* list contains stuff in our default encoding
	   */
	  if (need_conversion)
	    {
	      gchar *utf = g_convert (local_list[i], -1,
				      "UTF-8", charset,
				      NULL, NULL, &error);
	      if (utf)
		{
		  if (list)
		    (*list)[count++] = utf;
		  else
		    g_free (utf);
		}
	      else
		{
		  g_warning ("Error converting to UTF-8 from '%s': %s",
			     charset, error->message);
		  g_error_free (error);
		  error = NULL;
		}
	    }
	  else
	    {
	      if (list)
663
664
665
666
667
668
		{
		  if (g_utf8_validate (local_list[i], -1, NULL))
		    (*list)[count++] = g_strdup (local_list[i]);
		  else
		    g_warning ("Error converting selection");
		}
669
670
	    }
	}
671
672
673

      if (local_count)
	gdk_free_text_list (local_list);
674
      
675
676
      if (list)
	(*list)[count] = NULL;
677
678
679
680
681

      return count;
    }
}

682
683
/**
 * gdk_string_to_compound_text_for_display:
Matthias Clasen's avatar
Matthias Clasen committed
684
685
 * @display:  the #GdkDisplay where the encoding is defined.
 * @str:      a nul-terminated string.
686
687
688
689
690
691
692
693
694
 * @encoding: location to store the encoding atom 
 *	      (to be used as the type for the property).
 * @format:   location to store the format of the property
 * @ctext:    location to store newly allocated data for the property.
 * @length:   the length of @text, in bytes
 * 
 * Convert a string from the encoding of the current 
 * locale into a form suitable for storing in a window property.
 * 
695
 * Returns: 0 upon success, non-zero upon failure. 
Matthias Clasen's avatar
Matthias Clasen committed
696
697
 *
 * Since: 2.2
698
 **/
699
gint
700
701
702
703
704
705
gdk_string_to_compound_text_for_display (GdkDisplay  *display,
					 const gchar *str,
					 GdkAtom     *encoding,
					 gint        *format,
					 guchar     **ctext,
					 gint        *length)
706
707
708
709
{
  gint res;
  XTextProperty property;

710
711
  g_return_val_if_fail (GDK_IS_DISPLAY (display), 0);

712
  if (gdk_display_is_closed (display))
713
714
715
716
717
    res = XLocaleNotSupported;
  else
    res = XmbTextListToTextProperty (GDK_DISPLAY_XDISPLAY (display), 
				     (char **)&str, 1, XCompoundTextStyle,
				     &property);
718
719
720
721
722
723
724
725
726
  if (res != Success)
    {
      property.encoding = None;
      property.format = None;
      property.value = NULL;
      property.nitems = 0;
    }

  if (encoding)
727
    *encoding = gdk_x11_xatom_to_atom_for_display (display, property.encoding);
728
729
730
731
732
733
734
735
736
737
  if (format)
    *format = property.format;
  if (ctext)
    *ctext = property.value;
  if (length)
    *length = property.nitems;

  return res;
}

738
739
740
741
742
/* The specifications for COMPOUND_TEXT and STRING specify that C0 and
 * C1 are not allowed except for \n and \t, however the X conversions
 * routines for COMPOUND_TEXT only enforce this in one direction,
 * causing cut-and-paste of \r and \r\n separated text to fail.
 * 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
747
sanitize_utf8 (const gchar *src,
	       gboolean return_latin1)
748
749
750
751
752
753
754
{
  gint len = strlen (src);
  GString *result = g_string_sized_new (len);
  const gchar *p = src;

  while (*p)
    {
755
      if (*p == '\r')
756
757
	{
	  p++;
758
	  if (*p == '\n')
759
760
761
762
763
764
765
766
	    p++;

	  g_string_append_c (result, '\n');
	}
      else
	{
	  gunichar ch = g_utf8_get_char (p);
	  
767
	  if (!((ch < 0x20 && ch != '\t' && ch != '\n') || (ch >= 0x7f && ch < 0xa0)))
768
	    {
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
	      if (return_latin1)
		{
		  if (ch <= 0xff)
		    g_string_append_c (result, ch);
		  else
		    g_string_append_printf (result,
					    ch < 0x10000 ? "\\u%04x" : "\\U%08x",
					    ch);
		}
	      else
		{
		  char buf[7];
		  gint buflen;
		  
		  buflen = g_unichar_to_utf8 (ch, buf);
		  g_string_append_len (result, buf, buflen);
		}
786
787
788
789
790
791
792
793
794
795
796
797
798
	    }

	  p = g_utf8_next_char (p);
	}
    }

  return g_string_free (result, FALSE);
}

/**
 * gdk_utf8_to_string_target:
 * @str: a UTF-8 string
 * 
Matthias Clasen's avatar
Matthias Clasen committed
799
 * Converts an UTF-8 string into the best possible representation
800
801
802
803
 * as a STRING. The representation of characters not in STRING
 * is not specified; it may be as pseudo-escape sequences
 * \x{ABCD}, or it may be in some other form of approximation.
 * 
Matthias Clasen's avatar
Matthias Clasen committed
804
 * Return value: the newly-allocated string, or %NULL if the
805
 *               conversion failed. (It should not fail for
806
807
 *               any properly formed UTF-8 string unless system
 *               limits like memory or file descriptors are exceeded.)
808
809
810
811
 **/
gchar *
gdk_utf8_to_string_target (const gchar *str)
{
812
  return sanitize_utf8 (str, TRUE);
813
814
815
}

/**
816
817
 * gdk_utf8_to_compound_text_for_display:
 * @display:  a #GdkDisplay
818
819
820
821
822
823
824
 * @str:      a UTF-8 string
 * @encoding: location to store resulting encoding
 * @format:   location to store format of the result
 * @ctext:    location to store the data of the result
 * @length:   location to store the length of the data
 *            stored in @ctext
 * 
Matthias Clasen's avatar
Matthias Clasen committed
825
 * Converts from UTF-8 to compound text. 
826
827
 * 
 * Return value: %TRUE if the conversion succeeded, otherwise
Matthias Clasen's avatar
Matthias Clasen committed
828
 *               %FALSE.
Matthias Clasen's avatar
Matthias Clasen committed
829
830
 *
 * Since: 2.2
831
832
 **/
gboolean
833
834
835
836
837
838
gdk_utf8_to_compound_text_for_display (GdkDisplay  *display,
				       const gchar *str,
				       GdkAtom     *encoding,
				       gint        *format,
				       guchar     **ctext,
				       gint        *length)
839
840
{
  gboolean need_conversion;
841
  const gchar *charset;
842
843
844
845
846
  gchar *locale_str, *tmp_str;
  GError *error = NULL;
  gboolean result;

  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
854

  if (need_conversion)
    {
855
856
857
      locale_str = g_convert (tmp_str, -1,
			      charset, "UTF-8",
			      NULL, NULL, &error);
858
859
860
861
      g_free (tmp_str);

      if (!locale_str)
	{
862
863
864
865
866
867
	  if (!(error->domain = G_CONVERT_ERROR &&
		error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
	    {
	      g_warning ("Error converting from UTF-8 to '%s': %s",
			 charset, error->message);
	    }
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
	  g_error_free (error);

	  if (encoding)
	    *encoding = None;
	  if (format)
	    *format = None;
	  if (ctext)
	    *ctext = NULL;
	  if (length)
	    *length = 0;

	  return FALSE;
	}
    }
  else
    locale_str = tmp_str;
    
885
886
887
  result = gdk_string_to_compound_text_for_display (display, locale_str,
						    encoding, format, 
						    ctext, length);
888
  result = (result == Success? TRUE : FALSE);
889
890
891
892
893
894
  
  g_free (locale_str);

  return result;
}

895
896
897
898
899
900
901
/**
 * gdk_free_compound_text:
 * @ctext: The pointer stored in @ctext from a call to
 *   gdk_string_to_compound_text().
 *
 * Frees the data returned from gdk_string_to_compound_text().
 */
902
903
904
905
906
void gdk_free_compound_text (guchar *ctext)
{
  if (ctext)
    XFree (ctext);
}