source: trunk/third/gtk/gdk/gdkselection.c @ 17071

Revision 17071, 8.4 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17070, which included commits to RCS files with non-trunk default branches.
Line 
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 Library 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 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library 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-1999.  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#include "gdk.h"
31#include "gdkprivate.h"
32#include "gdkx.h"
33
34
35gboolean
36gdk_selection_owner_set (GdkWindow *owner,
37                         GdkAtom    selection,
38                         guint32    time,
39                         gint       send_event)
40{
41  Display *xdisplay;
42  Window xwindow;
43
44  if (owner)
45    {
46      GdkWindowPrivate *private;
47
48      private = (GdkWindowPrivate*) owner;
49      if (private->destroyed)
50        return FALSE;
51
52      xdisplay = private->xdisplay;
53      xwindow = private->xwindow;
54    }
55  else
56    {
57      xdisplay = gdk_display;
58      xwindow = None;
59    }
60
61  XSetSelectionOwner (xdisplay, selection, xwindow, time);
62
63  return (XGetSelectionOwner (xdisplay, selection) == xwindow);
64}
65
66GdkWindow*
67gdk_selection_owner_get (GdkAtom selection)
68{
69  Window xwindow;
70
71  xwindow = XGetSelectionOwner (gdk_display, selection);
72  if (xwindow == None)
73    return NULL;
74
75  return gdk_window_lookup (xwindow);
76}
77
78void
79gdk_selection_convert (GdkWindow *requestor,
80                       GdkAtom    selection,
81                       GdkAtom    target,
82                       guint32    time)
83{
84  GdkWindowPrivate *private;
85
86  g_return_if_fail (requestor != NULL);
87
88  private = (GdkWindowPrivate*) requestor;
89  if (private->destroyed)
90    return;
91
92  XConvertSelection (private->xdisplay, selection, target,
93                     gdk_selection_property, private->xwindow, time);
94}
95
96gint
97gdk_selection_property_get (GdkWindow  *requestor,
98                            guchar    **data,
99                            GdkAtom    *ret_type,
100                            gint       *ret_format)
101{
102  GdkWindowPrivate *private;
103  gulong nitems;
104  gulong nbytes;
105  gulong length;
106  GdkAtom prop_type;
107  gint prop_format;
108  guchar *t = NULL;
109
110  g_return_val_if_fail (requestor != NULL, 0);
111
112  /* If retrieved chunks are typically small, (and the ICCCM says the
113     should be) it would be a win to try first with a buffer of
114     moderate length, to avoid two round trips to the server */
115
116  private = (GdkWindowPrivate*) requestor;
117  if (private->destroyed)
118    return 0;
119
120  t = NULL;
121  XGetWindowProperty (private->xdisplay, private->xwindow,
122                      gdk_selection_property, 0, 0, False,
123                      AnyPropertyType, &prop_type, &prop_format,
124                      &nitems, &nbytes, &t);
125
126  if (ret_type)
127    *ret_type = prop_type;
128  if (ret_format)
129    *ret_format = prop_format;
130
131  if (prop_type == None)
132    {
133      *data = NULL;
134      return 0;
135    }
136 
137  if (t)
138    {
139      XFree (t);
140      t = NULL;
141    }
142
143  /* Add on an extra byte to handle null termination.  X guarantees
144     that t will be 1 longer than nbytes and null terminated */
145  length = nbytes + 1;
146
147  /* We can't delete the selection here, because it might be the INCR
148     protocol, in which case the client has to make sure they'll be
149     notified of PropertyChange events _before_ the property is deleted.
150     Otherwise there's no guarantee we'll win the race ... */
151  XGetWindowProperty (private->xdisplay, private->xwindow,
152                      gdk_selection_property, 0, (nbytes + 3) / 4, False,
153                      AnyPropertyType, &prop_type, &prop_format,
154                      &nitems, &nbytes, &t);
155
156  if (prop_type != None)
157    {
158      *data = g_new (guchar, length);
159      memcpy (*data, t, length);
160      if (t)
161        XFree (t);
162      return length-1;
163    }
164  else
165    {
166      *data = NULL;
167      return 0;
168    }
169}
170
171
172void
173gdk_selection_send_notify (guint32  requestor,
174                           GdkAtom  selection,
175                           GdkAtom  target,
176                           GdkAtom  property,
177                           guint32  time)
178{
179  XSelectionEvent xevent;
180
181  xevent.type = SelectionNotify;
182  xevent.serial = 0;
183  xevent.send_event = True;
184  xevent.display = gdk_display;
185  xevent.requestor = requestor;
186  xevent.selection = selection;
187  xevent.target = target;
188  xevent.property = property;
189  xevent.time = time;
190
191  gdk_send_xevent (requestor, False, NoEventMask, (XEvent*) &xevent);
192}
193
194
195/* The output of XmbTextPropertyToTextList may include stuff not valid
196 * for COMPOUND_TEXT. This routine tries to correct this by:
197 *
198 * a) Canonicalizing CR LF and CR to LF
199 * b) Stripping out all other non-allowed control characters
200 *
201 * See the COMPOUND_TEXT spec distributed with X for explanations
202 * what is allowed.
203 */
204static gchar *
205sanitize_ctext (const char *str,
206                gint       *length)
207{
208  gchar *result = g_malloc (*length + 1);
209  gint out_length = 0;
210  gint i;
211  const guchar *ustr = (const guchar *)str;
212
213  for (i=0; i < *length; i++)
214    {
215      guchar c = ustr[i];
216     
217      if (c == '\r')
218        {
219          result[out_length++] = '\n';
220          if (i + 1 < *length && ustr[i + 1] == '\n')
221            i++;
222        }
223      else if (c == 27 /* ESC */)
224        {
225          /* Check for "extended segments, which can contain arbitrary
226           * octets. See CTEXT spec, section 6.
227           */
228
229          if (i + 5 < *length &&
230              ustr[i + 1] == '%' &&
231              ustr[i + 2] == '/' &&
232              (ustr[i + 3] >= 48 && ustr[i + 3] <= 52) &&
233              ustr[i + 4] >= 128 &&
234              ustr[i + 5] >= 128)
235            {
236              int extra_len = 6 + (ustr[i + 4] - 128) * 128 + ustr[i + 5] - 128;
237              extra_len = MAX (extra_len, *length - i);
238
239              memcpy (result + out_length, ustr + i, extra_len);
240              out_length += extra_len;
241              i += extra_len - 1;
242            }
243          else
244            result[out_length++] = c;       
245        }
246      else if (c == '\n' || c == '\t' || c == 27 /* ESC */ ||
247               (c >= 32 && c <= 127) || /* GL */
248               c == 155 /* CONTROL SEQUENCE INTRODUCER */ ||   
249               (c >= 160 && c <= 255)) /* GR */
250        {
251          result[out_length++] = c;
252        }
253    }
254
255  result[out_length] = '\0';
256  *length = out_length;
257 
258  return result;
259}
260
261gint
262gdk_text_property_to_text_list (GdkAtom encoding, gint format,
263                                guchar *text, gint length,
264                                gchar ***list)
265{
266  XTextProperty property;
267  gint count = 0;
268  gint res;
269  gchar *sanitized_text = NULL;
270
271  if (!list)
272    return 0;
273
274  property.encoding = encoding;
275  property.format = format;
276
277  if (encoding == gdk_atom_intern ("COMPOUND_TEXT", FALSE) && format == 8)
278    {
279      gint sanitized_text_length = length;
280     
281      property.value = sanitized_text = sanitize_ctext (text, &sanitized_text_length);
282      property.nitems = sanitized_text_length;
283    }
284  else
285    {
286      property.value = text;
287      property.nitems = length;
288    }
289 
290  res = XmbTextPropertyToTextList (GDK_DISPLAY(), &property, list, &count);
291
292  if (sanitized_text)
293    g_free (sanitized_text);
294
295  if (res == XNoMemory || res == XLocaleNotSupported ||
296      res == XConverterNotFound)
297    return 0;
298  else
299    return count;
300}
301
302void
303gdk_free_text_list (gchar **list)
304{
305  g_return_if_fail (list != NULL);
306
307  XFreeStringList (list);
308}
309
310gint
311gdk_string_to_compound_text (const gchar *str,
312                             GdkAtom *encoding, gint *format,
313                             guchar **ctext, gint *length)
314{
315  gint res;
316  XTextProperty property;
317  gint sanitized_text_length;
318  gchar *sanitized_text;
319
320  res = XmbTextListToTextProperty (GDK_DISPLAY(),
321                                   (char **)&str, 1, XCompoundTextStyle,
322                                   &property);
323  if (res != Success)
324    {
325      property.encoding = None;
326      property.format = None;
327      property.value = NULL;
328      property.nitems = 0;
329    }
330
331  g_assert (property.encoding == gdk_atom_intern ("COMPOUND_TEXT", FALSE) && property.format == 8);
332
333  if (encoding)
334    *encoding = property.encoding;
335  if (format)
336    *format = property.format;
337
338  sanitized_text_length = property.nitems;
339  sanitized_text = sanitize_ctext (property.value, &sanitized_text_length);
340
341  if (ctext)
342    *ctext = sanitized_text;
343  else
344    g_free (sanitized_text);
345 
346  if (length)
347    *length = sanitized_text_length;
348
349  if (property.value)
350    XFree (property.value);
351
352  return res;
353}
354
355void gdk_free_compound_text (guchar *ctext)
356{
357  if (ctext)
358    g_free (ctext);
359}
Note: See TracBrowser for help on using the repository browser.