|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.awt.datatransfer; |
|
|
|
import java.awt.EventQueue; |
|
|
|
import java.awt.datatransfer.Clipboard; |
|
import java.awt.datatransfer.FlavorTable; |
|
import java.awt.datatransfer.SystemFlavorMap; |
|
import java.awt.datatransfer.Transferable; |
|
import java.awt.datatransfer.ClipboardOwner; |
|
import java.awt.datatransfer.DataFlavor; |
|
import java.awt.datatransfer.FlavorListener; |
|
import java.awt.datatransfer.FlavorEvent; |
|
import java.awt.datatransfer.UnsupportedFlavorException; |
|
|
|
import java.beans.PropertyChangeEvent; |
|
import java.beans.PropertyChangeListener; |
|
import java.util.Arrays; |
|
import java.util.Iterator; |
|
import java.util.Set; |
|
import java.util.HashSet; |
|
|
|
import java.io.IOException; |
|
|
|
import sun.awt.AppContext; |
|
import sun.awt.PeerEvent; |
|
import sun.awt.SunToolkit; |
|
import sun.awt.EventListenerAggregate; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public abstract class SunClipboard extends Clipboard |
|
implements PropertyChangeListener { |
|
|
|
private AppContext contentsContext = null; |
|
|
|
private final Object CLIPBOARD_FLAVOR_LISTENER_KEY; |
|
|
|
|
|
|
|
|
|
*/ |
|
private volatile int numberOfFlavorListeners = 0; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private volatile long[] currentFormats; |
|
|
|
public SunClipboard(String name) { |
|
super(name); |
|
CLIPBOARD_FLAVOR_LISTENER_KEY = new StringBuffer(name + "_CLIPBOARD_FLAVOR_LISTENER_KEY"); |
|
} |
|
|
|
public synchronized void setContents(Transferable contents, |
|
ClipboardOwner owner) { |
|
// 4378007 : Toolkit.getSystemClipboard().setContents(null, null) |
|
|
|
if (contents == null) { |
|
throw new NullPointerException("contents"); |
|
} |
|
|
|
initContext(); |
|
|
|
final ClipboardOwner oldOwner = this.owner; |
|
final Transferable oldContents = this.contents; |
|
|
|
try { |
|
this.owner = owner; |
|
this.contents = new TransferableProxy(contents, true); |
|
|
|
setContentsNative(contents); |
|
} finally { |
|
if (oldOwner != null && oldOwner != owner) { |
|
EventQueue.invokeLater(new Runnable() { |
|
public void run() { |
|
oldOwner.lostOwnership(SunClipboard.this, oldContents); |
|
} |
|
}); |
|
} |
|
} |
|
} |
|
|
|
private synchronized void initContext() { |
|
final AppContext context = AppContext.getAppContext(); |
|
|
|
if (contentsContext != context) { |
|
// Need to synchronize on the AppContext to guarantee that it cannot |
|
|
|
synchronized (context) { |
|
if (context.isDisposed()) { |
|
throw new IllegalStateException("Can't set contents from disposed AppContext"); |
|
} |
|
context.addPropertyChangeListener |
|
(AppContext.DISPOSED_PROPERTY_NAME, this); |
|
} |
|
if (contentsContext != null) { |
|
contentsContext.removePropertyChangeListener |
|
(AppContext.DISPOSED_PROPERTY_NAME, this); |
|
} |
|
contentsContext = context; |
|
} |
|
} |
|
|
|
public synchronized Transferable getContents(Object requestor) { |
|
if (contents != null) { |
|
return contents; |
|
} |
|
return new ClipboardTransferable(this); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected synchronized Transferable getContextContents() { |
|
AppContext context = AppContext.getAppContext(); |
|
return (context == contentsContext) ? contents : null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public DataFlavor[] getAvailableDataFlavors() { |
|
Transferable cntnts = getContextContents(); |
|
if (cntnts != null) { |
|
return cntnts.getTransferDataFlavors(); |
|
} |
|
|
|
long[] formats = getClipboardFormatsOpenClose(); |
|
|
|
return DataTransferer.getInstance(). |
|
getFlavorsForFormatsAsArray(formats, getDefaultFlavorTable()); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean isDataFlavorAvailable(DataFlavor flavor) { |
|
if (flavor == null) { |
|
throw new NullPointerException("flavor"); |
|
} |
|
|
|
Transferable cntnts = getContextContents(); |
|
if (cntnts != null) { |
|
return cntnts.isDataFlavorSupported(flavor); |
|
} |
|
|
|
long[] formats = getClipboardFormatsOpenClose(); |
|
|
|
return formatArrayAsDataFlavorSet(formats).contains(flavor); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public Object getData(DataFlavor flavor) |
|
throws UnsupportedFlavorException, IOException { |
|
if (flavor == null) { |
|
throw new NullPointerException("flavor"); |
|
} |
|
|
|
Transferable cntnts = getContextContents(); |
|
if (cntnts != null) { |
|
return cntnts.getTransferData(flavor); |
|
} |
|
|
|
long format = 0; |
|
byte[] data = null; |
|
Transferable localeTransferable = null; |
|
|
|
try { |
|
openClipboard(null); |
|
|
|
long[] formats = getClipboardFormats(); |
|
Long lFormat = (Long)DataTransferer.getInstance(). |
|
getFlavorsForFormats(formats, getDefaultFlavorTable()).get(flavor); |
|
|
|
if (lFormat == null) { |
|
throw new UnsupportedFlavorException(flavor); |
|
} |
|
|
|
format = lFormat.longValue(); |
|
data = getClipboardData(format); |
|
|
|
if (DataTransferer.getInstance().isLocaleDependentTextFormat(format)) { |
|
localeTransferable = createLocaleTransferable(formats); |
|
} |
|
|
|
} finally { |
|
closeClipboard(); |
|
} |
|
|
|
return DataTransferer.getInstance(). |
|
translateBytes(data, flavor, format, localeTransferable); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected Transferable createLocaleTransferable(long[] formats) throws IOException { |
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void openClipboard(SunClipboard newOwner) {} |
|
public void closeClipboard() {} |
|
|
|
public abstract long getID(); |
|
|
|
public void propertyChange(PropertyChangeEvent evt) { |
|
if (AppContext.DISPOSED_PROPERTY_NAME.equals(evt.getPropertyName()) && |
|
Boolean.TRUE.equals(evt.getNewValue())) { |
|
final AppContext disposedContext = (AppContext)evt.getSource(); |
|
lostOwnershipLater(disposedContext); |
|
} |
|
} |
|
|
|
protected void lostOwnershipImpl() { |
|
lostOwnershipLater(null); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void lostOwnershipLater(final AppContext disposedContext) { |
|
final AppContext context = this.contentsContext; |
|
if (context == null) { |
|
return; |
|
} |
|
|
|
SunToolkit.postEvent(context, new PeerEvent(this, () -> lostOwnershipNow(disposedContext), |
|
PeerEvent.PRIORITY_EVENT)); |
|
} |
|
|
|
protected void lostOwnershipNow(final AppContext disposedContext) { |
|
final SunClipboard sunClipboard = SunClipboard.this; |
|
ClipboardOwner owner = null; |
|
Transferable contents = null; |
|
|
|
synchronized (sunClipboard) { |
|
final AppContext context = sunClipboard.contentsContext; |
|
|
|
if (context == null) { |
|
return; |
|
} |
|
|
|
if (disposedContext == null || context == disposedContext) { |
|
owner = sunClipboard.owner; |
|
contents = sunClipboard.contents; |
|
sunClipboard.contentsContext = null; |
|
sunClipboard.owner = null; |
|
sunClipboard.contents = null; |
|
sunClipboard.clearNativeContext(); |
|
context.removePropertyChangeListener |
|
(AppContext.DISPOSED_PROPERTY_NAME, sunClipboard); |
|
} else { |
|
return; |
|
} |
|
} |
|
if (owner != null) { |
|
owner.lostOwnership(sunClipboard, contents); |
|
} |
|
} |
|
|
|
|
|
protected abstract void clearNativeContext(); |
|
|
|
protected abstract void setContentsNative(Transferable contents); |
|
|
|
|
|
|
|
*/ |
|
protected long[] getClipboardFormatsOpenClose() { |
|
try { |
|
openClipboard(null); |
|
return getClipboardFormats(); |
|
} finally { |
|
closeClipboard(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected abstract long[] getClipboardFormats(); |
|
|
|
protected abstract byte[] getClipboardData(long format) throws IOException; |
|
|
|
|
|
private static Set formatArrayAsDataFlavorSet(long[] formats) { |
|
return (formats == null) ? null : |
|
DataTransferer.getInstance(). |
|
getFlavorsForFormatsAsSet(formats, getDefaultFlavorTable()); |
|
} |
|
|
|
|
|
public synchronized void addFlavorListener(FlavorListener listener) { |
|
if (listener == null) { |
|
return; |
|
} |
|
AppContext appContext = AppContext.getAppContext(); |
|
EventListenerAggregate contextFlavorListeners = (EventListenerAggregate) |
|
appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY); |
|
if (contextFlavorListeners == null) { |
|
contextFlavorListeners = new EventListenerAggregate(FlavorListener.class); |
|
appContext.put(CLIPBOARD_FLAVOR_LISTENER_KEY, contextFlavorListeners); |
|
} |
|
contextFlavorListeners.add(listener); |
|
|
|
if (numberOfFlavorListeners++ == 0) { |
|
long[] currentFormats = null; |
|
try { |
|
openClipboard(null); |
|
currentFormats = getClipboardFormats(); |
|
} catch (final IllegalStateException ignored) { |
|
} finally { |
|
closeClipboard(); |
|
} |
|
this.currentFormats = currentFormats; |
|
|
|
registerClipboardViewerChecked(); |
|
} |
|
} |
|
|
|
public synchronized void removeFlavorListener(FlavorListener listener) { |
|
if (listener == null) { |
|
return; |
|
} |
|
AppContext appContext = AppContext.getAppContext(); |
|
EventListenerAggregate contextFlavorListeners = (EventListenerAggregate) |
|
appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY); |
|
if (contextFlavorListeners == null){ |
|
|
|
return; |
|
} |
|
if (contextFlavorListeners.remove(listener) && |
|
--numberOfFlavorListeners == 0) { |
|
unregisterClipboardViewerChecked(); |
|
currentFormats = null; |
|
} |
|
} |
|
|
|
public synchronized FlavorListener[] getFlavorListeners() { |
|
EventListenerAggregate contextFlavorListeners = (EventListenerAggregate) |
|
AppContext.getAppContext().get(CLIPBOARD_FLAVOR_LISTENER_KEY); |
|
return contextFlavorListeners == null ? new FlavorListener[0] : |
|
(FlavorListener[])contextFlavorListeners.getListenersCopy(); |
|
} |
|
|
|
public boolean areFlavorListenersRegistered() { |
|
return (numberOfFlavorListeners > 0); |
|
} |
|
|
|
protected abstract void registerClipboardViewerChecked(); |
|
|
|
protected abstract void unregisterClipboardViewerChecked(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected final void checkChange(final long[] formats) { |
|
if (Arrays.equals(formats, currentFormats)) { |
|
// we've been able to successfully get available on the clipboard |
|
// DataFlavors this and previous time and they are coincident; |
|
|
|
return; |
|
} |
|
currentFormats = formats; |
|
|
|
|
|
class SunFlavorChangeNotifier implements Runnable { |
|
private final FlavorListener flavorListener; |
|
|
|
SunFlavorChangeNotifier(FlavorListener flavorListener) { |
|
this.flavorListener = flavorListener; |
|
} |
|
|
|
public void run() { |
|
if (flavorListener != null) { |
|
flavorListener.flavorsChanged(new FlavorEvent(SunClipboard.this)); |
|
} |
|
} |
|
}; |
|
|
|
for (Iterator it = AppContext.getAppContexts().iterator(); it.hasNext();) { |
|
AppContext appContext = (AppContext)it.next(); |
|
if (appContext == null || appContext.isDisposed()) { |
|
continue; |
|
} |
|
EventListenerAggregate flavorListeners = (EventListenerAggregate) |
|
appContext.get(CLIPBOARD_FLAVOR_LISTENER_KEY); |
|
if (flavorListeners != null) { |
|
FlavorListener[] flavorListenerArray = |
|
(FlavorListener[])flavorListeners.getListenersInternal(); |
|
for (int i = 0; i < flavorListenerArray.length; i++) { |
|
SunToolkit.postEvent(appContext, new PeerEvent(this, |
|
new SunFlavorChangeNotifier(flavorListenerArray[i]), |
|
PeerEvent.PRIORITY_EVENT)); |
|
} |
|
} |
|
} |
|
} |
|
|
|
public static FlavorTable getDefaultFlavorTable() { |
|
return (FlavorTable) SystemFlavorMap.getDefaultFlavorMap(); |
|
} |
|
} |