/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.beans.*;
import java.lang.reflect.*;
import java.io.*;
import java.util.TooManyListenersException;
import javax.swing.plaf.UIResource;
import javax.swing.event.*;
import javax.swing.text.JTextComponent;
import sun.reflect.misc.MethodUtil;
import sun.swing.SwingUtilities2;
import sun.awt.AppContext;
import sun.swing.*;
import sun.awt.SunToolkit;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.AccessControlContext;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.JavaSecurityAccess;
import sun.awt.AWTAccessor;
This class is used to handle the transfer of a Transferable
to and from Swing components. The Transferable
is used to
represent data that is exchanged via a cut, copy, or paste
to/from a clipboard. It is also used in drag-and-drop operations
to represent a drag from a component, and a drop to a component.
Swing provides functionality that automatically supports cut, copy,
and paste keyboard bindings that use the functionality provided by
an implementation of this class. Swing also provides functionality
that automatically supports drag and drop that uses the functionality
provided by an implementation of this class. The Swing developer can
concentrate on specifying the semantics of a transfer primarily by setting
the transferHandler
property on a Swing component.
This class is implemented to provide a default behavior of transferring
a component property simply by specifying the name of the property in
the constructor. For example, to transfer the foreground color from
one component to another either via the clipboard or a drag and drop operation
a TransferHandler
can be constructed with the string "foreground". The
built in support will use the color returned by getForeground
as the source
of the transfer, and setForeground
for the target of a transfer.
Please see
How to Use Drag and Drop and Data Transfer,
a section in The Java Tutorial, for more information.
Author: Timothy Prinzing, Shannon Hickey Since: 1.4
/**
* This class is used to handle the transfer of a <code>Transferable</code>
* to and from Swing components. The <code>Transferable</code> is used to
* represent data that is exchanged via a cut, copy, or paste
* to/from a clipboard. It is also used in drag-and-drop operations
* to represent a drag from a component, and a drop to a component.
* Swing provides functionality that automatically supports cut, copy,
* and paste keyboard bindings that use the functionality provided by
* an implementation of this class. Swing also provides functionality
* that automatically supports drag and drop that uses the functionality
* provided by an implementation of this class. The Swing developer can
* concentrate on specifying the semantics of a transfer primarily by setting
* the <code>transferHandler</code> property on a Swing component.
* <p>
* This class is implemented to provide a default behavior of transferring
* a component property simply by specifying the name of the property in
* the constructor. For example, to transfer the foreground color from
* one component to another either via the clipboard or a drag and drop operation
* a <code>TransferHandler</code> can be constructed with the string "foreground". The
* built in support will use the color returned by <code>getForeground</code> as the source
* of the transfer, and <code>setForeground</code> for the target of a transfer.
* <p>
* Please see
* <a href="https://docs.oracle.com/javase/tutorial/uiswing/dnd/index.html">
* How to Use Drag and Drop and Data Transfer</a>,
* a section in <em>The Java Tutorial</em>, for more information.
*
*
* @author Timothy Prinzing
* @author Shannon Hickey
* @since 1.4
*/
@SuppressWarnings("serial")
public class TransferHandler implements Serializable {
An int
representing no transfer action.
/**
* An <code>int</code> representing no transfer action.
*/
public static final int NONE = DnDConstants.ACTION_NONE;
An int
representing a "copy" transfer action.
This value is used when data is copied to a clipboard
or copied elsewhere in a drag and drop operation.
/**
* An <code>int</code> representing a "copy" transfer action.
* This value is used when data is copied to a clipboard
* or copied elsewhere in a drag and drop operation.
*/
public static final int COPY = DnDConstants.ACTION_COPY;
An int
representing a "move" transfer action.
This value is used when data is moved to a clipboard (i.e. a cut)
or moved elsewhere in a drag and drop operation.
/**
* An <code>int</code> representing a "move" transfer action.
* This value is used when data is moved to a clipboard (i.e. a cut)
* or moved elsewhere in a drag and drop operation.
*/
public static final int MOVE = DnDConstants.ACTION_MOVE;
An int
representing a source action capability of either
"copy" or "move".
/**
* An <code>int</code> representing a source action capability of either
* "copy" or "move".
*/
public static final int COPY_OR_MOVE = DnDConstants.ACTION_COPY_OR_MOVE;
An int
representing a "link" transfer action.
This value is used to specify that data should be linked in a drag
and drop operation.
See Also: - ACTION_LINK.ACTION_LINK
Since: 1.6
/**
* An <code>int</code> representing a "link" transfer action.
* This value is used to specify that data should be linked in a drag
* and drop operation.
*
* @see java.awt.dnd.DnDConstants#ACTION_LINK
* @since 1.6
*/
public static final int LINK = DnDConstants.ACTION_LINK;
An interface to tag things with a getTransferHandler
method. /**
* An interface to tag things with a {@code getTransferHandler} method.
*/
interface HasGetTransferHandler {
Returns the TransferHandler
. Returns: The TransferHandler
or null
/** Returns the {@code TransferHandler}.
*
* @return The {@code TransferHandler} or {@code null}
*/
public TransferHandler getTransferHandler();
}
Represents a location where dropped data should be inserted.
This is a base class that only encapsulates a point.
Components supporting drop may provide subclasses of this
containing more information.
Developers typically shouldn't create instances of, or extend, this
class. Instead, these are something provided by the DnD
implementation by TransferSupport
instances and by
components with a getDropLocation()
method.
See Also: - getDropLocation.getDropLocation
Since: 1.6
/**
* Represents a location where dropped data should be inserted.
* This is a base class that only encapsulates a point.
* Components supporting drop may provide subclasses of this
* containing more information.
* <p>
* Developers typically shouldn't create instances of, or extend, this
* class. Instead, these are something provided by the DnD
* implementation by <code>TransferSupport</code> instances and by
* components with a <code>getDropLocation()</code> method.
*
* @see javax.swing.TransferHandler.TransferSupport#getDropLocation
* @since 1.6
*/
public static class DropLocation {
private final Point dropPoint;
Constructs a drop location for the given point.
Params: - dropPoint – the drop point, representing the mouse's
current location within the component.
Throws: - IllegalArgumentException – if the point
is
null
/**
* Constructs a drop location for the given point.
*
* @param dropPoint the drop point, representing the mouse's
* current location within the component.
* @throws IllegalArgumentException if the point
* is <code>null</code>
*/
protected DropLocation(Point dropPoint) {
if (dropPoint == null) {
throw new IllegalArgumentException("Point cannot be null");
}
this.dropPoint = new Point(dropPoint);
}
Returns the drop point, representing the mouse's
current location within the component.
Returns: the drop point.
/**
* Returns the drop point, representing the mouse's
* current location within the component.
*
* @return the drop point.
*/
public final Point getDropPoint() {
return new Point(dropPoint);
}
Returns a string representation of this drop location.
This method is intended to be used for debugging purposes,
and the content and format of the returned string may vary
between implementations.
Returns: a string representation of this drop location
/**
* Returns a string representation of this drop location.
* This method is intended to be used for debugging purposes,
* and the content and format of the returned string may vary
* between implementations.
*
* @return a string representation of this drop location
*/
public String toString() {
return getClass().getName() + "[dropPoint=" + dropPoint + "]";
}
};
This class encapsulates all relevant details of a clipboard
or drag and drop transfer, and also allows for customizing
aspects of the drag and drop experience.
The main purpose of this class is to provide the information
needed by a developer to determine the suitability of a
transfer or to import the data contained within. But it also
doubles as a controller for customizing properties during drag
and drop, such as whether or not to show the drop location,
and which drop action to use.
Developers typically need not create instances of this
class. Instead, they are something provided by the DnD
implementation to certain methods in TransferHandler
.
See Also: - canImport(TransferSupport)
- TransferHandler.importData(TransferSupport)
Since: 1.6
/**
* This class encapsulates all relevant details of a clipboard
* or drag and drop transfer, and also allows for customizing
* aspects of the drag and drop experience.
* <p>
* The main purpose of this class is to provide the information
* needed by a developer to determine the suitability of a
* transfer or to import the data contained within. But it also
* doubles as a controller for customizing properties during drag
* and drop, such as whether or not to show the drop location,
* and which drop action to use.
* <p>
* Developers typically need not create instances of this
* class. Instead, they are something provided by the DnD
* implementation to certain methods in <code>TransferHandler</code>.
*
* @see #canImport(TransferHandler.TransferSupport)
* @see #importData(TransferHandler.TransferSupport)
* @since 1.6
*/
public static final class TransferSupport {
private boolean isDrop;
private Component component;
private boolean showDropLocationIsSet;
private boolean showDropLocation;
private int dropAction = -1;
The source is a DropTargetDragEvent
or DropTargetDropEvent
for drops, and a Transferable
otherwise /**
* The source is a {@code DropTargetDragEvent} or
* {@code DropTargetDropEvent} for drops,
* and a {@code Transferable} otherwise
*/
private Object source;
private DropLocation dropLocation;
Create a TransferSupport
with isDrop()
true
for the given component, event, and index.
Params: - component – the target component
- event – a
DropTargetEvent
/**
* Create a <code>TransferSupport</code> with <code>isDrop()</code>
* <code>true</code> for the given component, event, and index.
*
* @param component the target component
* @param event a <code>DropTargetEvent</code>
*/
private TransferSupport(Component component,
DropTargetEvent event) {
isDrop = true;
setDNDVariables(component, event);
}
Create a TransferSupport
with isDrop()
false
for the given component and
Transferable
.
Params: - component – the target component
- transferable – the transferable
Throws: - NullPointerException – if either parameter
is
null
/**
* Create a <code>TransferSupport</code> with <code>isDrop()</code>
* <code>false</code> for the given component and
* <code>Transferable</code>.
*
* @param component the target component
* @param transferable the transferable
* @throws NullPointerException if either parameter
* is <code>null</code>
*/
public TransferSupport(Component component, Transferable transferable) {
if (component == null) {
throw new NullPointerException("component is null");
}
if (transferable == null) {
throw new NullPointerException("transferable is null");
}
isDrop = false;
this.component = component;
this.source = transferable;
}
Allows for a single instance to be reused during DnD.
Params: - component – the target component
- event – a
DropTargetEvent
/**
* Allows for a single instance to be reused during DnD.
*
* @param component the target component
* @param event a <code>DropTargetEvent</code>
*/
private void setDNDVariables(Component component,
DropTargetEvent event) {
assert isDrop;
this.component = component;
this.source = event;
dropLocation = null;
dropAction = -1;
showDropLocationIsSet = false;
if (source == null) {
return;
}
assert source instanceof DropTargetDragEvent ||
source instanceof DropTargetDropEvent;
Point p = source instanceof DropTargetDragEvent
? ((DropTargetDragEvent)source).getLocation()
: ((DropTargetDropEvent)source).getLocation();
if (SunToolkit.isInstanceOf(component, "javax.swing.text.JTextComponent")) {
dropLocation = SwingAccessor.getJTextComponentAccessor().
dropLocationForPoint((JTextComponent)component, p);
} else if (component instanceof JComponent) {
dropLocation = ((JComponent)component).dropLocationForPoint(p);
}
/*
* The drop location may be null at this point if the component
* doesn't return custom drop locations. In this case, a point-only
* drop location will be created lazily when requested.
*/
}
Returns whether or not this TransferSupport
represents a drop operation.
Returns: true
if this is a drop operation,
false
otherwise.
/**
* Returns whether or not this <code>TransferSupport</code>
* represents a drop operation.
*
* @return <code>true</code> if this is a drop operation,
* <code>false</code> otherwise.
*/
public boolean isDrop() {
return isDrop;
}
Returns the target component of this transfer.
Returns: the target component
/**
* Returns the target component of this transfer.
*
* @return the target component
*/
public Component getComponent() {
return component;
}
Checks that this is a drop and throws an IllegalStateException
if it isn't. Throws: - IllegalStateException – if
isDrop
is false.
/**
* Checks that this is a drop and throws an
* {@code IllegalStateException} if it isn't.
*
* @throws IllegalStateException if {@code isDrop} is false.
*/
private void assureIsDrop() {
if (!isDrop) {
throw new IllegalStateException("Not a drop");
}
}
Returns the current (non-null
) drop location for the component, when this TransferSupport
represents a drop. Note: For components with built-in drop support, this location will be a subclass of DropLocation
of the same type returned by that component's getDropLocation
method.
This method is only for use with drag and drop transfers. Calling it when isDrop()
is false
results in an IllegalStateException
.
Throws: - IllegalStateException – if this is not a drop
See Also: Returns: the drop location
/**
* Returns the current (non-{@code null}) drop location for the component,
* when this {@code TransferSupport} represents a drop.
* <p>
* Note: For components with built-in drop support, this location
* will be a subclass of {@code DropLocation} of the same type
* returned by that component's {@code getDropLocation} method.
* <p>
* This method is only for use with drag and drop transfers.
* Calling it when {@code isDrop()} is {@code false} results
* in an {@code IllegalStateException}.
*
* @return the drop location
* @throws IllegalStateException if this is not a drop
* @see #isDrop()
*/
public DropLocation getDropLocation() {
assureIsDrop();
if (dropLocation == null) {
/*
* component didn't give us a custom drop location,
* so lazily create a point-only location
*/
Point p = source instanceof DropTargetDragEvent
? ((DropTargetDragEvent)source).getLocation()
: ((DropTargetDropEvent)source).getLocation();
dropLocation = new DropLocation(p);
}
return dropLocation;
}
Sets whether or not the drop location should be visually indicated for the transfer - which must represent a drop. This is applicable to those components that automatically show the drop location when appropriate during a drag and drop operation. By default, the drop location is shown only when the TransferHandler
has said it can accept the import represented by this TransferSupport
. With this method you can force the drop location to always be shown, or always not be shown. This method is only for use with drag and drop transfers. Calling it when isDrop()
is false
results in an IllegalStateException
.
Params: - showDropLocation – whether or not to indicate the drop location
Throws: - IllegalStateException – if this is not a drop
See Also:
/**
* Sets whether or not the drop location should be visually indicated
* for the transfer - which must represent a drop. This is applicable to
* those components that automatically
* show the drop location when appropriate during a drag and drop
* operation. By default, the drop location is shown only when the
* {@code TransferHandler} has said it can accept the import represented
* by this {@code TransferSupport}. With this method you can force the
* drop location to always be shown, or always not be shown.
* <p>
* This method is only for use with drag and drop transfers.
* Calling it when {@code isDrop()} is {@code false} results
* in an {@code IllegalStateException}.
*
* @param showDropLocation whether or not to indicate the drop location
* @throws IllegalStateException if this is not a drop
* @see #isDrop()
*/
public void setShowDropLocation(boolean showDropLocation) {
assureIsDrop();
this.showDropLocation = showDropLocation;
this.showDropLocationIsSet = true;
}
Sets the drop action for the transfer - which must represent a drop - to the given action, instead of the default user drop action. The action must be supported by the source's drop actions, and must be one of COPY
, MOVE
or LINK
. This method is only for use with drag and drop transfers. Calling it when isDrop()
is false
results in an IllegalStateException
.
Params: - dropAction – the drop action
Throws: - IllegalStateException – if this is not a drop
- IllegalArgumentException – if an invalid action is specified
See Also:
/**
* Sets the drop action for the transfer - which must represent a drop
* - to the given action,
* instead of the default user drop action. The action must be
* supported by the source's drop actions, and must be one
* of {@code COPY}, {@code MOVE} or {@code LINK}.
* <p>
* This method is only for use with drag and drop transfers.
* Calling it when {@code isDrop()} is {@code false} results
* in an {@code IllegalStateException}.
*
* @param dropAction the drop action
* @throws IllegalStateException if this is not a drop
* @throws IllegalArgumentException if an invalid action is specified
* @see #getDropAction
* @see #getUserDropAction
* @see #getSourceDropActions
* @see #isDrop()
*/
public void setDropAction(int dropAction) {
assureIsDrop();
int action = dropAction & getSourceDropActions();
if (!(action == COPY || action == MOVE || action == LINK)) {
throw new IllegalArgumentException("unsupported drop action: " + dropAction);
}
this.dropAction = dropAction;
}
Returns the action chosen for the drop, when this TransferSupport
represents a drop. Unless explicitly chosen by way of setDropAction
, this returns the user drop action provided by getUserDropAction
.
You may wish to query this in TransferHandler
's importData
method to customize processing based on the action.
This method is only for use with drag and drop transfers. Calling it when isDrop()
is false
results in an IllegalStateException
.
Throws: - IllegalStateException – if this is not a drop
See Also: Returns: the action chosen for the drop
/**
* Returns the action chosen for the drop, when this
* {@code TransferSupport} represents a drop.
* <p>
* Unless explicitly chosen by way of {@code setDropAction},
* this returns the user drop action provided by
* {@code getUserDropAction}.
* <p>
* You may wish to query this in {@code TransferHandler}'s
* {@code importData} method to customize processing based
* on the action.
* <p>
* This method is only for use with drag and drop transfers.
* Calling it when {@code isDrop()} is {@code false} results
* in an {@code IllegalStateException}.
*
* @return the action chosen for the drop
* @throws IllegalStateException if this is not a drop
* @see #setDropAction
* @see #getUserDropAction
* @see #isDrop()
*/
public int getDropAction() {
return dropAction == -1 ? getUserDropAction() : dropAction;
}
Returns the user drop action for the drop, when this TransferSupport
represents a drop. The user drop action is chosen for a drop as described in the documentation for DropTargetDragEvent
and DropTargetDropEvent
. A different action may be chosen as the drop action by way of the setDropAction
method.
You may wish to query this in TransferHandler
's canImport
method when determining the suitability of a drop or when deciding on a drop action to explicitly choose.
This method is only for use with drag and drop transfers. Calling it when isDrop()
is false
results in an IllegalStateException
.
Throws: - IllegalStateException – if this is not a drop
See Also: Returns: the user drop action
/**
* Returns the user drop action for the drop, when this
* {@code TransferSupport} represents a drop.
* <p>
* The user drop action is chosen for a drop as described in the
* documentation for {@link java.awt.dnd.DropTargetDragEvent} and
* {@link java.awt.dnd.DropTargetDropEvent}. A different action
* may be chosen as the drop action by way of the {@code setDropAction}
* method.
* <p>
* You may wish to query this in {@code TransferHandler}'s
* {@code canImport} method when determining the suitability of a
* drop or when deciding on a drop action to explicitly choose.
* <p>
* This method is only for use with drag and drop transfers.
* Calling it when {@code isDrop()} is {@code false} results
* in an {@code IllegalStateException}.
*
* @return the user drop action
* @throws IllegalStateException if this is not a drop
* @see #setDropAction
* @see #getDropAction
* @see #isDrop()
*/
public int getUserDropAction() {
assureIsDrop();
return (source instanceof DropTargetDragEvent)
? ((DropTargetDragEvent)source).getDropAction()
: ((DropTargetDropEvent)source).getDropAction();
}
Returns the drag source's supported drop actions, when this TransferSupport
represents a drop. The source actions represent the set of actions supported by the source of this transfer, and are represented as some bitwise-OR combination of COPY
, MOVE
and LINK
. You may wish to query this in TransferHandler
's canImport
method when determining the suitability of a drop or when deciding on a drop action to explicitly choose. To determine if a particular action is supported by the source, bitwise-AND the action with the source drop actions, and then compare the result against the original action. For example:
boolean copySupported = (COPY & getSourceDropActions()) == COPY;
This method is only for use with drag and drop transfers. Calling it when isDrop()
is false
results in an IllegalStateException
.
Throws: - IllegalStateException – if this is not a drop
See Also: Returns: the drag source's supported drop actions
/**
* Returns the drag source's supported drop actions, when this
* {@code TransferSupport} represents a drop.
* <p>
* The source actions represent the set of actions supported by the
* source of this transfer, and are represented as some bitwise-OR
* combination of {@code COPY}, {@code MOVE} and {@code LINK}.
* You may wish to query this in {@code TransferHandler}'s
* {@code canImport} method when determining the suitability of a drop
* or when deciding on a drop action to explicitly choose. To determine
* if a particular action is supported by the source, bitwise-AND
* the action with the source drop actions, and then compare the result
* against the original action. For example:
* <pre>
* boolean copySupported = (COPY & getSourceDropActions()) == COPY;
* </pre>
* <p>
* This method is only for use with drag and drop transfers.
* Calling it when {@code isDrop()} is {@code false} results
* in an {@code IllegalStateException}.
*
* @return the drag source's supported drop actions
* @throws IllegalStateException if this is not a drop
* @see #isDrop()
*/
public int getSourceDropActions() {
assureIsDrop();
return (source instanceof DropTargetDragEvent)
? ((DropTargetDragEvent)source).getSourceActions()
: ((DropTargetDropEvent)source).getSourceActions();
}
Returns the data flavors for this transfer.
Returns: the data flavors for this transfer
/**
* Returns the data flavors for this transfer.
*
* @return the data flavors for this transfer
*/
public DataFlavor[] getDataFlavors() {
if (isDrop) {
if (source instanceof DropTargetDragEvent) {
return ((DropTargetDragEvent)source).getCurrentDataFlavors();
} else {
return ((DropTargetDropEvent)source).getCurrentDataFlavors();
}
}
return ((Transferable)source).getTransferDataFlavors();
}
Returns whether or not the given data flavor is supported.
Params: - df – the
DataFlavor
to test
Returns: whether or not the given flavor is supported.
/**
* Returns whether or not the given data flavor is supported.
*
* @param df the <code>DataFlavor</code> to test
* @return whether or not the given flavor is supported.
*/
public boolean isDataFlavorSupported(DataFlavor df) {
if (isDrop) {
if (source instanceof DropTargetDragEvent) {
return ((DropTargetDragEvent)source).isDataFlavorSupported(df);
} else {
return ((DropTargetDropEvent)source).isDataFlavorSupported(df);
}
}
return ((Transferable)source).isDataFlavorSupported(df);
}
Returns the Transferable
associated with this transfer.
Note: Unless it is necessary to fetch the Transferable
directly, use one of the other methods on this class to inquire about
the transfer. This may perform better than fetching the
Transferable
and asking it directly.
Returns: the Transferable
associated with this transfer
/**
* Returns the <code>Transferable</code> associated with this transfer.
* <p>
* Note: Unless it is necessary to fetch the <code>Transferable</code>
* directly, use one of the other methods on this class to inquire about
* the transfer. This may perform better than fetching the
* <code>Transferable</code> and asking it directly.
*
* @return the <code>Transferable</code> associated with this transfer
*/
public Transferable getTransferable() {
if (isDrop) {
if (source instanceof DropTargetDragEvent) {
return ((DropTargetDragEvent)source).getTransferable();
} else {
return ((DropTargetDropEvent)source).getTransferable();
}
}
return (Transferable)source;
}
}
Returns an Action
that performs cut operations to the clipboard. When performed, this action operates on the JComponent
source of the ActionEvent
by invoking exportToClipboard
, with a MOVE
action, on the component's TransferHandler
. Returns: an Action
for performing cuts to the clipboard
/**
* Returns an {@code Action} that performs cut operations to the
* clipboard. When performed, this action operates on the {@code JComponent}
* source of the {@code ActionEvent} by invoking {@code exportToClipboard},
* with a {@code MOVE} action, on the component's {@code TransferHandler}.
*
* @return an {@code Action} for performing cuts to the clipboard
*/
public static Action getCutAction() {
return cutAction;
}
Returns an Action
that performs copy operations to the clipboard. When performed, this action operates on the JComponent
source of the ActionEvent
by invoking exportToClipboard
, with a COPY
action, on the component's TransferHandler
. Returns: an Action
for performing copies to the clipboard
/**
* Returns an {@code Action} that performs copy operations to the
* clipboard. When performed, this action operates on the {@code JComponent}
* source of the {@code ActionEvent} by invoking {@code exportToClipboard},
* with a {@code COPY} action, on the component's {@code TransferHandler}.
*
* @return an {@code Action} for performing copies to the clipboard
*/
public static Action getCopyAction() {
return copyAction;
}
Returns an Action
that performs paste operations from the clipboard. When performed, this action operates on the JComponent
source of the ActionEvent
by invoking importData
, with the clipboard contents, on the component's TransferHandler
. Returns: an Action
for performing pastes from the clipboard
/**
* Returns an {@code Action} that performs paste operations from the
* clipboard. When performed, this action operates on the {@code JComponent}
* source of the {@code ActionEvent} by invoking {@code importData},
* with the clipboard contents, on the component's {@code TransferHandler}.
*
* @return an {@code Action} for performing pastes from the clipboard
*/
public static Action getPasteAction() {
return pasteAction;
}
Constructs a transfer handler that can transfer a Java Bean property
from one component to another via the clipboard or a drag and drop
operation.
Params: - property – the name of the property to transfer; this can
be
null
if there is no property associated with the transfer
handler (a subclass that performs some other kind of transfer, for example)
/**
* Constructs a transfer handler that can transfer a Java Bean property
* from one component to another via the clipboard or a drag and drop
* operation.
*
* @param property the name of the property to transfer; this can
* be <code>null</code> if there is no property associated with the transfer
* handler (a subclass that performs some other kind of transfer, for example)
*/
public TransferHandler(String property) {
propertyName = property;
}
Convenience constructor for subclasses.
/**
* Convenience constructor for subclasses.
*/
protected TransferHandler() {
this(null);
}
image for the startDrag
method See Also:
/**
* image for the {@code startDrag} method
*
* @see java.awt.dnd.DragGestureEvent#startDrag(Cursor dragCursor, Image dragImage, Point imageOffset, Transferable transferable, DragSourceListener dsl)
*/
private Image dragImage;
anchor offset for the startDrag
method See Also:
/**
* anchor offset for the {@code startDrag} method
*
* @see java.awt.dnd.DragGestureEvent#startDrag(Cursor dragCursor, Image dragImage, Point imageOffset, Transferable transferable, DragSourceListener dsl)
*/
private Point dragImageOffset;
Sets the drag image parameter. The image has to be prepared
for rendering by the moment of the call. The image is stored
by reference because of some performance reasons.
Params: - img – an image to drag
/**
* Sets the drag image parameter. The image has to be prepared
* for rendering by the moment of the call. The image is stored
* by reference because of some performance reasons.
*
* @param img an image to drag
*/
public void setDragImage(Image img) {
dragImage = img;
}
Returns the drag image. If there is no image to drag, the returned value is null
. Returns: the reference to the drag image
/**
* Returns the drag image. If there is no image to drag,
* the returned value is {@code null}.
*
* @return the reference to the drag image
*/
public Image getDragImage() {
return dragImage;
}
Sets an anchor offset for the image to drag. It can not be null
. Params: - p – a
Point
object that corresponds to coordinates of an anchor offset of the image relative to the upper left corner of the image
/**
* Sets an anchor offset for the image to drag.
* It can not be {@code null}.
*
* @param p a {@code Point} object that corresponds
* to coordinates of an anchor offset of the image
* relative to the upper left corner of the image
*/
public void setDragImageOffset(Point p) {
dragImageOffset = new Point(p);
}
Returns an anchor offset for the image to drag.
Returns: a Point
object that corresponds to coordinates of an anchor offset of the image relative to the upper left corner of the image. The point (0,0)
returns by default.
/**
* Returns an anchor offset for the image to drag.
*
* @return a {@code Point} object that corresponds
* to coordinates of an anchor offset of the image
* relative to the upper left corner of the image.
* The point {@code (0,0)} returns by default.
*/
public Point getDragImageOffset() {
if (dragImageOffset == null) {
return new Point(0,0);
}
return new Point(dragImageOffset);
}
Causes the Swing drag support to be initiated. This is called by
the various UI implementations in the javax.swing.plaf.basic
package if the dragEnabled property is set on the component.
This can be called by custom UI
implementations to use the Swing drag support. This method can also be called
by a Swing extension written as a subclass of JComponent
to take advantage of the Swing drag support.
The transfer will not necessarily have been completed at the
return of this call (i.e. the call does not block waiting for the drop).
The transfer will take place through the Swing implementation of the
java.awt.dnd
mechanism, requiring no further effort
from the developer. The exportDone
method will be called
when the transfer has completed.
Params: - comp – the component holding the data to be transferred;
provided to enable sharing of
TransferHandler
s - e – the event that triggered the transfer
- action – the transfer action initially requested; either
COPY
, MOVE
or LINK
; the DnD system may change the action used during the course of the drag operation
/**
* Causes the Swing drag support to be initiated. This is called by
* the various UI implementations in the <code>javax.swing.plaf.basic</code>
* package if the dragEnabled property is set on the component.
* This can be called by custom UI
* implementations to use the Swing drag support. This method can also be called
* by a Swing extension written as a subclass of <code>JComponent</code>
* to take advantage of the Swing drag support.
* <p>
* The transfer <em>will not necessarily</em> have been completed at the
* return of this call (i.e. the call does not block waiting for the drop).
* The transfer will take place through the Swing implementation of the
* <code>java.awt.dnd</code> mechanism, requiring no further effort
* from the developer. The <code>exportDone</code> method will be called
* when the transfer has completed.
*
* @param comp the component holding the data to be transferred;
* provided to enable sharing of <code>TransferHandler</code>s
* @param e the event that triggered the transfer
* @param action the transfer action initially requested;
* either {@code COPY}, {@code MOVE} or {@code LINK};
* the DnD system may change the action used during the
* course of the drag operation
*/
public void exportAsDrag(JComponent comp, InputEvent e, int action) {
int srcActions = getSourceActions(comp);
// only mouse events supported for drag operations
if (!(e instanceof MouseEvent)
// only support known actions
|| !(action == COPY || action == MOVE || action == LINK)
// only support valid source actions
|| (srcActions & action) == 0) {
action = NONE;
}
if (action != NONE && !GraphicsEnvironment.isHeadless()) {
if (recognizer == null) {
recognizer = new SwingDragGestureRecognizer(new DragHandler());
}
recognizer.gestured(comp, (MouseEvent)e, srcActions, action);
} else {
exportDone(comp, null, NONE);
}
}
Causes a transfer from the given component to the
given clipboard. This method is called by the default cut and
copy actions registered in a component's action map.
The transfer will take place using the java.awt.datatransfer
mechanism, requiring no further effort from the developer. Any data
transfer will be complete and the exportDone
method will be called with the action that occurred, before this method
returns. Should the clipboard be unavailable when attempting to place
data on it, the IllegalStateException
thrown by Clipboard.setContents(Transferable, ClipboardOwner)
will be propagated through this method. However, exportDone
will first be called with an action
of NONE
for consistency.
Params: - comp – the component holding the data to be transferred;
provided to enable sharing of
TransferHandler
s - clip – the clipboard to transfer the data into
- action – the transfer action requested; this should
be a value of either
COPY
or MOVE
;
the operation performed is the intersection of the transfer
capabilities given by getSourceActions and the requested action;
the intersection may result in an action of NONE
if the requested action isn't supported
Throws: - IllegalStateException – if the clipboard is currently unavailable
See Also:
/**
* Causes a transfer from the given component to the
* given clipboard. This method is called by the default cut and
* copy actions registered in a component's action map.
* <p>
* The transfer will take place using the <code>java.awt.datatransfer</code>
* mechanism, requiring no further effort from the developer. Any data
* transfer <em>will</em> be complete and the <code>exportDone</code>
* method will be called with the action that occurred, before this method
* returns. Should the clipboard be unavailable when attempting to place
* data on it, the <code>IllegalStateException</code> thrown by
* {@link Clipboard#setContents(Transferable, ClipboardOwner)} will
* be propagated through this method. However,
* <code>exportDone</code> will first be called with an action
* of <code>NONE</code> for consistency.
*
* @param comp the component holding the data to be transferred;
* provided to enable sharing of <code>TransferHandler</code>s
* @param clip the clipboard to transfer the data into
* @param action the transfer action requested; this should
* be a value of either <code>COPY</code> or <code>MOVE</code>;
* the operation performed is the intersection of the transfer
* capabilities given by getSourceActions and the requested action;
* the intersection may result in an action of <code>NONE</code>
* if the requested action isn't supported
* @throws IllegalStateException if the clipboard is currently unavailable
* @see Clipboard#setContents(Transferable, ClipboardOwner)
*/
public void exportToClipboard(JComponent comp, Clipboard clip, int action)
throws IllegalStateException {
if ((action == COPY || action == MOVE)
&& (getSourceActions(comp) & action) != 0) {
Transferable t = createTransferable(comp);
if (t != null) {
try {
clip.setContents(t, null);
exportDone(comp, t, action);
return;
} catch (IllegalStateException ise) {
exportDone(comp, t, NONE);
throw ise;
}
}
}
exportDone(comp, null, NONE);
}
Causes a transfer to occur from a clipboard or a drag and
drop operation. The Transferable
to be
imported and the component to transfer to are contained
within the TransferSupport
.
While the drag and drop implementation calls canImport
to determine the suitability of a transfer before calling this method, the implementation of paste does not. As such, it cannot be assumed that the transfer is acceptable upon a call to this method for paste. It is recommended that canImport
be explicitly called to cover this case.
Note: The TransferSupport
object passed to this method
is only valid for the duration of the method call. It is undefined
what values it may contain after this method returns.
Params: - support – the object containing the details of
the transfer, not
null
.
Throws: - NullPointerException – if
support
is null
See Also: Returns: true if the data was inserted into the component,
false otherwise Since: 1.6
/**
* Causes a transfer to occur from a clipboard or a drag and
* drop operation. The <code>Transferable</code> to be
* imported and the component to transfer to are contained
* within the <code>TransferSupport</code>.
* <p>
* While the drag and drop implementation calls {@code canImport}
* to determine the suitability of a transfer before calling this
* method, the implementation of paste does not. As such, it cannot
* be assumed that the transfer is acceptable upon a call to
* this method for paste. It is recommended that {@code canImport} be
* explicitly called to cover this case.
* <p>
* Note: The <code>TransferSupport</code> object passed to this method
* is only valid for the duration of the method call. It is undefined
* what values it may contain after this method returns.
*
* @param support the object containing the details of
* the transfer, not <code>null</code>.
* @return true if the data was inserted into the component,
* false otherwise
* @throws NullPointerException if <code>support</code> is {@code null}
* @see #canImport(TransferHandler.TransferSupport)
* @since 1.6
*/
public boolean importData(TransferSupport support) {
return support.getComponent() instanceof JComponent
? importData((JComponent)support.getComponent(), support.getTransferable())
: false;
}
Causes a transfer to a component from a clipboard or a
DND drop operation. The Transferable
represents
the data to be imported into the component.
Note: Swing now calls the newer version of importData
that takes a TransferSupport
, which in turn calls this method (if the component in the TransferSupport
is a JComponent
). Developers are encouraged to call and override the newer version as it provides more information (and is the only version that supports use with a TransferHandler
set directly on a JFrame
or other non-JComponent
).
Params: - comp – the component to receive the transfer;
provided to enable sharing of
TransferHandler
s - t – the data to import
See Also: Returns: true if the data was inserted into the component, false otherwise
/**
* Causes a transfer to a component from a clipboard or a
* DND drop operation. The <code>Transferable</code> represents
* the data to be imported into the component.
* <p>
* Note: Swing now calls the newer version of <code>importData</code>
* that takes a <code>TransferSupport</code>, which in turn calls this
* method (if the component in the {@code TransferSupport} is a
* {@code JComponent}). Developers are encouraged to call and override the
* newer version as it provides more information (and is the only
* version that supports use with a {@code TransferHandler} set directly
* on a {@code JFrame} or other non-{@code JComponent}).
*
* @param comp the component to receive the transfer;
* provided to enable sharing of <code>TransferHandler</code>s
* @param t the data to import
* @return true if the data was inserted into the component, false otherwise
* @see #importData(TransferHandler.TransferSupport)
*/
public boolean importData(JComponent comp, Transferable t) {
PropertyDescriptor prop = getPropertyDescriptor(comp);
if (prop != null) {
Method writer = prop.getWriteMethod();
if (writer == null) {
// read-only property. ignore
return false;
}
Class<?>[] params = writer.getParameterTypes();
if (params.length != 1) {
// zero or more than one argument, ignore
return false;
}
DataFlavor flavor = getPropertyDataFlavor(params[0], t.getTransferDataFlavors());
if (flavor != null) {
try {
Object value = t.getTransferData(flavor);
Object[] args = { value };
MethodUtil.invoke(writer, comp, args);
return true;
} catch (Exception ex) {
System.err.println("Invocation failed");
// invocation code
}
}
}
return false;
}
This method is called repeatedly during a drag and drop operation to allow the developer to configure properties of, and to return the acceptability of transfers; with a return value of true
indicating that the transfer represented by the given TransferSupport
(which contains all of the details of the transfer) is acceptable at the current time, and a value of false
rejecting the transfer. For those components that automatically display a drop location during drag and drop, accepting the transfer, by default, tells them to show the drop location. This can be changed by calling setShowDropLocation
on the TransferSupport
.
By default, when the transfer is accepted, the chosen drop action is that picked by the user via their drag gesture. The developer can override this and choose a different action, from the supported source actions, by calling setDropAction
on the TransferSupport
.
On every call to canImport
, the TransferSupport
contains fresh state. As such, any properties set on it must be set on every call. Upon a drop, canImport
is called one final time before calling into importData
. Any state set on the TransferSupport
during that last call will be available in importData
.
This method is not called internally in response to paste operations. As such, it is recommended that implementations of importData
explicitly call this method for such cases and that this method be prepared to return the suitability of paste operations as well.
Note: The TransferSupport
object passed to this method
is only valid for the duration of the method call. It is undefined
what values it may contain after this method returns.
Params: - support – the object containing the details of
the transfer, not
null
.
Throws: - NullPointerException – if
support
is null
See Also: Returns: true
if the import can happen,
false
otherwiseSince: 1.6
/**
* This method is called repeatedly during a drag and drop operation
* to allow the developer to configure properties of, and to return
* the acceptability of transfers; with a return value of {@code true}
* indicating that the transfer represented by the given
* {@code TransferSupport} (which contains all of the details of the
* transfer) is acceptable at the current time, and a value of {@code false}
* rejecting the transfer.
* <p>
* For those components that automatically display a drop location during
* drag and drop, accepting the transfer, by default, tells them to show
* the drop location. This can be changed by calling
* {@code setShowDropLocation} on the {@code TransferSupport}.
* <p>
* By default, when the transfer is accepted, the chosen drop action is that
* picked by the user via their drag gesture. The developer can override
* this and choose a different action, from the supported source
* actions, by calling {@code setDropAction} on the {@code TransferSupport}.
* <p>
* On every call to {@code canImport}, the {@code TransferSupport} contains
* fresh state. As such, any properties set on it must be set on every
* call. Upon a drop, {@code canImport} is called one final time before
* calling into {@code importData}. Any state set on the
* {@code TransferSupport} during that last call will be available in
* {@code importData}.
* <p>
* This method is not called internally in response to paste operations.
* As such, it is recommended that implementations of {@code importData}
* explicitly call this method for such cases and that this method
* be prepared to return the suitability of paste operations as well.
* <p>
* Note: The <code>TransferSupport</code> object passed to this method
* is only valid for the duration of the method call. It is undefined
* what values it may contain after this method returns.
*
* @param support the object containing the details of
* the transfer, not <code>null</code>.
* @return <code>true</code> if the import can happen,
* <code>false</code> otherwise
* @throws NullPointerException if <code>support</code> is {@code null}
* @see #importData(TransferHandler.TransferSupport)
* @see javax.swing.TransferHandler.TransferSupport#setShowDropLocation
* @see javax.swing.TransferHandler.TransferSupport#setDropAction
* @since 1.6
*/
public boolean canImport(TransferSupport support) {
return support.getComponent() instanceof JComponent
? canImport((JComponent)support.getComponent(), support.getDataFlavors())
: false;
}
Indicates whether a component will accept an import of the given
set of data flavors prior to actually attempting to import it.
Note: Swing now calls the newer version of canImport
that takes a TransferSupport
, which in turn calls this method (only if the component in the TransferSupport
is a JComponent
). Developers are encouraged to call and override the newer version as it provides more information (and is the only version that supports use with a TransferHandler
set directly on a JFrame
or other non-JComponent
).
Params: - comp – the component to receive the transfer;
provided to enable sharing of
TransferHandler
s - transferFlavors – the data formats available
See Also: Returns: true if the data can be inserted into the component, false otherwise
/**
* Indicates whether a component will accept an import of the given
* set of data flavors prior to actually attempting to import it.
* <p>
* Note: Swing now calls the newer version of <code>canImport</code>
* that takes a <code>TransferSupport</code>, which in turn calls this
* method (only if the component in the {@code TransferSupport} is a
* {@code JComponent}). Developers are encouraged to call and override the
* newer version as it provides more information (and is the only
* version that supports use with a {@code TransferHandler} set directly
* on a {@code JFrame} or other non-{@code JComponent}).
*
* @param comp the component to receive the transfer;
* provided to enable sharing of <code>TransferHandler</code>s
* @param transferFlavors the data formats available
* @return true if the data can be inserted into the component, false otherwise
* @see #canImport(TransferHandler.TransferSupport)
*/
public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
PropertyDescriptor prop = getPropertyDescriptor(comp);
if (prop != null) {
Method writer = prop.getWriteMethod();
if (writer == null) {
// read-only property. ignore
return false;
}
Class<?>[] params = writer.getParameterTypes();
if (params.length != 1) {
// zero or more than one argument, ignore
return false;
}
DataFlavor flavor = getPropertyDataFlavor(params[0], transferFlavors);
if (flavor != null) {
return true;
}
}
return false;
}
Returns the type of transfer actions supported by the source; any bitwise-OR combination of COPY
, MOVE
and LINK
. Some models are not mutable, so a transfer operation of MOVE
should not be advertised in that case. Returning NONE
disables transfers from the component.
Params: - c – the component holding the data to be transferred;
provided to enable sharing of
TransferHandler
s
Returns: COPY
if the transfer property can be found, otherwise returns NONE
/**
* Returns the type of transfer actions supported by the source;
* any bitwise-OR combination of {@code COPY}, {@code MOVE}
* and {@code LINK}.
* <p>
* Some models are not mutable, so a transfer operation of {@code MOVE}
* should not be advertised in that case. Returning {@code NONE}
* disables transfers from the component.
*
* @param c the component holding the data to be transferred;
* provided to enable sharing of <code>TransferHandler</code>s
* @return {@code COPY} if the transfer property can be found,
* otherwise returns <code>NONE</code>
*/
public int getSourceActions(JComponent c) {
PropertyDescriptor prop = getPropertyDescriptor(c);
if (prop != null) {
return COPY;
}
return NONE;
}
Returns an object that establishes the look of a transfer. This is
useful for both providing feedback while performing a drag operation and for
representing the transfer in a clipboard implementation that has a visual
appearance. The implementation of the Icon
interface should
not alter the graphics clip or alpha level.
The icon implementation need not be rectangular or paint all of the
bounding rectangle and logic that calls the icons paint method should
not assume the all bits are painted. null
is a valid return value
for this method and indicates there is no visual representation provided.
In that case, the calling logic is free to represent the
transferable however it wants.
The default Swing logic will not do an alpha blended drag animation if
the return is null
.
Params: - t – the data to be transferred; this value is expected to have been
created by the
createTransferable
method
Returns: null
, indicating
there is no default visual representation
/**
* Returns an object that establishes the look of a transfer. This is
* useful for both providing feedback while performing a drag operation and for
* representing the transfer in a clipboard implementation that has a visual
* appearance. The implementation of the <code>Icon</code> interface should
* not alter the graphics clip or alpha level.
* The icon implementation need not be rectangular or paint all of the
* bounding rectangle and logic that calls the icons paint method should
* not assume the all bits are painted. <code>null</code> is a valid return value
* for this method and indicates there is no visual representation provided.
* In that case, the calling logic is free to represent the
* transferable however it wants.
* <p>
* The default Swing logic will not do an alpha blended drag animation if
* the return is <code>null</code>.
*
* @param t the data to be transferred; this value is expected to have been
* created by the <code>createTransferable</code> method
* @return <code>null</code>, indicating
* there is no default visual representation
*/
public Icon getVisualRepresentation(Transferable t) {
return null;
}
Creates a Transferable
to use as the source for
a data transfer. Returns the representation of the data to
be transferred, or null
if the component's
property is null
Params: - c – the component holding the data to be transferred;
provided to enable sharing of
TransferHandler
s
Returns: the representation of the data to be transferred, or
null
if the property associated with c
is null
/**
* Creates a <code>Transferable</code> to use as the source for
* a data transfer. Returns the representation of the data to
* be transferred, or <code>null</code> if the component's
* property is <code>null</code>
*
* @param c the component holding the data to be transferred;
* provided to enable sharing of <code>TransferHandler</code>s
* @return the representation of the data to be transferred, or
* <code>null</code> if the property associated with <code>c</code>
* is <code>null</code>
*
*/
protected Transferable createTransferable(JComponent c) {
PropertyDescriptor property = getPropertyDescriptor(c);
if (property != null) {
return new PropertyTransferable(property, c);
}
return null;
}
Invoked after data has been exported. This method should remove
the data that was transferred if the action was MOVE
.
This method is implemented to do nothing since MOVE
is not a supported action of this implementation
(getSourceActions
does not include MOVE
).
Params: - source – the component that was the source of the data
- data – The data that was transferred or possibly null
if the action is
NONE
. - action – the actual action that was performed
/**
* Invoked after data has been exported. This method should remove
* the data that was transferred if the action was <code>MOVE</code>.
* <p>
* This method is implemented to do nothing since <code>MOVE</code>
* is not a supported action of this implementation
* (<code>getSourceActions</code> does not include <code>MOVE</code>).
*
* @param source the component that was the source of the data
* @param data The data that was transferred or possibly null
* if the action is <code>NONE</code>.
* @param action the actual action that was performed
*/
protected void exportDone(JComponent source, Transferable data, int action) {
}
Fetches the property descriptor for the property assigned to this transfer
handler on the given component (transfer handler may be shared). This
returns null
if the property descriptor can't be found
or there is an error attempting to fetch the property descriptor.
/**
* Fetches the property descriptor for the property assigned to this transfer
* handler on the given component (transfer handler may be shared). This
* returns <code>null</code> if the property descriptor can't be found
* or there is an error attempting to fetch the property descriptor.
*/
private PropertyDescriptor getPropertyDescriptor(JComponent comp) {
if (propertyName == null) {
return null;
}
Class<?> k = comp.getClass();
BeanInfo bi;
try {
bi = Introspector.getBeanInfo(k);
} catch (IntrospectionException ex) {
return null;
}
PropertyDescriptor[] props = bi.getPropertyDescriptors();
for (int i=0; i < props.length; i++) {
if (propertyName.equals(props[i].getName())) {
Method reader = props[i].getReadMethod();
if (reader != null) {
Class<?>[] params = reader.getParameterTypes();
if (params == null || params.length == 0) {
// found the desired descriptor
return props[i];
}
}
}
}
return null;
}
Fetches the data flavor from the array of possible flavors that
has data of the type represented by property type. Null is
returned if there is no match.
/**
* Fetches the data flavor from the array of possible flavors that
* has data of the type represented by property type. Null is
* returned if there is no match.
*/
private DataFlavor getPropertyDataFlavor(Class<?> k, DataFlavor[] flavors) {
for(int i = 0; i < flavors.length; i++) {
DataFlavor flavor = flavors[i];
if ("application".equals(flavor.getPrimaryType()) &&
"x-java-jvm-local-objectref".equals(flavor.getSubType()) &&
k.isAssignableFrom(flavor.getRepresentationClass())) {
return flavor;
}
}
return null;
}
private String propertyName;
private static SwingDragGestureRecognizer recognizer = null;
private static DropTargetListener getDropTargetListener() {
synchronized(DropHandler.class) {
DropHandler handler =
(DropHandler)AppContext.getAppContext().get(DropHandler.class);
if (handler == null) {
handler = new DropHandler();
AppContext.getAppContext().put(DropHandler.class, handler);
}
return handler;
}
}
static class PropertyTransferable implements Transferable {
PropertyTransferable(PropertyDescriptor p, JComponent c) {
property = p;
component = c;
}
// --- Transferable methods ----------------------------------------------
Returns an array of DataFlavor
objects indicating the flavors the data
can be provided in. The array should be ordered according to preference
for providing the data (from most richly descriptive to least descriptive).
Returns: an array of data flavors in which this data can be transferred
/**
* Returns an array of <code>DataFlavor</code> objects indicating the flavors the data
* can be provided in. The array should be ordered according to preference
* for providing the data (from most richly descriptive to least descriptive).
* @return an array of data flavors in which this data can be transferred
*/
public DataFlavor[] getTransferDataFlavors() {
DataFlavor[] flavors = new DataFlavor[1];
Class<?> propertyType = property.getPropertyType();
String mimeType = DataFlavor.javaJVMLocalObjectMimeType + ";class=" + propertyType.getName();
try {
flavors[0] = new DataFlavor(mimeType);
} catch (ClassNotFoundException cnfe) {
flavors = new DataFlavor[0];
}
return flavors;
}
Returns whether the specified data flavor is supported for
this object.
Params: - flavor – the requested flavor for the data
Returns: true if this DataFlavor
is supported,
otherwise false
/**
* Returns whether the specified data flavor is supported for
* this object.
* @param flavor the requested flavor for the data
* @return true if this <code>DataFlavor</code> is supported,
* otherwise false
*/
public boolean isDataFlavorSupported(DataFlavor flavor) {
Class<?> propertyType = property.getPropertyType();
if ("application".equals(flavor.getPrimaryType()) &&
"x-java-jvm-local-objectref".equals(flavor.getSubType()) &&
flavor.getRepresentationClass().isAssignableFrom(propertyType)) {
return true;
}
return false;
}
Returns an object which represents the data to be transferred. The class
of the object returned is defined by the representation class of the flavor.
Params: - flavor – the requested flavor for the data
Throws: - IOException – if the data is no longer available
in the requested flavor.
- UnsupportedFlavorException – if the requested data flavor is
not supported.
See Also:
/**
* Returns an object which represents the data to be transferred. The class
* of the object returned is defined by the representation class of the flavor.
*
* @param flavor the requested flavor for the data
* @see DataFlavor#getRepresentationClass
* @exception IOException if the data is no longer available
* in the requested flavor.
* @exception UnsupportedFlavorException if the requested data flavor is
* not supported.
*/
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (! isDataFlavorSupported(flavor)) {
throw new UnsupportedFlavorException(flavor);
}
Method reader = property.getReadMethod();
Object value = null;
try {
value = MethodUtil.invoke(reader, component, (Object[])null);
} catch (Exception ex) {
throw new IOException("Property read failed: " + property.getName());
}
return value;
}
JComponent component;
PropertyDescriptor property;
}
This is the default drop target for drag and drop operations if
one isn't provided by the developer. DropTarget
only supports one DropTargetListener
and doesn't
function properly if it isn't set.
This class sets the one listener as the linkage of drop handling
to the TransferHandler
, and adds support for
additional listeners which some of the ComponentUI
implementations install to manipulate a drop insertion location.
/**
* This is the default drop target for drag and drop operations if
* one isn't provided by the developer. <code>DropTarget</code>
* only supports one <code>DropTargetListener</code> and doesn't
* function properly if it isn't set.
* This class sets the one listener as the linkage of drop handling
* to the <code>TransferHandler</code>, and adds support for
* additional listeners which some of the <code>ComponentUI</code>
* implementations install to manipulate a drop insertion location.
*/
static class SwingDropTarget extends DropTarget implements UIResource {
SwingDropTarget(Component c) {
super(c, COPY_OR_MOVE | LINK, null);
try {
// addDropTargetListener is overridden
// we specifically need to add to the superclass
super.addDropTargetListener(getDropTargetListener());
} catch (TooManyListenersException tmle) {}
}
public void addDropTargetListener(DropTargetListener dtl) throws TooManyListenersException {
// Since the super class only supports one DropTargetListener,
// and we add one from the constructor, we always add to the
// extended list.
if (listenerList == null) {
listenerList = new EventListenerList();
}
listenerList.add(DropTargetListener.class, dtl);
}
public void removeDropTargetListener(DropTargetListener dtl) {
if (listenerList != null) {
listenerList.remove(DropTargetListener.class, dtl);
}
}
// --- DropTargetListener methods (multicast) --------------------------
public void dragEnter(DropTargetDragEvent e) {
super.dragEnter(e);
if (listenerList != null) {
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==DropTargetListener.class) {
((DropTargetListener)listeners[i+1]).dragEnter(e);
}
}
}
}
public void dragOver(DropTargetDragEvent e) {
super.dragOver(e);
if (listenerList != null) {
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==DropTargetListener.class) {
((DropTargetListener)listeners[i+1]).dragOver(e);
}
}
}
}
public void dragExit(DropTargetEvent e) {
super.dragExit(e);
if (listenerList != null) {
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==DropTargetListener.class) {
((DropTargetListener)listeners[i+1]).dragExit(e);
}
}
}
if (!isActive()) {
// If the Drop target is inactive the dragExit will not be dispatched to the dtListener,
// so make sure that we clean up the dtListener anyway.
DropTargetListener dtListener = getDropTargetListener();
if (dtListener != null && dtListener instanceof DropHandler) {
((DropHandler)dtListener).cleanup(false);
}
}
}
public void drop(DropTargetDropEvent e) {
super.drop(e);
if (listenerList != null) {
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==DropTargetListener.class) {
((DropTargetListener)listeners[i+1]).drop(e);
}
}
}
}
public void dropActionChanged(DropTargetDragEvent e) {
super.dropActionChanged(e);
if (listenerList != null) {
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==DropTargetListener.class) {
((DropTargetListener)listeners[i+1]).dropActionChanged(e);
}
}
}
}
private EventListenerList listenerList;
}
private static class DropHandler implements DropTargetListener,
Serializable,
ActionListener {
private Timer timer;
private Point lastPosition;
private Rectangle outer = new Rectangle();
private Rectangle inner = new Rectangle();
private int hysteresis = 10;
private Component component;
private Object state;
private TransferSupport support =
new TransferSupport(null, (DropTargetEvent)null);
private static final int AUTOSCROLL_INSET = 10;
Update the geometry of the autoscroll region. The geometry is
maintained as a pair of rectangles. The region can cause
a scroll if the pointer sits inside it for the duration of the
timer. The region that causes the timer countdown is the area
between the two rectangles.
This is implemented to use the visible area of the component
as the outer rectangle, and the insets are fixed at 10. Should
the component be smaller than a total of 20 in any direction,
autoscroll will not occur in that direction.
/**
* Update the geometry of the autoscroll region. The geometry is
* maintained as a pair of rectangles. The region can cause
* a scroll if the pointer sits inside it for the duration of the
* timer. The region that causes the timer countdown is the area
* between the two rectangles.
* <p>
* This is implemented to use the visible area of the component
* as the outer rectangle, and the insets are fixed at 10. Should
* the component be smaller than a total of 20 in any direction,
* autoscroll will not occur in that direction.
*/
private void updateAutoscrollRegion(JComponent c) {
// compute the outer
Rectangle visible = c.getVisibleRect();
outer.setBounds(visible.x, visible.y, visible.width, visible.height);
// compute the insets
Insets i = new Insets(0, 0, 0, 0);
if (c instanceof Scrollable) {
int minSize = 2 * AUTOSCROLL_INSET;
if (visible.width >= minSize) {
i.left = i.right = AUTOSCROLL_INSET;
}
if (visible.height >= minSize) {
i.top = i.bottom = AUTOSCROLL_INSET;
}
}
// set the inner from the insets
inner.setBounds(visible.x + i.left,
visible.y + i.top,
visible.width - (i.left + i.right),
visible.height - (i.top + i.bottom));
}
Perform an autoscroll operation. This is implemented to scroll by the
unit increment of the Scrollable using scrollRectToVisible. If the
cursor is in a corner of the autoscroll region, more than one axis will
scroll.
/**
* Perform an autoscroll operation. This is implemented to scroll by the
* unit increment of the Scrollable using scrollRectToVisible. If the
* cursor is in a corner of the autoscroll region, more than one axis will
* scroll.
*/
private void autoscroll(JComponent c, Point pos) {
if (c instanceof Scrollable) {
Scrollable s = (Scrollable) c;
if (pos.y < inner.y) {
// scroll upward
int dy = s.getScrollableUnitIncrement(outer, SwingConstants.VERTICAL, -1);
Rectangle r = new Rectangle(inner.x, outer.y - dy, inner.width, dy);
c.scrollRectToVisible(r);
} else if (pos.y > (inner.y + inner.height)) {
// scroll downard
int dy = s.getScrollableUnitIncrement(outer, SwingConstants.VERTICAL, 1);
Rectangle r = new Rectangle(inner.x, outer.y + outer.height, inner.width, dy);
c.scrollRectToVisible(r);
}
if (pos.x < inner.x) {
// scroll left
int dx = s.getScrollableUnitIncrement(outer, SwingConstants.HORIZONTAL, -1);
Rectangle r = new Rectangle(outer.x - dx, inner.y, dx, inner.height);
c.scrollRectToVisible(r);
} else if (pos.x > (inner.x + inner.width)) {
// scroll right
int dx = s.getScrollableUnitIncrement(outer, SwingConstants.HORIZONTAL, 1);
Rectangle r = new Rectangle(outer.x + outer.width, inner.y, dx, inner.height);
c.scrollRectToVisible(r);
}
}
}
Initializes the internal properties if they haven't been already
inited. This is done lazily to avoid loading of desktop properties.
/**
* Initializes the internal properties if they haven't been already
* inited. This is done lazily to avoid loading of desktop properties.
*/
private void initPropertiesIfNecessary() {
if (timer == null) {
Toolkit t = Toolkit.getDefaultToolkit();
Integer prop;
prop = (Integer)
t.getDesktopProperty("DnD.Autoscroll.interval");
timer = new Timer(prop == null ? 100 : prop.intValue(), this);
prop = (Integer)
t.getDesktopProperty("DnD.Autoscroll.initialDelay");
timer.setInitialDelay(prop == null ? 100 : prop.intValue());
prop = (Integer)
t.getDesktopProperty("DnD.Autoscroll.cursorHysteresis");
if (prop != null) {
hysteresis = prop.intValue();
}
}
}
The timer fired, perform autoscroll if the pointer is within the
autoscroll region.
Params: - e – the
ActionEvent
/**
* The timer fired, perform autoscroll if the pointer is within the
* autoscroll region.
* <P>
* @param e the <code>ActionEvent</code>
*/
public void actionPerformed(ActionEvent e) {
updateAutoscrollRegion((JComponent)component);
if (outer.contains(lastPosition) && !inner.contains(lastPosition)) {
autoscroll((JComponent)component, lastPosition);
}
}
// --- DropTargetListener methods -----------------------------------
private void setComponentDropLocation(TransferSupport support,
boolean forDrop) {
DropLocation dropLocation = (support == null)
? null
: support.getDropLocation();
if (SunToolkit.isInstanceOf(component, "javax.swing.text.JTextComponent")) {
state = SwingAccessor.getJTextComponentAccessor().
setDropLocation((JTextComponent)component, dropLocation, state, forDrop);
} else if (component instanceof JComponent) {
state = ((JComponent)component).setDropLocation(dropLocation, state, forDrop);
}
}
private void handleDrag(DropTargetDragEvent e) {
TransferHandler importer =
((HasGetTransferHandler)component).getTransferHandler();
if (importer == null) {
e.rejectDrag();
setComponentDropLocation(null, false);
return;
}
support.setDNDVariables(component, e);
boolean canImport = importer.canImport(support);
if (canImport) {
e.acceptDrag(support.getDropAction());
} else {
e.rejectDrag();
}
boolean showLocation = support.showDropLocationIsSet ?
support.showDropLocation :
canImport;
setComponentDropLocation(showLocation ? support : null, false);
}
public void dragEnter(DropTargetDragEvent e) {
state = null;
component = e.getDropTargetContext().getComponent();
handleDrag(e);
if (component instanceof JComponent) {
lastPosition = e.getLocation();
updateAutoscrollRegion((JComponent)component);
initPropertiesIfNecessary();
}
}
public void dragOver(DropTargetDragEvent e) {
handleDrag(e);
if (!(component instanceof JComponent)) {
return;
}
Point p = e.getLocation();
if (Math.abs(p.x - lastPosition.x) > hysteresis
|| Math.abs(p.y - lastPosition.y) > hysteresis) {
// no autoscroll
if (timer.isRunning()) timer.stop();
} else {
if (!timer.isRunning()) timer.start();
}
lastPosition = p;
}
public void dragExit(DropTargetEvent e) {
cleanup(false);
}
public void drop(DropTargetDropEvent e) {
TransferHandler importer =
((HasGetTransferHandler)component).getTransferHandler();
if (importer == null) {
e.rejectDrop();
cleanup(false);
return;
}
support.setDNDVariables(component, e);
boolean canImport = importer.canImport(support);
if (canImport) {
e.acceptDrop(support.getDropAction());
boolean showLocation = support.showDropLocationIsSet ?
support.showDropLocation :
canImport;
setComponentDropLocation(showLocation ? support : null, false);
boolean success;
try {
success = importer.importData(support);
} catch (RuntimeException re) {
success = false;
}
e.dropComplete(success);
cleanup(success);
} else {
e.rejectDrop();
cleanup(false);
}
}
public void dropActionChanged(DropTargetDragEvent e) {
/*
* Work-around for Linux bug where dropActionChanged
* is called before dragEnter.
*/
if (component == null) {
return;
}
handleDrag(e);
}
private void cleanup(boolean forDrop) {
setComponentDropLocation(null, forDrop);
if (component instanceof JComponent) {
((JComponent)component).dndDone();
}
if (timer != null) {
timer.stop();
}
state = null;
component = null;
lastPosition = null;
}
}
This is the default drag handler for drag and drop operations that
use the TransferHandler
.
/**
* This is the default drag handler for drag and drop operations that
* use the <code>TransferHandler</code>.
*/
private static class DragHandler implements DragGestureListener, DragSourceListener {
private boolean scrolls;
// --- DragGestureListener methods -----------------------------------
a Drag gesture has been recognized
/**
* a Drag gesture has been recognized
*/
public void dragGestureRecognized(DragGestureEvent dge) {
JComponent c = (JComponent) dge.getComponent();
TransferHandler th = c.getTransferHandler();
Transferable t = th.createTransferable(c);
if (t != null) {
scrolls = c.getAutoscrolls();
c.setAutoscrolls(false);
try {
Image im = th.getDragImage();
if (im == null) {
dge.startDrag(null, t, this);
} else {
dge.startDrag(null, im, th.getDragImageOffset(), t, this);
}
return;
} catch (RuntimeException re) {
c.setAutoscrolls(scrolls);
}
}
th.exportDone(c, t, NONE);
}
// --- DragSourceListener methods -----------------------------------
as the hotspot enters a platform dependent drop site
/**
* as the hotspot enters a platform dependent drop site
*/
public void dragEnter(DragSourceDragEvent dsde) {
}
as the hotspot moves over a platform dependent drop site
/**
* as the hotspot moves over a platform dependent drop site
*/
public void dragOver(DragSourceDragEvent dsde) {
}
as the hotspot exits a platform dependent drop site
/**
* as the hotspot exits a platform dependent drop site
*/
public void dragExit(DragSourceEvent dsde) {
}
as the operation completes
/**
* as the operation completes
*/
public void dragDropEnd(DragSourceDropEvent dsde) {
DragSourceContext dsc = dsde.getDragSourceContext();
JComponent c = (JComponent)dsc.getComponent();
if (dsde.getDropSuccess()) {
c.getTransferHandler().exportDone(c, dsc.getTransferable(), dsde.getDropAction());
} else {
c.getTransferHandler().exportDone(c, dsc.getTransferable(), NONE);
}
c.setAutoscrolls(scrolls);
}
public void dropActionChanged(DragSourceDragEvent dsde) {
}
}
private static class SwingDragGestureRecognizer extends DragGestureRecognizer {
SwingDragGestureRecognizer(DragGestureListener dgl) {
super(DragSource.getDefaultDragSource(), null, NONE, dgl);
}
void gestured(JComponent c, MouseEvent e, int srcActions, int action) {
setComponent(c);
setSourceActions(srcActions);
appendEvent(e);
fireDragGestureRecognized(action, e.getPoint());
}
register this DragGestureRecognizer's Listeners with the Component
/**
* register this DragGestureRecognizer's Listeners with the Component
*/
protected void registerListeners() {
}
unregister this DragGestureRecognizer's Listeners with the Component
subclasses must override this method
/**
* unregister this DragGestureRecognizer's Listeners with the Component
*
* subclasses must override this method
*/
protected void unregisterListeners() {
}
}
static final Action cutAction = new TransferAction("cut");
static final Action copyAction = new TransferAction("copy");
static final Action pasteAction = new TransferAction("paste");
static class TransferAction extends UIAction implements UIResource {
TransferAction(String name) {
super(name);
}
@Override
public boolean accept(Object sender) {
return !(sender instanceof JComponent
&& ((JComponent)sender).getTransferHandler() == null);
}
private static final JavaSecurityAccess javaSecurityAccess =
SharedSecrets.getJavaSecurityAccess();
public void actionPerformed(final ActionEvent e) {
final Object src = e.getSource();
final PrivilegedAction<Void> action = new PrivilegedAction<Void>() {
public Void run() {
actionPerformedImpl(e);
return null;
}
};
final AccessControlContext stack = AccessController.getContext();
final AccessControlContext srcAcc = AWTAccessor.getComponentAccessor().getAccessControlContext((Component)src);
final AccessControlContext eventAcc = AWTAccessor.getAWTEventAccessor().getAccessControlContext(e);
if (srcAcc == null) {
javaSecurityAccess.doIntersectionPrivilege(action, stack, eventAcc);
} else {
javaSecurityAccess.doIntersectionPrivilege(
new PrivilegedAction<Void>() {
public Void run() {
javaSecurityAccess.doIntersectionPrivilege(action, eventAcc);
return null;
}
}, stack, srcAcc);
}
}
private void actionPerformedImpl(ActionEvent e) {
Object src = e.getSource();
if (src instanceof JComponent) {
JComponent c = (JComponent) src;
TransferHandler th = c.getTransferHandler();
Clipboard clipboard = getClipboard(c);
String name = (String) getValue(Action.NAME);
Transferable trans = null;
// any of these calls may throw IllegalStateException
try {
if ((clipboard != null) && (th != null) && (name != null)) {
if ("cut".equals(name)) {
th.exportToClipboard(c, clipboard, MOVE);
} else if ("copy".equals(name)) {
th.exportToClipboard(c, clipboard, COPY);
} else if ("paste".equals(name)) {
trans = clipboard.getContents(null);
}
}
} catch (IllegalStateException ise) {
// clipboard was unavailable
UIManager.getLookAndFeel().provideErrorFeedback(c);
return;
}
// this is a paste action, import data into the component
if (trans != null) {
th.importData(new TransferSupport(c, trans));
}
}
}
Returns the clipboard to use for cut/copy/paste.
/**
* Returns the clipboard to use for cut/copy/paste.
*/
private Clipboard getClipboard(JComponent c) {
if (SwingUtilities2.canAccessSystemClipboard()) {
return c.getToolkit().getSystemClipboard();
}
Clipboard clipboard = (Clipboard)sun.awt.AppContext.getAppContext().
get(SandboxClipboardKey);
if (clipboard == null) {
clipboard = new Clipboard("Sandboxed Component Clipboard");
sun.awt.AppContext.getAppContext().put(SandboxClipboardKey,
clipboard);
}
return clipboard;
}
Key used in app context to lookup Clipboard to use if access to
System clipboard is denied.
/**
* Key used in app context to lookup Clipboard to use if access to
* System clipboard is denied.
*/
private static Object SandboxClipboardKey = new Object();
}
}