Source code: gnu/java/awt/peer/gtk/GtkClipboard.java
1 /* GtkClipboard.java
2 Copyright (C) 1999, 2005 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package gnu.java.awt.peer.gtk;
40
41 import java.awt.Image;
42 import java.awt.datatransfer.*;
43
44 import java.io.*;
45
46 import java.util.List;
47 import java.util.Iterator;
48
49 public class GtkClipboard extends Clipboard
50 {
51
52 // Given to the native side so it can signal special targets that
53 // can be converted to one of the special predefined DataFlavors.
54 static final String stringMimeType;
55 static final String imageMimeType;
56 static final String filesMimeType;
57
58 // Indicates whether the results of the clipboard selection can be
59 // cached by GtkSelection. True if
60 // gdk_display_supports_selection_notification.
61 static final boolean canCache;
62
63 static
64 {
65 stringMimeType = DataFlavor.stringFlavor.getMimeType();
66 imageMimeType = DataFlavor.imageFlavor.getMimeType();
67 filesMimeType = DataFlavor.javaFileListFlavor.getMimeType();
68
69 canCache = initNativeState(stringMimeType, imageMimeType, filesMimeType);
70 }
71
72 /**
73 * The one and only gtk+ clipboard instance.
74 */
75 private static GtkClipboard instance = new GtkClipboard();
76
77 /**
78 * Creates the clipboard and sets the initial contents to the
79 * current gtk+ selection.
80 */
81 private GtkClipboard()
82 {
83 super("System Clipboard");
84 setContents(new GtkSelection(), null);
85 }
86
87 /**
88 * Returns the one and only GtkClipboard instance.
89 */
90
91 static GtkClipboard getInstance()
92 {
93 return instance;
94 }
95
96 /**
97 * Sets the GtkSelection facade as new contents of the clipboard.
98 * Called from gtk+ when another application grabs the clipboard and
99 * we loose ownership.
100 */
101 private static void setSystemContents()
102 {
103 GtkClipboardNotifier.announce();
104 }
105
106 /**
107 * Sets the new contents and advertises the available flavors to the
108 * gtk+ clipboard.
109 */
110 public synchronized void setContents(Transferable contents,
111 ClipboardOwner owner)
112 {
113 super.setContents(contents, owner);
114
115 if (contents == null)
116 {
117 advertiseContent(null, false, false, false);
118 return;
119 }
120
121 // We don't need to do anything for a GtkSelection facade.
122 if (contents instanceof GtkSelection)
123 return;
124
125 boolean text = false;
126 boolean images = false;
127 boolean files = false;
128
129 if (contents instanceof StringSelection
130 || contents.isDataFlavorSupported(DataFlavor.stringFlavor)
131 || contents.isDataFlavorSupported(DataFlavor.plainTextFlavor)
132 || contents.isDataFlavorSupported(DataFlavor
133 .getTextPlainUnicodeFlavor()))
134 text = true;
135
136 DataFlavor[] flavors = contents.getTransferDataFlavors();
137 String[] mimeTargets = new String[flavors.length];
138 for (int i = 0; i < flavors.length; i++)
139 {
140 DataFlavor flavor = flavors[i];
141 String mimeType = flavor.getMimeType();
142 mimeTargets[i] = mimeType;
143
144 if (! text)
145 if ("text".equals(flavor.getPrimaryType())
146 || flavor.isRepresentationClassReader())
147 text = true;
148
149 // XXX - We only support automatic image conversion for
150 // GtkImages at the moment. So explicitly check that we have
151 // one.
152 if (! images && flavors[i].equals(DataFlavor.imageFlavor))
153 {
154 try
155 {
156 Object o = contents.getTransferData(DataFlavor.imageFlavor);
157 if (o instanceof GtkImage)
158 images = true;
159 }
160 catch (UnsupportedFlavorException ufe)
161 {
162 }
163 catch (IOException ioe)
164 {
165 }
166 catch (ClassCastException cce)
167 {
168 }
169 }
170
171 if (flavors[i].equals(DataFlavor.javaFileListFlavor))
172 files = true;
173 }
174
175 advertiseContent(mimeTargets, text, images, files);
176 }
177
178 /**
179 * Advertises new contents to the gtk+ clipboard given a string
180 * array of (mime-type) targets. When the boolean flags text, images
181 * and/or files are set then gtk+ is asked to also advertise the
182 * availability of any text, image or uri/file content types it
183 * supports. If targets is null (and all flags false) then the
184 * selection has explicitly been erased.
185 */
186 private native void advertiseContent(String[] targets,
187 boolean text,
188 boolean images,
189 boolean files);
190
191 /**
192 * Called by the gtk+ clipboard when an application has requested
193 * text. Return a string representing the current clipboard
194 * contents or null when no text can be provided.
195 */
196 private String provideText()
197 {
198 Transferable contents = this.contents;
199 if (contents == null || contents instanceof GtkSelection)
200 return null;
201
202 // Handle StringSelection special since that is just pure text.
203 if (contents instanceof StringSelection)
204 {
205 try
206 {
207 return (String) contents.getTransferData(DataFlavor.stringFlavor);
208 }
209 catch (UnsupportedFlavorException ufe)
210 {
211 }
212 catch (IOException ioe)
213 {
214 }
215 catch (ClassCastException cce)
216 {
217 }
218 }
219
220 // Try to get a plain text reader for the current contents and
221 // turn the result into a string.
222 try
223 {
224 DataFlavor plainText = DataFlavor.getTextPlainUnicodeFlavor();
225 Reader r = plainText.getReaderForText(contents);
226 if (r != null)
227 {
228 StringBuffer sb = new StringBuffer();
229 char[] cs = new char[1024];
230 int l = r.read(cs);
231 while (l != -1)
232 {
233 sb.append(cs, 0, l);
234 l = r.read(cs);
235 }
236 return sb.toString();
237 }
238 }
239 catch (IllegalArgumentException iae)
240 {
241 }
242 catch (UnsupportedEncodingException iee)
243 {
244 }
245 catch (UnsupportedFlavorException ufe)
246 {
247 }
248 catch (IOException ioe)
249 {
250 }
251
252 return null;
253 }
254
255 /**
256 * Called by the gtk+ clipboard when an application has requested an
257 * image. Returns a GtkImage representing the current clipboard
258 * contents or null when no image can be provided.
259 */
260 private GtkImage provideImage()
261 {
262 Transferable contents = this.contents;
263 if (contents == null || contents instanceof GtkSelection)
264 return null;
265
266 try
267 {
268 return (GtkImage) contents.getTransferData(DataFlavor.imageFlavor);
269 }
270 catch (UnsupportedFlavorException ufe)
271 {
272 }
273 catch (IOException ioe)
274 {
275 }
276 catch (ClassCastException cce)
277 {
278 }
279
280 return null;
281 }
282
283 /**
284 * Called by the gtk+ clipboard when an application has requested a
285 * uri-list. Return a string array containing the URIs representing
286 * the current clipboard contents or null when no URIs can be
287 * provided.
288 */
289 private String[] provideURIs()
290 {
291 Transferable contents = this.contents;
292 if (contents == null || contents instanceof GtkSelection)
293 return null;
294
295 try
296 {
297 List list = (List) contents.getTransferData
298 (DataFlavor.javaFileListFlavor);
299 String[] uris = new String[list.size()];
300 int u = 0;
301 Iterator it = list.iterator();
302 while (it.hasNext())
303 uris[u++] = ((File) it.next()).toURI().toString();
304 return uris;
305 }
306 catch (UnsupportedFlavorException ufe)
307 {
308 }
309 catch (IOException ioe)
310 {
311 }
312 catch (ClassCastException cce)
313 {
314 }
315
316 return null;
317 }
318
319 /**
320 * Called by gtk+ clipboard when an application requests the given
321 * target mime-type. Returns a byte array containing the requested
322 * data, or null when the contents cannot be provided in the
323 * requested target mime-type. Only called after any explicit text,
324 * image or file/uri requests have been handled earlier and failed.
325 */
326 private byte[] provideContent(String target)
327 {
328 // Sanity check. The callback could be triggered just after we
329 // changed the clipboard.
330 Transferable contents = this.contents;
331 if (contents == null || contents instanceof GtkSelection)
332 return null;
333
334 // XXX - We are being called from a gtk+ callback. Which means we
335 // should return as soon as possible and not call arbitrary code
336 // that could deadlock or go bonkers. But we don't really know
337 // what DataTransfer contents object we are dealing with. Same for
338 // the other provideXXX() methods.
339 try
340 {
341 DataFlavor flavor = new DataFlavor(target);
342 Object o = contents.getTransferData(flavor);
343
344 if (o instanceof byte[])
345 return (byte[]) o;
346
347 if (o instanceof InputStream)
348 {
349 InputStream is = (InputStream) o;
350 ByteArrayOutputStream baos = new ByteArrayOutputStream();
351 byte[] bs = new byte[1024];
352 int l = is.read(bs);
353 while (l != -1)
354 {
355 baos.write(bs, 0, l);
356 l = is.read(bs);
357 }
358 return baos.toByteArray();
359 }
360
361 if (o instanceof Serializable)
362 {
363 ByteArrayOutputStream baos = new ByteArrayOutputStream();
364 ObjectOutputStream oos = new ObjectOutputStream(baos);
365 oos.writeObject(o);
366 oos.close();
367 return baos.toByteArray();
368 }
369 }
370 catch (ClassNotFoundException cnfe)
371 {
372 }
373 catch (UnsupportedFlavorException ufe)
374 {
375 }
376 catch (IOException ioe)
377 {
378 }
379 catch (ClassCastException cce)
380 {
381 }
382
383 return null;
384 }
385
386 /**
387 * Initializes the gtk+ clipboard and caches any native side
388 * structures needed. Returns whether or not the contents of the
389 * Clipboard can be cached (gdk_display_supports_selection_notification).
390 */
391 private static native boolean initNativeState(String stringTarget,
392 String imageTarget,
393 String filesTarget);
394 }