Again, a very simple method override to enable drop target functionality. Paint a marquee on the full list if it's empty, otherwise mark a space at the end of the list. Most of the code is just futzing with the data model on a drop or moving/sizing the decoration.
Compare that API with the raw D&D APIs (which are still available to override in
JList list = ...;
DataFlavor[] acceptableFlavors = { DataFlavor.stringFlavor };
new DropHandler(list, DnDConstants.ACTION_COPY, acceptableFlavors) {
private Marquee marquee;
/** Always drop at the end of the list. */
protected void drop(DropTargetDropEvent e, int action) throws UnsupportedFlavorException, IOException {
final List data = new ArrayList();
for (int i=0;i < list.getModel().getSize();i++) {
data.add(list.getModel().getElementAt(i));
}
data.add(e.getTransferable().getTransferData(DataFlavor.stringFlavor));
list.setModel(new AbstractListModel() {
public int getSize() {
return data.size();
}
public Object getElementAt(int index) {
return data.get(index);
}
});
}
protected void paintDropTarget(DropTargetEvent e, int action, Point location) {
if (action != DnDConstants.ACTION_NONE && location != null) {
if (marquee == null) {
marquee = new Marquee(list);
}
int count = list.getModel().getSize();
if (count == 0) {
Dimension size = list.getSize();
marquee.setDecorationBounds(new Rectangle(0, 0, size.width, size.height));
}
else {
Rectangle r = list.getCellBounds(count-1, count-1);
r.y += r.height;
marquee.setDecorationBounds(r);
}
}
else if (marquee != null) {
marquee.dispose();
marquee = null;
}
}
};
DropHandler
, by the way):// DropHandler
void drop(DropTargetDropEvent e, int action);
// DropTargetListenerMost of the complexity comes in figuring out the appropriate sequence of calling back methods on the DropTargetEvents, which communicates whether a drag is acceptable and if so, which action is actually to be accepted. This could be clarified a great deal in the Javadoc APIs, or by a thorough example, but ultimately it's boilerplate code that rarely, if ever, needs changes to its behavior.
void dragEnter(DropTargetDragEvent);
void dragOver(DropTargetDragEvent);
void dropActionChanged(DropTargetDragEvent);
void dropExit(DropTargetEvent);
void drop(DropTargetDropEvent);
Back to the example. The JTree drop handler code is more complex, but only to determine what should happen when dropping on any given location. Non-leaf nodes accept drops, but leaf nodes refuse drops, and the spaces between leaf nodes accept drops.
Some
DropHandler
features:- Automatically get the copy action when it's the only one allowed
- Automatically disable user actions which are not allowed
- Some parts of the drop target allow drops, some don't
- Paint any drop target indication you please, regardless of the target component (no subclassing of components)
paintImmediately
within the drop handler. That's avoided by use of a decorator, which uses the existing component hierarchy and layout to paint over the target component. The Marquee
class uses a dashed stroke to draw a rectangle. A timer regularly increases the phase and triggers a repaint, which gives the "marching ants" effect.Unfortunately, you still have to make the component implement
Autoscrolls
, although you could probably call the methods yourself from within the drop handler. It'd be nice if the D&D code which calls into the Autoscrolls
interface instead looked for a client property. That functionality could probably be implemented in the DropHandler
.Next step: hook this up to the list and tree animators.