CONTENTS | PREV | NEXT | Drag and Drop |
The DragSource is the entity responsible for the initiation of the Drag and Drop operation:
The DragSource and associated constant interfaces are defined as follows:The DnDConstants class defines the operations that may be applied to the subject of the transfer:
public final class java.awt.dnd.DnDConstants { public static int ACTION_NONE = 0x0; public static int ACTION_COPY = 0x1; public static int ACTION_MOVE = 0x2; public static int ACTION_COPY_OR_MOVE = ACTION_COPY | ACTION_MOVE; public static int ACTION_REFERENCE = 0x40000000; } public class java.awt.dnd.DragSource { public static Cursor DefaultCopyDrop; public static Cursor DefaultMoveDrop; public static Cursor DefaultLinkDrop; public static Cursor DefaultCopyNoDrop; public static Cursor DefaultMoveNoDrop; public static Cursor DefaultLinkNoDrop; public static DragSource getDefaultDragSource(); public static boolean isDragImageSupported(); public void startDrag(DragGestureEvent trigger, Cursor dragCursor, Image dragImage, Point dragImageOffset, Transferable transferable, DragSourceListener dsl, FlavorMap fm) throws InvalidDnDOperationException; protected DragSourceContext createDragSourceContext( DragSourceContextPeer dscp, DragGestureEvent trigger, Cursor dragCursor, Image dragImage, Point dragImageOffset, Transferable transferable, DragSourceListener dsl ); public FlavorMap getFlavorMap(); public DragGestureRecongizer createDragGestureRecognizer( Class abstractRecognizerClass, Component c, int srcActions, DragGestureListener dgl ); public DragGestureRecongizer createDefaultDragGestureRecognizer( Component c, int srcActions, DragGestureListener dgl ); }
The DragSource may be used in a number of scenarios:
- 1 default instance per JVM* for the lifetime of that JVM. (defined by this spec)
- 1 instance per class of potential Drag Initiator object (e.g TextField). [implementation dependent]
- 1 per instance of a particular Component, or application specific object associated with a Component instance in the GUI. [implementation dependent]
- some other arbitrary association. [implementation dependent]
A controlling object, shall obtain a DragSource instance prior to a users gesture, effecting an associated Component, in order to process the operation. Once obtained a DragGestureRecognizer should be obtained and used to associate the DragSource with a Component.The initial interpretation of the users gesture, and the subsequent starting of the Drag operation are the responsibility of the implementing Component, this is usually implemented by a DragGestureRecognizer.
When a gesture occurs, the DragSource's startDrag() method shall be invoked in order to cause processing of the users navigational gestures and delivery of Drag and Drop protocol notifications. A DragSource shall only permit a single Drag and Drop operation to be current at any one time, and shall reject any further startDrag() requests by throwing an IllegalDnDOperationException until such time as the extant operation is complete.
In order to start a drag operation the caller of the startDrag() method shall provide the following parameters:
- The DragGestureEvent for the gesture.
- A Cursor representing the initial "Drag Over" feedback for the operation(s) specified. (This shall be a Cursor that provides "No Drop" visual feedback to the user).
- An (optional) Image to visually represent the item, or item(s) that are the subject(s) of the operation.
On platforms that can support this feature, a "Drag" image may be associated with the operation to enhance the fidelity of the "Drag Over" feedback. This image would typically be a small "iconic" representation of the object, or objects being dragged, and would be rendered by the underlying system, tracking the movement of, and coincident with, but typically in addition to the Cursor animation.Where this facility is not available, or where the image is not of a suitable type to be rendered by the underlying system, this parameter is ignored and only Cursor "Drag Over" animation results, so applications should not depend upon this feature. The presence of the facility on a particular platform may be tested by invoking the static method isDragImageSupported().
- Where an Image is provided; a Point (in the co-ordinate space of the Component) specifying the initial origin of that Image relative to the co-ordinates of the "hotspot" of the drag "Cursor", in the co-ordinate space of the Component, at the time of the initial gesture, for the purposes of initiating a correctly positioned "Drag Over" animation of that Image relative to that "hotspot".
- A Transferable that describes the various DataFlavor(s) that represent the subject(s) of any subsequent data transfer that may result from a successful Drop.
The Transferable instance associated with the DragSource at the start of the Drag operation, represents the object(s) or data that are the operand(s), or the subject(s), of the Drag and Drop operation, that is the information that will subsequently be passed from the DragSource to the DropTarget as a result of a successful Drop on the Component associated with that DropTarget.Note that multiple (collections) of either homogeneous, or heterogeneous, objects may be subject of a Drag and Drop operation, by creating a container object, that is the subject of the transfer, and implements Transferable. However it should be noted that since none of the targeted native platforms systems support a standard mechanism for describing and thus transferring such collections it is not possible to implement such transfers in a transparent, or platform portable fashion.
As stated above, the primary role of the startDrag() method is to initiate a Drag on behalf of the user. In order to accomplish this, the startDrag() method must create a DragSourceContext instance to track the operation itself, and more importantly it must initiate the operation itself in the underlying platform implementation. In order to accomplish this, the DragSource must first obtain a DragSourceContextPeer from the underlying system (usually via an invocation of java.awt.Toolkit.createDragSourceContextPeer() method) and subsequently associate this newly created DragSourceContextPeer (which provides a platform independent interface to the underlying systems capabilities) with a DragSourceContext.The startDrag() method invokes the createDragSourceContext() method to instantiate an appropriate DragSourceContext and associate the DragSourceContextPeer with that.If the Drag and Drop System is unable to initiate a Drag operation for some reason the startDrag() method shall throw a java.awt.dnd.InvalidDnDOperationException to signal such a condition. Typically this exception is thrown when the underlying platform system is either not in a state to initiate a Drag, or the parameters specified are invalid.
Note that during the Drag neither the set of operations the source exposed at the start of the Drag operation may change for the duration of the operation, in other words the operation(s) and are constant for the duration of the operation with respect to the DragSource.
The getFlavorMap() method is used by the underlying system to obtain a FlavorMap object in order to map the DataFlavors exposed by the Transferable to data type names of the underlying DnD platform. [see later for details of the FlavorMap]
A "private" FlavorMap may be provided to the startDrag() method of the DragSource, or null, in which case the "default" FlavorMap for that DragSource class or instance is used.
As a result of a DragSource's startDrag() method being successfully invoked an instance of the DragSourceContext class is created. This instance is responsible for tracking the state of the operation on behalf of the DragSource and dispatching state changes to the DragSourceListener.The DragSourceContext class is defined as follows:
public class DragSourceContext implements DragSourceListener { protected DragSourceContext( DragSourceContextPeer dscp, DragGestureEvent trigger, Cursor dragCursor, Image dragImage, Point dragOffset, Transferable transferable, DragSourceListener dsl ); public DragSource getDragSource(); public Component getComponent(); public DragGestureEvent getTrigger(); public Image getDragImage(); public Point getDragImageOffset(); public void transferablesFlavorsChanged(); public int getSourceActions(); public Cursor getCursor(); pbulic void setCursor Cursor(Cursor) throws InvalidDnDOperationException; public void addDragSourceListener(DragSourceListener dsl) throws TooManyListenersException; public void removeDragSourceListener(DragSourceListener dsl); protected updateCurrentCursor(int dropOperation, int targetActions, int status ); // values for status parameter above. protected static final int DEFAULT = 0; protected static final int ENTER = 1; protected static final int OVER = 2; protected static final int CHANGED = 3; protected boolean cursorDirty; }
Note that the DragSourceContext itself implements DragSourceListener, this is to allow the platform peer, the DragSourceContextPeer instance, created by the DragSource, to notify the DragSourceContext of changes in state in the ongoing operation, and thus allows the DragSourceContext to interpose itself between the platform and the DragSourceListener provided by the initiator of the operation.The state machine the platform exposes, with respect to the source, or initiator of the Drag and Drop operation is detailed below:
Notifications of changes in state with respect to the initiator during a Drag and Drop operation, as illustrated above, are delivered from the DragSourceContextPeer, to the appropriate DragSourceContext, which delegates notifications, via a unicast JavaBeans compliant EventListener subinterface, to an arbitrary object that implements DragSourceListener registered with the DragSource via startDrag().
The primary responsibility of the DragSourceListener is to monitor the progress of the users navigation during the Drag and Drop operation and provide the "Drag-Over" effects feedback to the user. Typically this is accomplished via changes to the "Drag Cursor".
Every Drag operation has 2 logical cursor states (Drag Cursors) associated with it:
The state of the Cursor may be modified by calling the setCursor() method of the DragSourceContext.
The DragSourceListener interface is defined as follows:
public interface java.awt.dnd.DragSourceListener extends java.util.EventListener { void dragEnter (DragSourceDragEvent dsde); void dragOver (DragSourceDragEvent dsde); void dropActionChanged (DragSourceDragEvent dsde); void dragExit (DragSourceEvent dse); void dragDropEnd (DragSourceDropEvent dsde); }
As the drag operation progresses, the DragSourceListener's dragEnter(), dragOver(), and dragExit() methods shall be invoked as a result of the users navigation of the logical "Drag" cursor's location intersecting the geometry of GUI Component(s) with associated DropTarget(s). [See below for details of the DropTarget's protocol interactions].
The DragSourceListener's dragEnter() method is invoked when the following conditions are true:
The DropTarget's registered DropTargetListener dragEnter() method is invoked and returns successfully.The registered DropTargetListener invokes the DropTargetDragEvent's acceptDrag() method to accept the Drag based upon interrogation of the source's potential Drop actions and available data types (DataFlavors).
The DragSourceListener's dragOver() method is invoked when the following conditions are true:
- The cursor's logical hotspot has moved but still intersects the visible geometry of the Component associated with the previous dragEnter() invocation.
- That Component still has a DropTarget associated.
- That DropTarget is still active.
- The DropTarget's registered DropTargetListener dragOver() method is invoked and returns successfully.
- The DropTarget does not reject the drag via rejectDrag().
The DragSourceListener's dragExit() method is invoked when one of the following conditions is true:
- The cursor's logical hotspot no longer intersects the visible geometry of the Component associated with the previous dragEnter() invocation.
Or:
- The Component that the logical cursor's hotspot intersected that resulted in the previous dragEnter() invocation, no longer has an active DropTarget (or DropTargetListener) associated.
Or:
The DragSourceListener's dropActionChanged() method is invoked when the state of the input device(s), typically the mouse buttons or keyboard modifiers, that the user is interacting with in order to preform the Drag operation, changes.The dragDropEnd() method is invoked to signify that the operation is completed. The getDropSuccess() method of the DragSourceDropEvent can be used to determine the termination state. The getDropAction() method returns the operation that the DropTarget selected (via the DropTargetDropEvent acceptDrop() parameter) to apply to the Drop operation.1
Once this method is complete the current DragSourceContext and the associated resources are invalid.
The DragSourceEvent class is the root Event class for all events pertaining to the DragSource, and is defined as follows:
public class java.awt.dnd.DragSourceEvent extends java.util.EventObject { public DragSourceEvent(DragSourceContext dsc); public DragSourceContext getDragSourceContext(); };
An instance of this event is passed to the DragSourceListener dragExit() method.
The DragSourceDragEvent class is defined as follows:
public class java.awt.dnd.DragSourceDragEvent extends DragSourceEvent { public int getTargetActions(); public int getUserAction(); public int getGestureModifiers(); public boolean isDropTargetLocal(); public int getDropAction(); }
An instance of the above class is passed to a DragSourceListener's dragEnter(), dragOver(), and dragGestureChanged() methods.The getDragSourceContext() method returns the DragSourceContext associated with the current Drag and Drop operation.
The getTargetActions() method returns the drop actions, supported by, and returned from the current DropTarget (if any in the case of dropActionChanged()).
The getDropAction() method returns the action that is currently selected by the users gesture.
The getTargetActions() method returns the set of actions supported by the current DropTarget.
The logical OR of these two results defines the actual effect of a Drop.
The getGestureModifiers() returns the current state of the input device modifiers, usually the mouse buttons and keyboard modifiers, associated with the users gesture.
The isDropTargetLocal() method returns true if the current DropTarget is contained within the same JVM as the DragSource, and false otherwise. This information can be useful to the implementor of the DragSource's Transferable in order to implement certain local optimizations.
The DragSourceDropEvent class is defined as follows:
public public class java.awt.dnd.DragSourceDropEvent extends java.util.EventObject { public DragSourceDropEvent(DragSourceContext dsc); public DragSourceDropEvent(DragSourceContext dsc, int action, boolean success); public boolean getDropSuccess(); public int getDropAction(); }
An instance of the above class is passed to a DragSourceListener's dragDropEnd() method. This event encapsulates the termination state of the Drag and Drop operation for the DragSource.If the Drop occurs, then the participating DropTarget will signal the success or failure of the data transfer via the DropTargetContext's dropComplete() method, this status is made available to the initiator via the getDropSuccess() method. The operation that the destination DropTarget selected to perform on the subject of the Drag (passed by the DropTarget's acceptDrop() method) is returned via the getDropAction() method.
If the Drag operation was aborted for any reason prior to a Drop occurring, for example if the users ends the gesture outside a DropTarget, or if the DropTarget invokes rejectDrop(), the isGetDropSuccess() method will return false, otherwise true.
*As used on this web site, the terms "Java Virtual Machine" or "JVM" mean a virtual machine for the Java platform.