| /* GtkClipboard.java |
| Copyright (C) 1999, 2005, 2006 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath 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 |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package gnu.java.awt.peer.gtk; |
| |
| import java.awt.Image; |
| import java.awt.datatransfer.*; |
| |
| import java.io.*; |
| |
| import java.util.List; |
| import java.util.Iterator; |
| |
| public class GtkClipboard extends Clipboard |
| { |
| /** |
| * The one and only gtk+ clipboard instance for the CLIPBOARD selection. |
| */ |
| final static GtkClipboard clipboard = new GtkClipboard("System Clipboard"); |
| |
| /** |
| * The one and only gtk+ clipboard instance for the PRIMARY selection. |
| */ |
| final static GtkClipboard selection = new GtkClipboard("System Selection"); |
| |
| // Given to the native side so it can signal special targets that |
| // can be converted to one of the special predefined DataFlavors. |
| static final String stringMimeType |
| = DataFlavor.stringFlavor.getMimeType(); |
| static final String imageMimeType |
| = DataFlavor.imageFlavor.getMimeType(); |
| static final String filesMimeType |
| = DataFlavor.javaFileListFlavor.getMimeType(); |
| |
| // Indicates whether the results of the clipboard selection can be |
| // cached by GtkSelection. True if |
| // gdk_display_supports_selection_notification. |
| static final boolean canCache = initNativeState(clipboard, selection, |
| stringMimeType, |
| imageMimeType, |
| filesMimeType); |
| |
| /** |
| * Creates the clipboard and sets the initial contents to the |
| * current gtk+ selection. |
| */ |
| private GtkClipboard(String name) |
| { |
| super(name); |
| setContents(new GtkSelection(this), null); |
| } |
| |
| /** |
| * Returns the one and only GtkClipboard instance for the CLIPBOARD |
| * selection. |
| */ |
| static GtkClipboard getClipboardInstance() |
| { |
| return clipboard; |
| } |
| |
| /** |
| * Returns the one and only GtkClipboard instance for the PRIMARY |
| * selection. |
| */ |
| static GtkClipboard getSelectionInstance() |
| { |
| return selection; |
| } |
| |
| /** |
| * Sets the GtkSelection facade as new contents of the clipboard. |
| * Called from gtk+ when another application grabs the clipboard and |
| * we loose ownership. |
| * |
| * @param cleared If true this is a clear event (someone takes the |
| * clipboard from us) otherwise it is an owner changed event. |
| */ |
| private synchronized void setSystemContents(boolean cleared) |
| { |
| // We need to notify clipboard owner listeners when we were the |
| // owner (the selection was explictly set) and someone takes the |
| // clipboard away from us and asks us the clear any held storage, |
| // or if we weren't the owner of the clipboard to begin with, but |
| // the clipboard contents changed. We could refine this and check |
| // whether the actual available formats did in fact change, but we |
| // assume listeners will check for that anyway (and if possible we |
| // ask to cache the available formats so even if multiple |
| // listeners check after a notification the overhead should be |
| // minimal). |
| boolean owner = ! (contents instanceof GtkSelection); |
| boolean needNotification = (cleared && owner) || (! cleared && ! owner); |
| if (needNotification) |
| GtkClipboardNotifier.announce(this); |
| } |
| |
| /** |
| * Sets the new contents and advertises the available flavors to the |
| * gtk+ clipboard. |
| */ |
| public synchronized void setContents(Transferable contents, |
| ClipboardOwner owner) |
| { |
| super.setContents(contents, owner); |
| |
| if (contents == null) |
| { |
| advertiseContent(null, false, false, false); |
| return; |
| } |
| |
| // We don't need to do anything for a GtkSelection facade. |
| if (contents instanceof GtkSelection) |
| return; |
| |
| boolean text = false; |
| boolean images = false; |
| boolean files = false; |
| |
| if (contents instanceof StringSelection |
| || contents.isDataFlavorSupported(DataFlavor.stringFlavor) |
| || contents.isDataFlavorSupported(DataFlavor.plainTextFlavor) |
| || contents.isDataFlavorSupported(DataFlavor |
| .getTextPlainUnicodeFlavor())) |
| text = true; |
| |
| DataFlavor[] flavors = contents.getTransferDataFlavors(); |
| String[] mimeTargets = new String[flavors.length]; |
| for (int i = 0; i < flavors.length; i++) |
| { |
| DataFlavor flavor = flavors[i]; |
| String mimeType = flavor.getMimeType(); |
| mimeTargets[i] = mimeType; |
| |
| if (! text) |
| if ("text".equals(flavor.getPrimaryType()) |
| || flavor.isRepresentationClassReader()) |
| text = true; |
| |
| if (! images && flavors[i].equals(DataFlavor.imageFlavor)) |
| { |
| try |
| { |
| Object o = contents.getTransferData(DataFlavor.imageFlavor); |
| if (o instanceof Image) |
| images = true; |
| } |
| catch (UnsupportedFlavorException ufe) |
| { |
| } |
| catch (IOException ioe) |
| { |
| } |
| catch (ClassCastException cce) |
| { |
| } |
| } |
| |
| if (flavors[i].equals(DataFlavor.javaFileListFlavor)) |
| files = true; |
| } |
| |
| advertiseContent(mimeTargets, text, images, files); |
| } |
| |
| /** |
| * Advertises new contents to the gtk+ clipboard given a string |
| * array of (mime-type) targets. When the boolean flags text, images |
| * and/or files are set then gtk+ is asked to also advertise the |
| * availability of any text, image or uri/file content types it |
| * supports. If targets is null (and all flags false) then the |
| * selection has explicitly been erased. |
| */ |
| private native void advertiseContent(String[] targets, |
| boolean text, |
| boolean images, |
| boolean files); |
| |
| /** |
| * Called by the gtk+ clipboard when an application has requested |
| * text. Return a string representing the current clipboard |
| * contents or null when no text can be provided. |
| */ |
| private String provideText() |
| { |
| Transferable contents = this.contents; |
| if (contents == null || contents instanceof GtkSelection) |
| return null; |
| |
| // Handle StringSelection special since that is just pure text. |
| if (contents instanceof StringSelection) |
| { |
| try |
| { |
| return (String) contents.getTransferData(DataFlavor.stringFlavor); |
| } |
| catch (UnsupportedFlavorException ufe) |
| { |
| } |
| catch (IOException ioe) |
| { |
| } |
| catch (ClassCastException cce) |
| { |
| } |
| } |
| |
| // Try to get a plain text reader for the current contents and |
| // turn the result into a string. |
| try |
| { |
| DataFlavor plainText = DataFlavor.getTextPlainUnicodeFlavor(); |
| Reader r = plainText.getReaderForText(contents); |
| if (r != null) |
| { |
| StringBuffer sb = new StringBuffer(); |
| char[] cs = new char[1024]; |
| int l = r.read(cs); |
| while (l != -1) |
| { |
| sb.append(cs, 0, l); |
| l = r.read(cs); |
| } |
| return sb.toString(); |
| } |
| } |
| catch (IllegalArgumentException iae) |
| { |
| } |
| catch (UnsupportedEncodingException iee) |
| { |
| } |
| catch (UnsupportedFlavorException ufe) |
| { |
| } |
| catch (IOException ioe) |
| { |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Called by the gtk+ clipboard when an application has requested an |
| * image. Returns a GtkImage representing the current clipboard |
| * contents or null when no image can be provided. |
| */ |
| private GtkImage provideImage() |
| { |
| Transferable contents = this.contents; |
| if (contents == null || contents instanceof GtkSelection) |
| return null; |
| |
| try |
| { |
| Object o = contents.getTransferData(DataFlavor.imageFlavor); |
| if( o instanceof GtkImage ) |
| return (GtkImage) o; |
| else |
| return new GtkImage(((Image)o).getSource()); |
| } |
| catch (UnsupportedFlavorException ufe) |
| { |
| } |
| catch (IOException ioe) |
| { |
| } |
| catch (ClassCastException cce) |
| { |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Called by the gtk+ clipboard when an application has requested a |
| * uri-list. Return a string array containing the URIs representing |
| * the current clipboard contents or null when no URIs can be |
| * provided. |
| */ |
| private String[] provideURIs() |
| { |
| Transferable contents = this.contents; |
| if (contents == null || contents instanceof GtkSelection) |
| return null; |
| |
| try |
| { |
| List list = (List) contents.getTransferData |
| (DataFlavor.javaFileListFlavor); |
| String[] uris = new String[list.size()]; |
| int u = 0; |
| Iterator it = list.iterator(); |
| while (it.hasNext()) |
| uris[u++] = ((File) it.next()).toURI().toString(); |
| return uris; |
| } |
| catch (UnsupportedFlavorException ufe) |
| { |
| } |
| catch (IOException ioe) |
| { |
| } |
| catch (ClassCastException cce) |
| { |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Called by gtk+ clipboard when an application requests the given |
| * target mime-type. Returns a byte array containing the requested |
| * data, or null when the contents cannot be provided in the |
| * requested target mime-type. Only called after any explicit text, |
| * image or file/uri requests have been handled earlier and failed. |
| */ |
| private byte[] provideContent(String target) |
| { |
| // Sanity check. The callback could be triggered just after we |
| // changed the clipboard. |
| Transferable contents = this.contents; |
| if (contents == null || contents instanceof GtkSelection) |
| return null; |
| |
| // XXX - We are being called from a gtk+ callback. Which means we |
| // should return as soon as possible and not call arbitrary code |
| // that could deadlock or go bonkers. But we don't really know |
| // what DataTransfer contents object we are dealing with. Same for |
| // the other provideXXX() methods. |
| try |
| { |
| DataFlavor flavor = new DataFlavor(target); |
| Object o = contents.getTransferData(flavor); |
| |
| if (o instanceof byte[]) |
| return (byte[]) o; |
| |
| if (o instanceof InputStream) |
| { |
| InputStream is = (InputStream) o; |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| byte[] bs = new byte[1024]; |
| int l = is.read(bs); |
| while (l != -1) |
| { |
| baos.write(bs, 0, l); |
| l = is.read(bs); |
| } |
| return baos.toByteArray(); |
| } |
| |
| if (o instanceof Serializable) |
| { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ObjectOutputStream oos = new ObjectOutputStream(baos); |
| oos.writeObject(o); |
| oos.close(); |
| return baos.toByteArray(); |
| } |
| } |
| catch (ClassNotFoundException cnfe) |
| { |
| } |
| catch (UnsupportedFlavorException ufe) |
| { |
| } |
| catch (IOException ioe) |
| { |
| } |
| catch (ClassCastException cce) |
| { |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Initializes the gtk+ clipboards and caches any native side |
| * structures needed. Returns whether or not the contents of the |
| * Clipboard can be cached (gdk_display_supports_selection_notification). |
| */ |
| private static native boolean initNativeState(GtkClipboard clipboard, |
| GtkClipboard selection, |
| String stringTarget, |
| String imageTarget, |
| String filesTarget); |
| } |