/*
* @(#)DragSourceContext.java 1.51 03/12/19
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.awt.dnd;
import java.awt.event.InputEvent;
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.DragGestureEvent;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.InvalidDnDOperationException;
import java.awt.dnd.peer.DragSourceContextPeer;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.TooManyListenersException;
/**
* The DragSourceContext
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 DragSourceListener
s
* and DragSourceMotionListener
s, and providing the
* Transferable
representing the source data for the drag operation.
*
* Note that the DragSourceContext
itself
* implements the DragSourceListener
and
* DragSourceMotionListener
interfaces.
* This is to allow the platform peer
* (the DragSourceContextPeer
instance)
* created by the DragSource
to notify
* the DragSourceContext
of
* state changes in the ongoing operation. This allows the
* DragSourceContext
to interpose
* itself between the platform and the
* listeners provided by the initiator of the drag operation.
*
* @see DragSourceListener
* @see DragSourceMotionListener
* @version 1.51, 12/19/03
* @since 1.2
*/
public class DragSourceContext
implements DragSourceListener, DragSourceMotionListener, Serializable {
private static final long serialVersionUID = -115407898692194719L;
// used by updateCurrentCursor
/**
* An int
used by updateCurrentCursor()
* indicating that the Cursor
should change
* to the default (no drop) Cursor
.
*/
protected static final int DEFAULT = 0;
/**
* An int
used by updateCurrentCursor()
* indicating that the Cursor
* has entered a DropTarget
.
*/
protected static final int ENTER = 1;
/**
* An int
used by updateCurrentCursor()
* indicating that the Cursor
is
* over a DropTarget
.
*/
protected static final int OVER = 2;
/**
* An int
used by updateCurrentCursor()
* indicating that the user operation has changed.
*/
protected static final int CHANGED = 3;
/**
* Called from DragSource
, this constructor creates a new
* DragSourceContext
given the
* DragSourceContextPeer
for this Drag, the
* DragGestureEvent
that triggered the Drag, the initial
* Cursor
to use for the Drag, an (optional)
* Image
to display while the Drag is taking place, the offset
* of the Image
origin from the hotspot at the instant of the
* triggering event, the Transferable
subject data, and the
* DragSourceListener
to use during the Drag and Drop
* operation.
*
* If DragSourceContextPeer
is null
* NullPointerException
is thrown.
*
* If DragGestureEvent
is null
* NullPointerException
is thrown.
*
* If Cursor
is null
no exception is thrown and
* the default drag cursor behavior is activated for this drag operation.
*
* If Image
is null
no exception is thrown.
*
* If Image
is not null
and the offset is
* null
NullPointerException
is thrown.
*
* If Transferable
is null
* NullPointerException
is thrown.
*
* If DragSourceListener
is null
no exception
* is thrown.
*
* @param dscp the DragSourceContextPeer
for this drag
* @param trigger the triggering event
* @param dragCursor the initial Cursor
* @param dragImage the Image
to drag (or null
)
* @param offset the offset of the image origin from the hotspot at the
* instant of the triggering event
* @param t the Transferable
* @param dsl the DragSourceListener
*
* @throws IllegalArgumentException if the Component
associated
* with the trigger event is null
.
* @throws IllegalArgumentException if the DragSource
for the
* trigger event is null
.
* @throws IllegalArgumentException if the drag action for the
* trigger event is DnDConstants.ACTION_NONE
.
* @throws IllegalArgumentException if the source actions for the
* DragGestureRecognizer
associated with the trigger
* event are equal to DnDConstants.ACTION_NONE
.
* @throws NullPointerException if dscp, trigger, or t are null, or
* if dragImage is non-null and offset is null
*/
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 DragSource
* that instantiated this DragSourceContext
.
*
* @return the DragSource
that
* instantiated this DragSourceContext
*/
public DragSource getDragSource() { return trigger.getDragSource(); }
/**
* Returns the Component
associated with this
* DragSourceContext
.
*
* @return the Component
that started the drag
*/
public Component getComponent() { return trigger.getComponent(); }
/**
* Returns the DragGestureEvent
* that initially triggered the drag.
*
* @return the Event that triggered the drag
*/
public DragGestureEvent getTrigger() { return trigger; }
/**
* Returns a bitwise mask of DnDConstants
that
* represent the set of drop actions supported by the drag source for the
* drag operation associated with this DragSourceContext
.
*
* @return the drop actions supported by the drag source
*/
public int getSourceActions() {
return sourceActions;
}
/**
* Sets the cursor for this drag operation to the specified
* Cursor
. If the specified Cursor
* is null
, the default drag cursor behavior is
* activated for this drag operation, otherwise it is deactivated.
*
* @param c the Cursor
to display, or
* null
to activate the default drag cursor
* behavior
*
*/
public synchronized void setCursor(Cursor c) {
useCustomCursor = (c != null);
setCursorImpl(c);
}
/**
* Returns the current drag Cursor
.
*
* @return the current drag Cursor
*/
public Cursor getCursor() { return cursor; }
/**
* Add a DragSourceListener
to this
* DragSourceContext
if one has not already been added.
* If a DragSourceListener
already exists,
* this method throws a TooManyListenersException
.
*
* @param dsl the DragSourceListener
to add.
* Note that while null
is not prohibited,
* it is not acceptable as a parameter.
*
* @throws TooManyListenersException
if
* a DragSourceListener
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 DragSourceListener
* from this DragSourceContext
.
*
* @param dsl the DragSourceListener
to remove;
* note that while null
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 Transferable
's
* DataFlavor
s have changed.
*/
public void transferablesFlavorsChanged() {
if (peer != null) peer.transferablesFlavorsChanged();
}
/**
* Calls dragEnter
on the
* DragSourceListener
s registered with this
* DragSourceContext
and with the associated
* DragSource
, and passes them the specified
* DragSourceDragEvent
.
*
* @param dsde the DragSourceDragEvent
*/
public void dragEnter(DragSourceDragEvent dsde) {
DragSourceListener dsl = listener;
if (dsl != null) {
dsl.dragEnter(dsde);
}
getDragSource().processDragEnter(dsde);
updateCurrentCursor(dsde.getDropAction(), dsde.getTargetActions(), ENTER);
}
/**
* Calls dragOver
on the
* DragSourceListener
s registered with this
* DragSourceContext
and with the associated
* DragSource
, and passes them the specified
* DragSourceDragEvent
.
*
* @param dsde the DragSourceDragEvent
*/
public void dragOver(DragSourceDragEvent dsde) {
DragSourceListener dsl = listener;
if (dsl != null) {
dsl.dragOver(dsde);
}
getDragSource().processDragOver(dsde);
updateCurrentCursor(dsde.getDropAction(), dsde.getTargetActions(), OVER);
}
/**
* Calls dragExit
on the
* DragSourceListener
s registered with this
* DragSourceContext
and with the associated
* DragSource
, and passes them the specified
* DragSourceEvent
.
*
* @param dse the DragSourceEvent
*/
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);
}
/**
* Calls dropActionChanged
on the
* DragSourceListener
s registered with this
* DragSourceContext
and with the associated
* DragSource
, and passes them the specified
* DragSourceDragEvent
.
*
* @param dsde the DragSourceDragEvent
*/
public void dropActionChanged(DragSourceDragEvent dsde) {
DragSourceListener dsl = listener;
if (dsl != null) {
dsl.dropActionChanged(dsde);
}
getDragSource().processDropActionChanged(dsde);
updateCurrentCursor(dsde.getDropAction(), dsde.getTargetActions(), CHANGED);
}
/**
* Calls dragDropEnd
on the
* DragSourceListener
s registered with this
* DragSourceContext
and with the associated
* DragSource
, and passes them the specified
* DragSourceDropEvent
.
*
* @param dsde the DragSourceDropEvent
*/
public void dragDropEnd(DragSourceDropEvent dsde) {
DragSourceListener dsl = listener;
if (dsl != null) {
dsl.dragDropEnd(dsde);
}
getDragSource().processDragDropEnd(dsde);
}
/**
* Calls dragMouseMoved
on the
* DragSourceMotionListener
s registered with the
* DragSource
associated with this
* DragSourceContext
, and them passes the specified
* DragSourceDragEvent
.
*
* @param dsde the DragSourceDragEvent
* @since 1.4
*/
public void dragMouseMoved(DragSourceDragEvent dsde) {
getDragSource().processDragMouseMoved(dsde);
}
/**
* Returns the Transferable
associated with
* this DragSourceContext
.
*
* @return the Transferable
*/
public Transferable getTransferable() { return transferable; }
/**
* If the default drag cursor behavior is active, this method
* sets the default drag cursor for the specified selected
* operation, supported actions and status, otherwise this
* method does nothing.
*
* @param dropOp the user's currently selected operation
* @param targetAct the current target's supported actions
* @param status the constant
*/
protected synchronized void updateCurrentCursor(int dropOp, int targetAct, int status) {
// if the cursor has been previously set then dont 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 = dropOp & targetAct;
if (ra == DnDConstants.ACTION_NONE) { // no drop possible
if ((dropOp & DnDConstants.ACTION_LINK) == DnDConstants.ACTION_LINK)
c = DragSource.DefaultLinkNoDrop;
else if ((dropOp & DnDConstants.ACTION_MOVE) == DnDConstants.ACTION_MOVE)
c = DragSource.DefaultMoveNoDrop;
else
c = DragSource.DefaultCopyNoDrop;
} else { // drop possible
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);
}
}
/**
* Serializes this DragSourceContext
. This method first
* performs default serialization. Next, this object's
* Transferable
is written out if and only if it can be
* serialized. If not, null
is written instead. In this case,
* a DragSourceContext
created from the resulting deserialized
* stream will contain a dummy Transferable
which supports no
* DataFlavor
s. Finally, this object's
* DragSourceListener
is written out if and only if it can be
* serialized. If not, null
is written instead.
*
* @serialData The default serializable fields, in alphabetical order,
* followed by either a Transferable
instance, or
* null
, followed by either a
* DragSourceListener
instance, or
* null
.
* @since 1.4
*/
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeObject(SerializationTester.test(transferable)
? transferable : null);
s.writeObject(SerializationTester.test(listener)
? listener : null);
}
/**
* Deserializes this DragSourceContext
. This method first
* performs default deserialization for all non-transient
* fields. This object's Transferable
and
* DragSourceListener
are then deserialized as well by using
* the next two objects in the stream. If the resulting
* Transferable
is null
, this object's
* Transferable
is set to a dummy Transferable
* which supports no DataFlavor
s.
*
* @since 1.4
*/
private void readObject(ObjectInputStream s)
throws ClassNotFoundException, IOException
{
s.defaultReadObject();
transferable = (Transferable)s.readObject();
listener = (DragSourceListener)s.readObject();
// Implementation assumes 'transferable' is never null.
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;
/**
* The event which triggered the start of the drag.
*
* @serial
*/
private DragGestureEvent trigger;
/**
* The current drag cursor.
*
* @serial
*/
private Cursor cursor;
private transient Transferable transferable;
private transient DragSourceListener listener;
/**
* true
if the custom drag cursor is used instead of the
* default one.
*
* @serial
*/
private boolean useCustomCursor;
/**
* A bitwise mask of DnDConstants
that represents the set of
* drop actions supported by the drag source for the drag operation associated
* with this DragSourceContext.
*
* @serial
*/
private final int sourceActions;
}