summaryrefslogtreecommitdiff
path: root/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java')
-rw-r--r--libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java390
1 files changed, 307 insertions, 83 deletions
diff --git a/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java b/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java
index c719cddb575..a502e1fd6ef 100644
--- a/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java
+++ b/libjava/classpath/gnu/java/awt/peer/gtk/GtkClipboard.java
@@ -38,133 +38,357 @@ exception statement from your version. */
package gnu.java.awt.peer.gtk;
-import java.awt.datatransfer.Clipboard;
-import java.awt.datatransfer.ClipboardOwner;
-import java.awt.datatransfer.DataFlavor;
-import java.awt.datatransfer.StringSelection;
-import java.awt.datatransfer.Transferable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
+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 number of milliseconds that we'll wait around for the
- owner of the GDK_SELECTION_PRIMARY selection to convert
- the requested data */
- static final int SELECTION_RECEIVED_TIMEOUT = 5000;
- /* We currently only support transferring of text between applications */
- static String selection;
- static Object selectionLock = new Object ();
+ // 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;
+ static final String imageMimeType;
+ static final String filesMimeType;
+
+ // Indicates whether the results of the clipboard selection can be
+ // cached by GtkSelection. True if
+ // gdk_display_supports_selection_notification.
+ static final boolean canCache;
+
+ static
+ {
+ stringMimeType = DataFlavor.stringFlavor.getMimeType();
+ imageMimeType = DataFlavor.imageFlavor.getMimeType();
+ filesMimeType = DataFlavor.javaFileListFlavor.getMimeType();
+
+ canCache = initNativeState(stringMimeType, imageMimeType, filesMimeType);
+ }
- static boolean hasSelection = false;
+ /**
+ * The one and only gtk+ clipboard instance.
+ */
+ private static GtkClipboard instance = new GtkClipboard();
- protected GtkClipboard()
+ /**
+ * Creates the clipboard and sets the initial contents to the
+ * current gtk+ selection.
+ */
+ private GtkClipboard()
{
super("System Clipboard");
- initNativeState();
+ setContents(new GtkSelection(), null);
+ }
+
+ /**
+ * Returns the one and only GtkClipboard instance.
+ */
+
+ static GtkClipboard getInstance()
+ {
+ return instance;
}
- public Transferable getContents(Object requestor)
+ /**
+ * Sets the GtkSelection facade as new contents of the clipboard.
+ * Called from gtk+ when another application grabs the clipboard and
+ * we loose ownership.
+ */
+ private static void setSystemContents()
{
- synchronized (this)
+ GtkClipboardNotifier.announce();
+ }
+
+ /**
+ * 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)
{
- if (hasSelection)
- return contents;
+ advertiseContent(null, false, false, false);
+ return;
}
- /* Java doesn't own the selection, so we need to ask X11 */
- // XXX: Does this hold with Swing too ?
- synchronized (selectionLock)
+ // 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++)
{
- requestStringConversion();
-
- try
- {
- selectionLock.wait(SELECTION_RECEIVED_TIMEOUT);
- }
- catch (InterruptedException e)
+ DataFlavor flavor = flavors[i];
+ String mimeType = flavor.getMimeType();
+ mimeTargets[i] = mimeType;
+
+ if (! text)
+ if ("text".equals(flavor.getPrimaryType())
+ || flavor.isRepresentationClassReader())
+ text = true;
+
+ // XXX - We only support automatic image conversion for
+ // GtkImages at the moment. So explicitly check that we have
+ // one.
+ if (! images && flavors[i].equals(DataFlavor.imageFlavor))
{
- return null;
+ try
+ {
+ Object o = contents.getTransferData(DataFlavor.imageFlavor);
+ if (o instanceof GtkImage)
+ images = true;
+ }
+ catch (UnsupportedFlavorException ufe)
+ {
+ }
+ catch (IOException ioe)
+ {
+ }
+ catch (ClassCastException cce)
+ {
+ }
}
-
- return selection == null ? null : new StringSelection(selection);
+
+ if (flavors[i].equals(DataFlavor.javaFileListFlavor))
+ files = true;
}
+
+ advertiseContent(mimeTargets, text, images, files);
}
- void stringSelectionReceived(String newSelection)
+ /**
+ * 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()
{
- synchronized (selectionLock)
+ 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)
{
- selection = newSelection;
- selectionLock.notify();
+ try
+ {
+ return (String) contents.getTransferData(DataFlavor.stringFlavor);
+ }
+ catch (UnsupportedFlavorException ufe)
+ {
+ }
+ catch (IOException ioe)
+ {
+ }
+ catch (ClassCastException cce)
+ {
+ }
}
- }
-
- /* convert Java clipboard data into a String suitable for sending
- to another application */
- synchronized String stringSelectionHandler() throws IOException
- {
- String selection = null;
+ // Try to get a plain text reader for the current contents and
+ // turn the result into a string.
try
{
- if (contents.isDataFlavorSupported(DataFlavor.stringFlavor))
- selection = (String)contents.getTransferData(DataFlavor.stringFlavor);
- else if (contents.isDataFlavorSupported(DataFlavor.plainTextFlavor))
+ DataFlavor plainText = DataFlavor.getTextPlainUnicodeFlavor();
+ Reader r = plainText.getReaderForText(contents);
+ if (r != null)
{
- StringBuffer sbuf = new StringBuffer();
- InputStreamReader reader;
- char readBuf[] = new char[512];
- int numChars;
-
- reader = new InputStreamReader
- ((InputStream)
- contents.getTransferData(DataFlavor.plainTextFlavor), "UNICODE");
-
- while (true)
+ StringBuffer sb = new StringBuffer();
+ char[] cs = new char[1024];
+ int l = r.read(cs);
+ while (l != -1)
{
- numChars = reader.read(readBuf);
- if (numChars == -1)
- break;
- sbuf.append(readBuf, 0, numChars);
+ sb.append(cs, 0, l);
+ l = r.read(cs);
}
-
- selection = new String(sbuf);
+ return sb.toString();
}
}
- catch (Exception e)
+ catch (IllegalArgumentException iae)
+ {
+ }
+ catch (UnsupportedEncodingException iee)
{
}
-
- return selection;
+ catch (UnsupportedFlavorException ufe)
+ {
+ }
+ catch (IOException ioe)
+ {
+ }
+
+ return null;
}
- public synchronized void setContents(Transferable contents,
- ClipboardOwner owner)
+ /**
+ * 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
+ {
+ return (GtkImage) contents.getTransferData(DataFlavor.imageFlavor);
+ }
+ 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()
{
- selectionGet();
+ Transferable contents = this.contents;
+ if (contents == null || contents instanceof GtkSelection)
+ return null;
- this.contents = contents;
- this.owner = owner;
+ 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)
+ {
+ }
- hasSelection = true;
+ return null;
}
- synchronized void selectionClear()
+ /**
+ * 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)
{
- hasSelection = false;
+ // 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 (owner != null)
+ 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)
{
- owner.lostOwnership(this, contents);
- owner = null;
- contents = null;
}
+ catch (IOException ioe)
+ {
+ }
+ catch (ClassCastException cce)
+ {
+ }
+
+ return null;
}
- native void initNativeState();
- static native void requestStringConversion();
- static native void selectionGet();
+ /**
+ * Initializes the gtk+ clipboard 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(String stringTarget,
+ String imageTarget,
+ String filesTarget);
}