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:
ray
2002-08-20 02:49:19 +00:00
parent 51adbd3b20
commit 597290d4df
3 changed files with 281 additions and 0 deletions
@@ -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);
}