// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.gui.datatransfer;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Toolkit;
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.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.gui.util.GuiHelper;
import org.openstreetmap.josm.tools.Utils;
/**
* This is a utility class that provides methods useful for general data transfer support.
*
* @author Michael Zangl
* @since 10604
*/
public final class ClipboardUtils {
private static final class DoNothingClipboardOwner implements ClipboardOwner {
@Override
public void lostOwnership(Clipboard clpbrd, Transferable t) {
// Do nothing
}
}
private static Clipboard clipboard;
private ClipboardUtils() {
}
/**
* This method should be used from all of JOSM to access the clipboard.
* <p>
* It will default to the system clipboard except for cases where that clipboard is not accessible.
* @return A clipboard.
* @see #getClipboardContent()
*/
public static synchronized Clipboard getClipboard() {
// Might be unsupported in some more cases, we need a fake clipboard then.
if (clipboard == null) {
try {
clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
} catch (HeadlessException e) {
Main.warn("Headless. Using fake clipboard.", e);
clipboard = new Clipboard("fake");
}
}
return clipboard;
}
/**
* Gets the singleton instance of the system selection as a <code>Clipboard</code> object.
* This allows an application to read and modify the current, system-wide selection.
* @return the system selection as a <code>Clipboard</code>, or <code>null</code> if the native platform does not
* support a system selection <code>Clipboard</code> or if GraphicsEnvironment.isHeadless() returns true
* @see Toolkit#getSystemSelection
*/
public static Clipboard getSystemSelection() {
if (GraphicsEnvironment.isHeadless()) {
return null;
} else {
return Toolkit.getDefaultToolkit().getSystemSelection();
}
}
/**
* Gets the clipboard content as string.
* @return the content if available, <code>null</code> otherwise.
*/
public static String getClipboardStringContent() {
try {
Transferable t = getClipboardContent();
if (t != null && t.isDataFlavorSupported(DataFlavor.stringFlavor)) {
return (String) t.getTransferData(DataFlavor.stringFlavor);
}
} catch (UnsupportedFlavorException | IOException ex) {
Main.error(ex);
}
return null;
}
/**
* Extracts clipboard content as {@code Transferable} object. Using this method avoids some problems on some platforms.
* @return The content or <code>null</code> if it is not available
*/
public static synchronized Transferable getClipboardContent() {
return getClipboardContent(getClipboard());
}
/**
* Extracts clipboard content as {@code Transferable} object. Using this method avoids some problems on some platforms.
* @param clipboard clipboard from which contents are retrieved
* @return clipboard contents if available, {@code null} otherwise.
*/
public static Transferable getClipboardContent(Clipboard clipboard) {
Transferable t = null;
for (int tries = 0; t == null && tries < 10; tries++) {
try {
t = clipboard.getContents(null);
} catch (IllegalStateException e) {
// Clipboard currently unavailable.
// On some platforms, the system clipboard is unavailable while it is accessed by another application.
Main.trace("Clipboard unavailable.", e);
try {
Thread.sleep(1);
} catch (InterruptedException ex) {
Main.warn(ex, "InterruptedException in " + Utils.class.getSimpleName()
+ " while getting clipboard content");
Thread.currentThread().interrupt();
}
} catch (NullPointerException e) { // NOPMD
// JDK-6322854: On Linux/X11, NPE can happen for unknown reasons, on all versions of Java
Main.error(e);
}
}
return t;
}
/**
* Copy the given string to the clipboard.
* @param s The string to copy.
* @return True if the copy was successful
*/
public static boolean copyString(String s) {
return copy(new StringSelection(s));
}
/**
* Copies the given transferable to the clipboard. Handles state problems that occur on some platforms.
* @param transferable The transferable to copy.
* @return True if the copy was successful
*/
public static boolean copy(final Transferable transferable) {
return GuiHelper.runInEDTAndWaitAndReturn(() -> {
try {
getClipboard().setContents(transferable, new DoNothingClipboardOwner());
return Boolean.TRUE;
} catch (IllegalStateException ex) {
Main.error(ex);
return Boolean.FALSE;
}
});
}
/**
* Returns a new {@link DataFlavor} for the given class and human-readable name.
* @param c class
* @param humanPresentableName the human-readable string used to identify this flavor
* @return a new {@link DataFlavor} for the given class and human-readable name
* @since 10801
*/
public static DataFlavor newDataFlavor(Class<?> c, String humanPresentableName) {
try {
return new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + ";class=" + c.getName(),
humanPresentableName, c.getClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}
}