|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package java.awt.dnd; |
|
|
|
import java.awt.Component; |
|
import java.awt.Cursor; |
|
import java.awt.Image; |
|
import java.awt.Point; |
|
|
|
import java.awt.datatransfer.DataFlavor; |
|
import java.awt.datatransfer.Transferable; |
|
import java.awt.datatransfer.UnsupportedFlavorException; |
|
|
|
import java.awt.dnd.peer.DragSourceContextPeer; |
|
|
|
import java.io.IOException; |
|
import java.io.InvalidObjectException; |
|
import java.io.ObjectOutputStream; |
|
import java.io.ObjectInputStream; |
|
import java.io.Serializable; |
|
|
|
import java.util.TooManyListenersException; |
|
|
|
/** |
|
* The <code>DragSourceContext</code> class is responsible for managing the |
|
* initiator side of the Drag and Drop protocol. In particular, it is responsible |
|
* for managing drag event notifications to the |
|
* {@linkplain DragSourceListener DragSourceListeners} |
|
* and {@linkplain DragSourceMotionListener DragSourceMotionListeners}, and providing the |
|
* {@link Transferable} representing the source data for the drag operation. |
|
* <p> |
|
* Note that the <code>DragSourceContext</code> itself |
|
* implements the <code>DragSourceListener</code> and |
|
* <code>DragSourceMotionListener</code> interfaces. |
|
* This is to allow the platform peer |
|
* (the {@link DragSourceContextPeer} instance) |
|
* created by the {@link DragSource} to notify |
|
* the <code>DragSourceContext</code> of |
|
* state changes in the ongoing operation. This allows the |
|
* <code>DragSourceContext</code> object to interpose |
|
* itself between the platform and the |
|
* listeners provided by the initiator of the drag operation. |
|
* <p> |
|
* <a name="defaultCursor"></a> |
|
* By default, {@code DragSourceContext} sets the cursor as appropriate |
|
* for the current state of the drag and drop operation. For example, if |
|
* the user has chosen {@linkplain DnDConstants#ACTION_MOVE the move action}, |
|
* and the pointer is over a target that accepts |
|
* the move action, the default move cursor is shown. When |
|
* the pointer is over an area that does not accept the transfer, |
|
* the default "no drop" cursor is shown. |
|
* <p> |
|
* This default handling mechanism is disabled when a custom cursor is set |
|
* by the {@link #setCursor} method. When the default handling is disabled, |
|
* it becomes the responsibility |
|
* of the developer to keep the cursor up to date, by listening |
|
* to the {@code DragSource} events and calling the {@code setCursor()} method. |
|
* Alternatively, you can provide custom cursor behavior by providing |
|
* custom implementations of the {@code DragSource} |
|
* and the {@code DragSourceContext} classes. |
|
* |
|
* @see DragSourceListener |
|
* @see DragSourceMotionListener |
|
* @see DnDConstants |
|
* @since 1.2 |
|
*/ |
|
|
|
public class DragSourceContext |
|
implements DragSourceListener, DragSourceMotionListener, Serializable { |
|
|
|
private static final long serialVersionUID = -115407898692194719L; |
|
|
|
// used by updateCurrentCursor |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected static final int DEFAULT = 0; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected static final int ENTER = 1; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected static final int OVER = 2; |
|
|
|
/** |
|
* An <code>int</code> used by updateCurrentCursor() |
|
* indicating that the user operation has changed. |
|
*/ |
|
|
|
protected static final int CHANGED = 3; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public DragSourceContext(DragSourceContextPeer dscp, |
|
DragGestureEvent trigger, Cursor dragCursor, |
|
Image dragImage, Point offset, Transferable t, |
|
DragSourceListener dsl) { |
|
if (dscp == null) { |
|
throw new NullPointerException("DragSourceContextPeer"); |
|
} |
|
|
|
if (trigger == null) { |
|
throw new NullPointerException("Trigger"); |
|
} |
|
|
|
if (trigger.getDragSource() == null) { |
|
throw new IllegalArgumentException("DragSource"); |
|
} |
|
|
|
if (trigger.getComponent() == null) { |
|
throw new IllegalArgumentException("Component"); |
|
} |
|
|
|
if (trigger.getSourceAsDragGestureRecognizer().getSourceActions() == |
|
DnDConstants.ACTION_NONE) { |
|
throw new IllegalArgumentException("source actions"); |
|
} |
|
|
|
if (trigger.getDragAction() == DnDConstants.ACTION_NONE) { |
|
throw new IllegalArgumentException("no drag action"); |
|
} |
|
|
|
if (t == null) { |
|
throw new NullPointerException("Transferable"); |
|
} |
|
|
|
if (dragImage != null && offset == null) { |
|
throw new NullPointerException("offset"); |
|
} |
|
|
|
peer = dscp; |
|
this.trigger = trigger; |
|
cursor = dragCursor; |
|
transferable = t; |
|
listener = dsl; |
|
sourceActions = |
|
trigger.getSourceAsDragGestureRecognizer().getSourceActions(); |
|
|
|
useCustomCursor = (dragCursor != null); |
|
|
|
updateCurrentCursor(trigger.getDragAction(), getSourceActions(), DEFAULT); |
|
} |
|
|
|
/** |
|
* Returns the <code>DragSource</code> |
|
* that instantiated this <code>DragSourceContext</code>. |
|
* |
|
* @return the <code>DragSource</code> that |
|
* instantiated this <code>DragSourceContext</code> |
|
*/ |
|
|
|
public DragSource getDragSource() { return trigger.getDragSource(); } |
|
|
|
/** |
|
* Returns the <code>Component</code> associated with this |
|
* <code>DragSourceContext</code>. |
|
* |
|
* @return the <code>Component</code> that started the drag |
|
*/ |
|
|
|
public Component getComponent() { return trigger.getComponent(); } |
|
|
|
/** |
|
* Returns the <code>DragGestureEvent</code> |
|
* that initially triggered the drag. |
|
* |
|
* @return the Event that triggered the drag |
|
*/ |
|
|
|
public DragGestureEvent getTrigger() { return trigger; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int getSourceActions() { |
|
return sourceActions; |
|
} |
|
|
|
/** |
|
* Sets the cursor for this drag operation to the specified |
|
* <code>Cursor</code>. If the specified <code>Cursor</code> |
|
* is <code>null</code>, the default drag cursor behavior is |
|
* activated for this drag operation, otherwise it is deactivated. |
|
* |
|
* @param c the initial {@code Cursor} for this drag operation, |
|
* or {@code null} for the default cursor handling; |
|
* see {@linkplain Cursor class |
|
* level documentation} for more details |
|
* on the cursor handling during drag and drop |
|
* |
|
*/ |
|
|
|
public synchronized void setCursor(Cursor c) { |
|
useCustomCursor = (c != null); |
|
setCursorImpl(c); |
|
} |
|
|
|
/** |
|
* Returns the current drag <code>Cursor</code>. |
|
* <P> |
|
* @return the current drag <code>Cursor</code> |
|
*/ |
|
|
|
public Cursor getCursor() { return cursor; } |
|
|
|
/** |
|
* Add a <code>DragSourceListener</code> to this |
|
* <code>DragSourceContext</code> if one has not already been added. |
|
* If a <code>DragSourceListener</code> already exists, |
|
* this method throws a <code>TooManyListenersException</code>. |
|
* <P> |
|
* @param dsl the <code>DragSourceListener</code> to add. |
|
* Note that while <code>null</code> is not prohibited, |
|
* it is not acceptable as a parameter. |
|
* <P> |
|
* @throws TooManyListenersException if |
|
* a <code>DragSourceListener</code> has already been added |
|
*/ |
|
|
|
public synchronized void addDragSourceListener(DragSourceListener dsl) throws TooManyListenersException { |
|
if (dsl == null) return; |
|
|
|
if (equals(dsl)) throw new IllegalArgumentException("DragSourceContext may not be its own listener"); |
|
|
|
if (listener != null) |
|
throw new TooManyListenersException(); |
|
else |
|
listener = dsl; |
|
} |
|
|
|
/** |
|
* Removes the specified <code>DragSourceListener</code> |
|
* from this <code>DragSourceContext</code>. |
|
* |
|
* @param dsl the <code>DragSourceListener</code> to remove; |
|
* note that while <code>null</code> is not prohibited, |
|
* it is not acceptable as a parameter |
|
*/ |
|
|
|
public synchronized void removeDragSourceListener(DragSourceListener dsl) { |
|
if (listener != null && listener.equals(dsl)) { |
|
listener = null; |
|
} else |
|
throw new IllegalArgumentException(); |
|
} |
|
|
|
/** |
|
* Notifies the peer that the <code>Transferable</code>'s |
|
* <code>DataFlavor</code>s have changed. |
|
*/ |
|
|
|
public void transferablesFlavorsChanged() { |
|
if (peer != null) peer.transferablesFlavorsChanged(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void dragEnter(DragSourceDragEvent dsde) { |
|
DragSourceListener dsl = listener; |
|
if (dsl != null) { |
|
dsl.dragEnter(dsde); |
|
} |
|
getDragSource().processDragEnter(dsde); |
|
|
|
updateCurrentCursor(getSourceActions(), dsde.getTargetActions(), ENTER); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void dragOver(DragSourceDragEvent dsde) { |
|
DragSourceListener dsl = listener; |
|
if (dsl != null) { |
|
dsl.dragOver(dsde); |
|
} |
|
getDragSource().processDragOver(dsde); |
|
|
|
updateCurrentCursor(getSourceActions(), dsde.getTargetActions(), OVER); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void dragExit(DragSourceEvent dse) { |
|
DragSourceListener dsl = listener; |
|
if (dsl != null) { |
|
dsl.dragExit(dse); |
|
} |
|
getDragSource().processDragExit(dse); |
|
|
|
updateCurrentCursor(DnDConstants.ACTION_NONE, DnDConstants.ACTION_NONE, DEFAULT); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void dropActionChanged(DragSourceDragEvent dsde) { |
|
DragSourceListener dsl = listener; |
|
if (dsl != null) { |
|
dsl.dropActionChanged(dsde); |
|
} |
|
getDragSource().processDropActionChanged(dsde); |
|
|
|
updateCurrentCursor(getSourceActions(), dsde.getTargetActions(), CHANGED); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void dragDropEnd(DragSourceDropEvent dsde) { |
|
DragSourceListener dsl = listener; |
|
if (dsl != null) { |
|
dsl.dragDropEnd(dsde); |
|
} |
|
getDragSource().processDragDropEnd(dsde); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void dragMouseMoved(DragSourceDragEvent dsde) { |
|
getDragSource().processDragMouseMoved(dsde); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Transferable getTransferable() { return transferable; } |
|
|
|
/** |
|
* If the default drag cursor behavior is active, this method |
|
* sets the default drag cursor for the specified actions |
|
* supported by the drag source, the drop target action, |
|
* and status, otherwise this method does nothing. |
|
* |
|
* @param sourceAct the actions supported by the drag source |
|
* @param targetAct the drop target action |
|
* @param status one of the fields <code>DEFAULT</code>, |
|
* <code>ENTER</code>, <code>OVER</code>, |
|
* <code>CHANGED</code> |
|
*/ |
|
|
|
protected synchronized void updateCurrentCursor(int sourceAct, int targetAct, int status) { |
|
|
|
// if the cursor has been previously set then don't do any defaults |
|
// processing. |
|
|
|
if (useCustomCursor) { |
|
return; |
|
} |
|
|
|
// do defaults processing |
|
|
|
Cursor c = null; |
|
|
|
switch (status) { |
|
default: |
|
targetAct = DnDConstants.ACTION_NONE; |
|
case ENTER: |
|
case OVER: |
|
case CHANGED: |
|
int ra = sourceAct & targetAct; |
|
|
|
if (ra == DnDConstants.ACTION_NONE) { |
|
if ((sourceAct & DnDConstants.ACTION_LINK) == DnDConstants.ACTION_LINK) |
|
c = DragSource.DefaultLinkNoDrop; |
|
else if ((sourceAct & DnDConstants.ACTION_MOVE) == DnDConstants.ACTION_MOVE) |
|
c = DragSource.DefaultMoveNoDrop; |
|
else |
|
c = DragSource.DefaultCopyNoDrop; |
|
} else { |
|
if ((ra & DnDConstants.ACTION_LINK) == DnDConstants.ACTION_LINK) |
|
c = DragSource.DefaultLinkDrop; |
|
else if ((ra & DnDConstants.ACTION_MOVE) == DnDConstants.ACTION_MOVE) |
|
c = DragSource.DefaultMoveDrop; |
|
else |
|
c = DragSource.DefaultCopyDrop; |
|
} |
|
} |
|
|
|
setCursorImpl(c); |
|
} |
|
|
|
private void setCursorImpl(Cursor c) { |
|
if (cursor == null || !cursor.equals(c)) { |
|
cursor = c; |
|
if (peer != null) peer.setCursor(cursor); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void writeObject(ObjectOutputStream s) throws IOException { |
|
s.defaultWriteObject(); |
|
|
|
s.writeObject(SerializationTester.test(transferable) |
|
? transferable : null); |
|
s.writeObject(SerializationTester.test(listener) |
|
? listener : null); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void readObject(ObjectInputStream s) |
|
throws ClassNotFoundException, IOException |
|
{ |
|
ObjectInputStream.GetField f = s.readFields(); |
|
|
|
DragGestureEvent newTrigger = (DragGestureEvent)f.get("trigger", null); |
|
if (newTrigger == null) { |
|
throw new InvalidObjectException("Null trigger"); |
|
} |
|
if (newTrigger.getDragSource() == null) { |
|
throw new InvalidObjectException("Null DragSource"); |
|
} |
|
if (newTrigger.getComponent() == null) { |
|
throw new InvalidObjectException("Null trigger component"); |
|
} |
|
|
|
int newSourceActions = f.get("sourceActions", 0) |
|
& (DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK); |
|
if (newSourceActions == DnDConstants.ACTION_NONE) { |
|
throw new InvalidObjectException("Invalid source actions"); |
|
} |
|
int triggerActions = newTrigger.getDragAction(); |
|
if (triggerActions != DnDConstants.ACTION_COPY && |
|
triggerActions != DnDConstants.ACTION_MOVE && |
|
triggerActions != DnDConstants.ACTION_LINK) { |
|
throw new InvalidObjectException("No drag action"); |
|
} |
|
trigger = newTrigger; |
|
|
|
cursor = (Cursor)f.get("cursor", null); |
|
useCustomCursor = f.get("useCustomCursor", false); |
|
sourceActions = newSourceActions; |
|
|
|
transferable = (Transferable)s.readObject(); |
|
listener = (DragSourceListener)s.readObject(); |
|
|
|
|
|
if (transferable == null) { |
|
if (emptyTransferable == null) { |
|
emptyTransferable = new Transferable() { |
|
public DataFlavor[] getTransferDataFlavors() { |
|
return new DataFlavor[0]; |
|
} |
|
public boolean isDataFlavorSupported(DataFlavor flavor) |
|
{ |
|
return false; |
|
} |
|
public Object getTransferData(DataFlavor flavor) |
|
throws UnsupportedFlavorException |
|
{ |
|
throw new UnsupportedFlavorException(flavor); |
|
} |
|
}; |
|
} |
|
transferable = emptyTransferable; |
|
} |
|
} |
|
|
|
private static Transferable emptyTransferable; |
|
|
|
/* |
|
* fields |
|
*/ |
|
|
|
private transient DragSourceContextPeer peer; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private DragGestureEvent trigger; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Cursor cursor; |
|
|
|
private transient Transferable transferable; |
|
|
|
private transient DragSourceListener listener; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean useCustomCursor; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int sourceActions; |
|
} |