1 /* GtkClipboard.java - Class representing gtk+ clipboard selection.
2 Copyright (C) 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
39 package gnu
.java
.awt
.peer
.gtk
;
41 import gnu
.classpath
.Pointer
;
43 import java
.awt
.datatransfer
.*;
49 import java
.awt
.Image
;
52 * Class representing the gtk+ clipboard selection. This is used when
53 * another program owns the clipboard. Whenever the system clipboard
54 * selection changes we create a new instance to notify the program
55 * that the available flavors might have changed. When requested it
56 * (lazily) caches the targets, and (text, image, or files/uris)
59 * XXX - should only cache when
60 * gdk_display_supports_selection_notification is true.
62 public class GtkSelection
implements Transferable
65 * Static lock used for requests of mimetypes and contents retrieval.
67 static private Object requestLock
= new Object();
70 * Whether a request for mimetypes, text, images, uris or byte[] is
71 * currently in progress. Should only be tested or set with
72 * requestLock held. When true no other requests should be made till
75 private boolean requestInProgress
;
78 * Indicates a requestMimeTypes() call was made and the
79 * corresponding mimeTypesAvailable() callback was triggered.
81 private boolean mimeTypesDelivered
;
84 * Set and returned by getTransferDataFlavors. Only valid when
85 * mimeTypesDelivered is true.
87 private DataFlavor
[] dataFlavors
;
90 * Indicates a requestText() call was made and the corresponding
91 * textAvailable() callback was triggered.
93 private boolean textDelivered
;
96 * Set as response to a requestText() call and possibly returned by
97 * getTransferData() for text targets. Only valid when textDelivered
103 * Indicates a requestImage() call was made and the corresponding
104 * imageAvailable() callback was triggered.
106 private boolean imageDelivered
;
109 * Set as response to a requestImage() call and possibly returned by
110 * getTransferData() for image targets. Only valid when
111 * imageDelivered is true and image is null.
113 private Pointer imagePointer
;
116 * Cached image value. Only valid when imageDelivered is
117 * true. Created from imagePointer.
122 * Indicates a requestUris() call was made and the corresponding
123 * urisAvailable() callback was triggered.
125 private boolean urisDelivered
;
128 * Set as response to a requestURIs() call. Only valid when
129 * urisDelivered is true
134 * Indicates a requestBytes(String) call was made and the
135 * corresponding bytesAvailable() callback was triggered.
137 private boolean bytesDelivered
;
140 * Set as response to a requestBytes(String) call. Only valid when
141 * bytesDelivered is true.
143 private byte[] bytes
;
146 * Should only be created by the GtkClipboard class.
153 * Gets an array of mime-type strings from the gtk+ clipboard and
154 * transforms them into an array of DataFlavors.
156 public DataFlavor
[] getTransferDataFlavors()
159 synchronized (requestLock
)
161 // Did we request already and cache the result?
162 if (mimeTypesDelivered
)
163 result
= (DataFlavor
[]) dataFlavors
.clone();
166 // Wait till there are no pending requests.
167 while (requestInProgress
)
173 catch (InterruptedException ie
)
179 // If nobody else beat us and cached the result we try
180 // ourselves to get it.
181 if (! mimeTypesDelivered
)
183 requestInProgress
= true;
185 while (! mimeTypesDelivered
)
191 catch (InterruptedException ie
)
196 requestInProgress
= false;
198 result
= dataFlavors
;
199 if (! GtkClipboard
.canCache
)
202 mimeTypesDelivered
= false;
204 requestLock
.notifyAll();
211 * Callback that sets the available DataFlavors[]. Note that this
212 * should not call any code that could need the main gdk lock.
214 private void mimeTypesAvailable(String
[] mimeTypes
)
216 synchronized (requestLock
)
218 if (mimeTypes
== null)
219 dataFlavors
= new DataFlavor
[0];
222 // Most likely the mimeTypes include text in which case we add an
224 ArrayList flavorsList
= new ArrayList(mimeTypes
.length
+ 1);
225 for (int i
= 0; i
< mimeTypes
.length
; i
++)
229 if (mimeTypes
[i
] == GtkClipboard
.stringMimeType
)
231 // XXX - Fix DataFlavor.getTextPlainUnicodeFlavor()
232 // and also add it to the list.
233 flavorsList
.add(DataFlavor
.stringFlavor
);
234 flavorsList
.add(DataFlavor
.plainTextFlavor
);
236 else if (mimeTypes
[i
] == GtkClipboard
.imageMimeType
)
237 flavorsList
.add(DataFlavor
.imageFlavor
);
238 else if (mimeTypes
[i
] == GtkClipboard
.filesMimeType
)
239 flavorsList
.add(DataFlavor
.javaFileListFlavor
);
242 // We check the target to prevent duplicates
243 // of the "magic" targets above.
244 DataFlavor target
= new DataFlavor(mimeTypes
[i
]);
245 if (! flavorsList
.contains(target
))
246 flavorsList
.add(target
);
249 catch (ClassNotFoundException cnfe
)
251 cnfe
.printStackTrace();
253 catch (NullPointerException npe
)
255 npe
.printStackTrace();
259 dataFlavors
= new DataFlavor
[flavorsList
.size()];
260 flavorsList
.toArray(dataFlavors
);
263 mimeTypesDelivered
= true;
264 requestLock
.notifyAll();
269 * Gets the available data flavors for this selection and checks
270 * that at least one of them is equal to the given DataFlavor.
272 public boolean isDataFlavorSupported(DataFlavor flavor
)
274 DataFlavor
[] dfs
= getTransferDataFlavors();
275 for (int i
= 0; i
< dfs
.length
; i
++)
276 if (flavor
.equals(dfs
[i
]))
283 * Helper method that tests whether we already have the text for the
284 * current gtk+ selection on the clipboard and if not requests it
285 * and waits till it is available.
287 private String
getText()
290 synchronized (requestLock
)
292 // Did we request already and cache the result?
297 // Wait till there are no pending requests.
298 while (requestInProgress
)
304 catch (InterruptedException ie
)
310 // If nobody else beat us we try ourselves to get and
311 // caching the result.
314 requestInProgress
= true;
316 while (! textDelivered
)
322 catch (InterruptedException ie
)
327 requestInProgress
= false;
330 if (! GtkClipboard
.canCache
)
333 textDelivered
= false;
335 requestLock
.notifyAll();
342 * Callback that sets the available text on the clipboard. Note that
343 * this should not call any code that could need the main gdk lock.
345 private void textAvailable(String text
)
347 synchronized (requestLock
)
350 textDelivered
= true;
351 requestLock
.notifyAll();
356 * Helper method that tests whether we already have an image for the
357 * current gtk+ selection on the clipboard and if not requests it
358 * and waits till it is available.
360 private Image
getImage()
363 synchronized (requestLock
)
365 // Did we request already and cache the result?
370 // Wait till there are no pending requests.
371 while (requestInProgress
)
377 catch (InterruptedException ie
)
383 // If nobody else beat us we try ourselves to get and
384 // caching the result.
385 if (! imageDelivered
)
387 requestInProgress
= true;
389 while (! imageDelivered
)
395 catch (InterruptedException ie
)
400 requestInProgress
= false;
402 if (imagePointer
!= null)
403 image
= new GtkImage(imagePointer
);
406 if (! GtkClipboard
.canCache
)
409 imageDelivered
= false;
411 requestLock
.notifyAll();
418 * Callback that sets the available image on the clipboard. Note
419 * that this should not call any code that could need the main gdk
420 * lock. Note that we get a Pointer to a GdkPixbuf which we cannot
421 * turn into a real GtkImage at this point. That will be done on the
422 * "user thread" in getImage().
424 private void imageAvailable(Pointer pointer
)
426 synchronized (requestLock
)
428 this.imagePointer
= pointer
;
429 imageDelivered
= true;
430 requestLock
.notifyAll();
435 * Helper method that test whether we already have a list of
436 * URIs/Files and if not requests them and waits till they are
439 private List
getURIs()
442 synchronized (requestLock
)
444 // Did we request already and cache the result?
449 // Wait till there are no pending requests.
450 while (requestInProgress
)
456 catch (InterruptedException ie
)
462 // If nobody else beat us we try ourselves to get and
463 // caching the result.
466 requestInProgress
= true;
468 while (! urisDelivered
)
474 catch (InterruptedException ie
)
479 requestInProgress
= false;
482 if (! GtkClipboard
.canCache
)
485 urisDelivered
= false;
487 requestLock
.notifyAll();
494 * Callback that sets the available File list. Note that this should
495 * not call any code that could need the main gdk lock.
497 private void urisAvailable(String
[] uris
)
499 synchronized (requestLock
)
501 if (uris
!= null && uris
.length
!= 0)
503 ArrayList list
= new ArrayList(uris
.length
);
504 for (int i
= 0; i
< uris
.length
; i
++)
508 URI uri
= new URI(uris
[i
]);
509 if (uri
.getScheme().equals("file"))
510 list
.add(new File(uri
));
512 catch (URISyntaxException use
)
519 urisDelivered
= true;
520 requestLock
.notifyAll();
525 * Helper method that requests a byte[] for the given target
526 * mime-type flavor and waits till it is available. Note that unlike
527 * the other get methods this one doesn't cache the result since
528 * there are possibly many targets.
530 private byte[] getBytes(String target
)
533 synchronized (requestLock
)
535 // Wait till there are no pending requests.
536 while (requestInProgress
)
542 catch (InterruptedException ie
)
548 // Request bytes and wait till they are available.
549 requestInProgress
= true;
550 requestBytes(target
);
551 while (! bytesDelivered
)
557 catch (InterruptedException ie
)
564 bytesDelivered
= false;
565 requestInProgress
= false;
567 requestLock
.notifyAll();
573 * Callback that sets the available byte array on the
574 * clipboard. Note that this should not call any code that could
575 * need the main gdk lock.
577 private void bytesAvailable(byte[] bytes
)
579 synchronized (requestLock
)
582 bytesDelivered
= true;
583 requestLock
.notifyAll();
587 public Object
getTransferData(DataFlavor flavor
)
588 throws UnsupportedFlavorException
590 // Note the fall throughs for the "magic targets" if they fail we
591 // try one more time through getBytes().
592 if (flavor
.equals(DataFlavor
.stringFlavor
))
594 String text
= getText();
599 if (flavor
.equals(DataFlavor
.plainTextFlavor
))
601 String text
= getText();
603 return new StringBufferInputStream(text
);
606 if (flavor
.equals(DataFlavor
.imageFlavor
))
608 Image image
= getImage();
613 if (flavor
.equals(DataFlavor
.javaFileListFlavor
))
615 List uris
= getURIs();
620 byte[] bytes
= getBytes(flavor
.getMimeType());
622 throw new UnsupportedFlavorException(flavor
);
624 if (flavor
.isMimeTypeSerializedObject())
628 ByteArrayInputStream bais
= new ByteArrayInputStream(bytes
);
629 ObjectInputStream ois
= new ObjectInputStream(bais
);
630 return ois
.readObject();
632 catch (IOException ioe
)
634 ioe
.printStackTrace();
636 catch (ClassNotFoundException cnfe
)
638 cnfe
.printStackTrace();
642 if (flavor
.isRepresentationClassInputStream())
643 return new ByteArrayInputStream(bytes
);
645 // XXX, need some more conversions?
647 throw new UnsupportedFlavorException(flavor
);
651 * Requests text, Image or an byte[] for a particular target from the
652 * other application. These methods return immediately. When the
653 * content is available the contentLock will be notified through
654 * textAvailable, imageAvailable, urisAvailable or bytesAvailable and the
655 * appropriate field is set.
657 private native void requestText();
658 private native void requestImage();
659 private native void requestURIs();
660 private native void requestBytes(String target
);
662 /* Similar to the above but for requesting the supported targets. */
663 private native void requestMimeTypes();