A simple DnD system that does not allow DnD operations with other programs,
but does things very nicely within a single app. git-svn-id: https://samskivert.googlecode.com/svn/trunk@814 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -0,0 +1,229 @@
|
|||||||
|
//
|
||||||
|
// $Id: DnDManager.java,v 1.1 2002/08/20 02:49:19 ray Exp $
|
||||||
|
|
||||||
|
package com.samskivert.swing.dnd;
|
||||||
|
|
||||||
|
import java.awt.AWTEvent;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Cursor;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
|
||||||
|
import java.awt.event.AWTEventListener;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseMotionListener;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
import javax.swing.event.AncestorEvent;
|
||||||
|
|
||||||
|
import com.samskivert.swing.event.AncestorAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom Drag and Drop manager for use within a single JVM. Does what we
|
||||||
|
* need it to do and no more.
|
||||||
|
*/
|
||||||
|
public class DnDManager
|
||||||
|
implements MouseMotionListener, AWTEventListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Add the specified component as a source of drags, with the DragSource
|
||||||
|
* controller.
|
||||||
|
*/
|
||||||
|
public static void addDragSource (DragSource source, JComponent comp)
|
||||||
|
{
|
||||||
|
singleton.addSource(source, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the specified component as a drop target.
|
||||||
|
*/
|
||||||
|
public static void addDropTarget (DropTarget target, JComponent comp)
|
||||||
|
{
|
||||||
|
singleton.addTarget(target, comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a custom cursor out of the specified image.
|
||||||
|
*/
|
||||||
|
public static Cursor createImageCursor (Image img)
|
||||||
|
{
|
||||||
|
// TODO: check colors/size
|
||||||
|
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||||
|
return tk.createCustomCursor(img,
|
||||||
|
new Point(img.getWidth(null) / 2, img.getHeight(null) / 2),
|
||||||
|
"samskivertDnDCursor");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restrict construction.
|
||||||
|
*/
|
||||||
|
private DnDManager ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a dragsource.
|
||||||
|
*/
|
||||||
|
protected void addSource (DragSource source, JComponent comp)
|
||||||
|
{
|
||||||
|
_draggers.put(comp, source);
|
||||||
|
comp.addAncestorListener(_remover);
|
||||||
|
comp.addMouseMotionListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a droptarget.
|
||||||
|
*/
|
||||||
|
protected void addTarget (DropTarget target, JComponent comp)
|
||||||
|
{
|
||||||
|
_droppers.put(comp, target);
|
||||||
|
comp.addAncestorListener(_remover);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface MouseMotionListener
|
||||||
|
public void mouseMoved (MouseEvent me)
|
||||||
|
{
|
||||||
|
// who cares.
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface MouseMotionListener
|
||||||
|
public void mouseDragged (MouseEvent me)
|
||||||
|
{
|
||||||
|
// make sure a drag hasn't already started.
|
||||||
|
if (_sourceComp != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sourceComp = me.getComponent();
|
||||||
|
_source = (DragSource) _draggers.get(_sourceComp);
|
||||||
|
|
||||||
|
// make sure the source wants to start a drag.
|
||||||
|
if (!_source.startDrag(_cursors, _data)) {
|
||||||
|
// if not, reset our start conditions and bail
|
||||||
|
reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// use standard cursors if custom ones not specified
|
||||||
|
if (_cursors[0] == null) {
|
||||||
|
_cursors[0] = java.awt.dnd.DragSource.DefaultMoveDrop;
|
||||||
|
}
|
||||||
|
if (_cursors[1] == null) {
|
||||||
|
_cursors[1] = java.awt.dnd.DragSource.DefaultMoveNoDrop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// install a listener so we know everywhere that the mouse enters
|
||||||
|
Toolkit.getDefaultToolkit().addAWTEventListener(this,
|
||||||
|
AWTEvent.MOUSE_EVENT_MASK);
|
||||||
|
|
||||||
|
// and start out with the no-drop cursor
|
||||||
|
_lastComp = _sourceComp;
|
||||||
|
_lastComp.setCursor(_cursors[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface AWTEventListener
|
||||||
|
public void eventDispatched (AWTEvent event)
|
||||||
|
{
|
||||||
|
int id = event.getID();
|
||||||
|
if (id == MouseEvent.MOUSE_ENTERED) {
|
||||||
|
Component oldcomp = _lastComp;
|
||||||
|
_lastComp = ((MouseEvent) event).getComponent();
|
||||||
|
_lastTarget = findAppropriateTarget(_lastComp);
|
||||||
|
_lastComp.setCursor(_cursors[(_lastTarget == null) ? 1 : 0]);
|
||||||
|
if (_lastComp != oldcomp) {
|
||||||
|
oldcomp.setCursor(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (id == MouseEvent.MOUSE_RELEASED) {
|
||||||
|
// since the release comes with a component of the source,
|
||||||
|
// we use the last enter...
|
||||||
|
if (_lastTarget != null) {
|
||||||
|
_lastTarget.dropCompleted(_data[0]);
|
||||||
|
_source.dragCompleted(_lastTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_lastComp != null) {
|
||||||
|
_lastComp.setCursor(null);
|
||||||
|
}
|
||||||
|
Toolkit.getDefaultToolkit().removeAWTEventListener(this);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the lowest accepting parental target to this component.
|
||||||
|
*/
|
||||||
|
protected DropTarget findAppropriateTarget (Component comp)
|
||||||
|
{
|
||||||
|
Component parent;
|
||||||
|
DropTarget target;
|
||||||
|
while (true) {
|
||||||
|
// here we sneakily prevent dropping on the source
|
||||||
|
target = (comp == _sourceComp) ? null
|
||||||
|
: (DropTarget) _droppers.get(comp);
|
||||||
|
if ((target != null) && target.checkDrop(_source, _data[0])) {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
parent = comp.getParent();
|
||||||
|
if (parent == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
comp = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset dnd to a starting state.
|
||||||
|
*/
|
||||||
|
protected void reset ()
|
||||||
|
{
|
||||||
|
_source = null;
|
||||||
|
_sourceComp = null;
|
||||||
|
_lastComp = null;
|
||||||
|
_lastTarget = null;
|
||||||
|
_data[0] = null;
|
||||||
|
_cursors[0] = null;
|
||||||
|
_cursors[1] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A handy helper that removes components when they're no longer in
|
||||||
|
* the hierarchy. */
|
||||||
|
protected AncestorAdapter _remover = new AncestorAdapter() {
|
||||||
|
public void ancestorRemoved (AncestorEvent ae)
|
||||||
|
{
|
||||||
|
JComponent comp = ae.getComponent();
|
||||||
|
_draggers.remove(comp);
|
||||||
|
_droppers.remove(comp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Our DropTargets, indexed by associated Component. */
|
||||||
|
protected HashMap _droppers = new HashMap();
|
||||||
|
|
||||||
|
/** Our DragSources, indexed by associated component. */
|
||||||
|
protected HashMap _draggers = new HashMap();
|
||||||
|
|
||||||
|
/** The original and last component that the mouse was in during a drag. */
|
||||||
|
protected Component _sourceComp, _lastComp;
|
||||||
|
|
||||||
|
/** The source of a drag. */
|
||||||
|
protected DragSource _source;
|
||||||
|
|
||||||
|
/** The last target, or null if no last target. */
|
||||||
|
protected DropTarget _lastTarget;
|
||||||
|
|
||||||
|
/** The accept/reject cursors. */
|
||||||
|
protected Cursor[] _cursors = new Cursor[2];
|
||||||
|
|
||||||
|
/** The data to be passed in the drop. */
|
||||||
|
protected Object[] _data = new Object[1];
|
||||||
|
|
||||||
|
/** A single manager for the entire JVM. */
|
||||||
|
protected static final DnDManager singleton = new DnDManager();
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// $Id: DragSource.java,v 1.1 2002/08/20 02:49:19 ray Exp $
|
||||||
|
|
||||||
|
package com.samskivert.swing.dnd;
|
||||||
|
|
||||||
|
import java.awt.Cursor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A drag source is associated with a component and represents a valid
|
||||||
|
* place to start a drag and drop operation.
|
||||||
|
*/
|
||||||
|
public interface DragSource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called by the DnDManager to verify that a drag may begin at this source.
|
||||||
|
*
|
||||||
|
* @param cursors a two-element array- the first element should contain
|
||||||
|
* the cursor to use for valid drops, the second for invalid drops. Either
|
||||||
|
* or both may be left blank to use the default DnD cursors.
|
||||||
|
* @param data a single-element array that should be filled in with
|
||||||
|
* the data that will be sent to a DropTarget if there is a successful
|
||||||
|
* drop.
|
||||||
|
* @return true if the drag may begin.
|
||||||
|
*/
|
||||||
|
public boolean startDrag (Cursor[] cursors, Object[] data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback to let the source know that the drop completed successfully.
|
||||||
|
*/
|
||||||
|
public void dragCompleted (DropTarget target);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// $Id: DropTarget.java,v 1.1 2002/08/20 02:49:19 ray Exp $
|
||||||
|
|
||||||
|
package com.samskivert.swing.dnd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A drop target is associated with a component and represents a valid
|
||||||
|
* place to end a drag and drop.
|
||||||
|
*/
|
||||||
|
public interface DropTarget
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return true if the specified drop should be allowed on this target.
|
||||||
|
*/
|
||||||
|
public boolean checkDrop (DragSource source, Object peekdata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the drop is actually executed.
|
||||||
|
*/
|
||||||
|
public void dropCompleted (Object data);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user