JTree, user feedback on dragOver

I want a "no drag target" cursor over no drop targets and a highlight on drop able targets.

What methods do I have to implement?

I have implemented a JTree with DND Support.

I use the following declaration.

publicfinalclass Treeextends JTreeimplements TreeSelectionListener, DragGestureListener, DropTargetListener, DragSourceListener

...

Now I want to highlight the node under the drag cursor as visual feedback to the user.

I thought dragEnter ist a good method for that functionality which for some reason is not working.

Despite TreePath path = this.getPathForLocation(location.x, location.y);

delivers always null. Why?

publicvoid dragEnter(DragSourceDragEvent dragEvent)

{

Point location = dragEvent.getLocation();

TreePath path = this.getPathForLocation(location.x, location.y);

// path is always null why ?

DragSourceContext context = dragEvent.getDragSourceContext();

// select node as visual feedback

if (path !=null)

{

this.setSelectionPath(path);

this.scrollPathToVisible(path);

context.setCursor(DragSource.DefaultMoveDrop);

}

else

{

context.setCursor(DragSource.DefaultMoveNoDrop);

}

}

I looked at the DNDTree in forum, but I need special explanation.

[1898 byte] By [Wladimira] at [2007-11-26 20:45:48]
# 1
try using dragOver
tjacobs01a at 2007-7-10 2:07:25 > top of Java-index,Desktop,Core GUI APIs...
# 2

You should use the DropTarget for this and not the DragSource.

Here's a modified example taken from here:

http://www.java2s.com/Code/Java/Swing-JFC/DnDdraganddropJTreecode.htm

import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.Graphics;

import java.awt.Insets;

import java.awt.Point;

import java.awt.Rectangle;

import java.awt.datatransfer.DataFlavor;

import java.awt.datatransfer.Transferable;

import java.awt.datatransfer.UnsupportedFlavorException;

import java.awt.dnd.Autoscroll;

import java.awt.dnd.DnDConstants;

import java.awt.dnd.DragGestureEvent;

import java.awt.dnd.DragGestureListener;

import java.awt.dnd.DragGestureRecognizer;

import java.awt.dnd.DragSource;

import java.awt.dnd.DragSourceDragEvent;

import java.awt.dnd.DragSourceDropEvent;

import java.awt.dnd.DragSourceEvent;

import java.awt.dnd.DragSourceListener;

import java.awt.dnd.DropTarget;

import java.awt.dnd.DropTargetContext;

import java.awt.dnd.DropTargetDragEvent;

import java.awt.dnd.DropTargetDropEvent;

import java.awt.dnd.DropTargetEvent;

import java.awt.dnd.DropTargetListener;

import java.io.IOException;

import java.util.Vector;

import javax.swing.JFrame;

import javax.swing.JScrollPane;

import javax.swing.JTree;

import javax.swing.tree.DefaultMutableTreeNode;

import javax.swing.tree.DefaultTreeModel;

import javax.swing.tree.TreeNode;

import javax.swing.tree.TreePath;

public class TreeDragTest extends JFrame {

TreeDragSource ds;

TreeDropTarget dt;

JTree tree;

public TreeDragTest() {

super("Rearrangeable Tree");

setSize(300, 200);

setDefaultCloseOperation(EXIT_ON_CLOSE);

// If you want autoscrolling, use this line:

tree = new AutoScrollingJTree();

// Otherwise, use this line:

//tree = new JTree();

getContentPane().add(new JScrollPane(tree), BorderLayout.CENTER);

// If we only support move operations...

//ds = new TreeDragSource(tree, DnDConstants.ACTION_MOVE);

ds = new TreeDragSource(tree, DnDConstants.ACTION_COPY_OR_MOVE);

dt = new TreeDropTarget(tree);

setVisible(true);

}

public class AutoScrollingJTree extends JTree implements Autoscroll {

private int margin = 12;

public AutoScrollingJTree() {

super();

}

public void setSelectionPath(TreePath path) {

if (!isPathSelected(path))

super.setSelectionPath(path);

}

public void autoscroll(Point p) {

int realrow = getRowForLocation(p.x, p.y);

Rectangle outer = getBounds();

realrow = (p.y + outer.y <= margin ? realrow < 1 ? 0 : realrow - 1

: realrow < getRowCount() - 1 ? realrow + 1 : realrow);

scrollRowToVisible(realrow);

}

public Insets getAutoscrollInsets() {

Rectangle outer = getBounds();

Rectangle inner = getParent().getBounds();

return new Insets(inner.y - outer.y + margin, inner.x - outer.x

+ margin, outer.height - inner.height - inner.y + outer.y

+ margin, outer.width - inner.width - inner.x + outer.x

+ margin);

}

// Use this method if you want to see the boundaries of the

// autoscroll active region

public void paintComponent(Graphics g) {

super.paintComponent(g);

Rectangle outer = getBounds();

Rectangle inner = getParent().getBounds();

g.setColor(Color.red);

g.drawRect(-outer.x + 12, -outer.y + 12, inner.width - 24,

inner.height - 24);

}

}

public static void main(String args[]) {

new TreeDragTest();

}

}

//TreeDragSource.java

//A drag source wrapper for a JTree. This class can be used to make

//a rearrangeable DnD tree with the TransferableTreeNode class as the

//transfer data type.

class TreeDragSource implements DragSourceListener, DragGestureListener {

DragSource source;

DragGestureRecognizer recognizer;

TransferableTreeNodes transferable;

Vector <DefaultMutableTreeNode> oldNodes = new Vector <DefaultMutableTreeNode>();

JTree sourceTree;

public TreeDragSource(JTree tree, int actions) {

sourceTree = tree;

source = new DragSource();

recognizer = source.createDefaultDragGestureRecognizer(sourceTree,

actions, this);

}

/*

* Drag Gesture Handler

*/

public void dragGestureRecognized(DragGestureEvent dge) {

TreePath[] paths = sourceTree.getSelectionPaths();

if ((paths == null) || (paths.length < 1)) {

// We can't move the root node or an empty selection

return;

}

oldNodes.clear();

for (TreePath path : paths)

oldNodes.add((DefaultMutableTreeNode) path.getLastPathComponent());

transferable = new TransferableTreeNodes(paths);

source.startDrag(dge, null, transferable, this);

// If you support dropping the node anywhere, you should probably

// start with a valid move cursor:

//source.startDrag(dge, DragSource.DefaultMoveDrop, transferable,

// this);

}

/*

* Drag Event Handlers

*/

public void dragEnter(DragSourceDragEvent dsde) {

}

public void dragExit(DragSourceEvent dse) {

}

public void dragOver(DragSourceDragEvent dsde) {

}

public void dropActionChanged(DragSourceDragEvent dsde) {

System.out.println("Action: " + dsde.getDropAction());

System.out.println("Target Action: " + dsde.getTargetActions());

System.out.println("User Action: " + dsde.getUserAction());

}

public void dragDropEnd(DragSourceDropEvent dsde) {

/*

* to support move or copy, we have to check which occurred:

*/

System.out.println("Drop Action: " + dsde.getDropAction());

if (dsde.getDropSuccess()

&& (dsde.getDropAction() == DnDConstants.ACTION_MOVE)) {

for (DefaultMutableTreeNode oldNode : oldNodes)

((DefaultTreeModel) sourceTree.getModel()).removeNodeFromParent(oldNode);

}

/*

* to support move only... if (dsde.getDropSuccess()) {

* ((DefaultTreeModel)sourceTree.getModel()).removeNodeFromParent(oldNode); }

*/

}

}

//TreeDropTarget.java

//A quick DropTarget that's looking for drops from draggable JTrees.

class TreeDropTarget implements DropTargetListener {

DropTarget target;

JTree targetTree;

public TreeDropTarget(JTree tree) {

targetTree = tree;

target = new DropTarget(targetTree, this);

}

/*

* Drop Event Handlers

*/

private TreeNode getNodeForEvent(DropTargetDragEvent dtde) {

Point p = dtde.getLocation();

DropTargetContext dtc = dtde.getDropTargetContext();

JTree tree = (JTree) dtc.getComponent();

TreePath path = tree.getClosestPathForLocation(p.x, p.y);

return (TreeNode) path.getLastPathComponent();

}

public void dragEnter(DropTargetDragEvent dtde) {

TreeNode node = getNodeForEvent(dtde);

if (node.isLeaf()) {

dtde.rejectDrag();

} else {

// start by supporting move operations

//dtde.acceptDrag(DnDConstants.ACTION_MOVE);

dtde.acceptDrag(dtde.getDropAction());

}

}

public void dragOver(DropTargetDragEvent dtde) {

TreeNode node = getNodeForEvent(dtde);

if (node.isLeaf()) {

dtde.rejectDrag();

} else {

// start by supporting move operations

//dtde.acceptDrag(DnDConstants.ACTION_MOVE);

dtde.acceptDrag(dtde.getDropAction());

DropTargetContext dtc = dtde.getDropTargetContext();

JTree tree = (JTree) dtc.getComponent();

TreePath path = tree.getClosestPathForLocation(dtde.getLocation().x, dtde.getLocation().y);

tree.setSelectionPath(path);

}

}

public void dragExit(DropTargetEvent dte) {

}

public void dropActionChanged(DropTargetDragEvent dtde) {

}

public void drop(DropTargetDropEvent dtde) {

Point pt = dtde.getLocation();

DropTargetContext dtc = dtde.getDropTargetContext();

JTree tree = (JTree) dtc.getComponent();

TreePath parentpath = tree.getClosestPathForLocation(pt.x, pt.y);

DefaultMutableTreeNode parent = (DefaultMutableTreeNode) parentpath.getLastPathComponent();

if (parent.isLeaf()) {

dtde.rejectDrop();

return;

}

try {

Transferable tr = dtde.getTransferable();

DataFlavor[] flavors = tr.getTransferDataFlavors();

for (int i = 0; i < flavors.length; i++) {

if (tr.isDataFlavorSupported(flavors[i])) {

dtde.acceptDrop(dtde.getDropAction());

TreePath[] paths = (TreePath[]) tr.getTransferData(flavors[i]);

for (TreePath p : paths) {

DefaultMutableTreeNode node = (DefaultMutableTreeNode) p.getLastPathComponent();

if (parentpath.toString().equals(p.toString())) {

dtde.dropComplete(false);

return;

}

else if (parentpath.toString().startsWith(p.toString().substring(0, p.toString().length()-1))) {

dtde.dropComplete(false);

return;

//DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

//model.insertNodeInto(node, parent, 0);

//model.insertNodeInto(parent, (DefaultMutableTreeNode)parent.getParent().getParent(), 0);

}

else {

DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

model.insertNodeInto(node, parent, 0);

}

}

dtde.dropComplete(true);

return;

}

}

dtde.rejectDrop();

} catch (Exception e) {

e.printStackTrace();

dtde.rejectDrop();

}

}

}

//TransferableTreeNode.java

//A Transferable TreePath to be used with Drag & Drop applications.

class TransferableTreeNodes implements Transferable {

public static DataFlavor TREE_PATH_FLAVOR = new DataFlavor(TreePath.class,

"Tree Path");

DataFlavor flavors[] = { TREE_PATH_FLAVOR };

TreePath[] paths;

public TransferableTreeNodes(TreePath[] tps) {

paths = tps;

}

public synchronized DataFlavor[] getTransferDataFlavors() {

return flavors;

}

public boolean isDataFlavorSupported(DataFlavor flavor) {

return (flavor.getRepresentationClass() == TreePath.class);

}

public synchronized Object getTransferData(DataFlavor flavor)

throws UnsupportedFlavorException, IOException {

if (isDataFlavorSupported(flavor)) {

return (Object) paths;

} else {

throw new UnsupportedFlavorException(flavor);

}

}

}

Rodney_McKaya at 2007-7-10 2:07:25 > top of Java-index,Desktop,Core GUI APIs...
# 3

DragTarget works, thanks!

But I try to fix the second problem.

My Cursors remains still as "Move Cursor".

What method decides if the cursor should be "no drag" or "move"?

I want only on my leafs "move" cursor and everywhere else "no drag".

I thought acceptDrag and rejectDrag would manage this. But they do not. Cursor is always on "move".

public void dragOver(DropTargetDragEvent dragEvent)

My current implementation.

public final class Tree extends JTree implements TreeSelectionListener, DragGestureListener, DropTargetListener, DragSourceListener

{

private static final longserialVersionUID = 1;

/** Stores the selected node info */

protected TreePathSelectedTreePath = null;

protected DefaultMutableTreeNode SelectedNode= null;

/** Variables needed for DnD */

private DragSourcedragSource= null;

public Tree(TreeModel model)

{

super(model);

this.addTreeSelectionListener(this);

/* ********************** CHANGED ********************** */

this.dragSource = DragSource.getDefaultDragSource();

/* ****************** END OF CHANGE ******************** */

DragGestureRecognizer dgr = dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this);

dgr.setSourceActions(dgr.getSourceActions() & ~InputEvent.BUTTON3_MASK);

DropTarget dropTarget = new DropTarget(this, this);

ToolTipManager.sharedInstance().registerComponent(this);

this.setCellRenderer(new TreeRenderer());

this.setRowHeight(TimeCoordinates.ROW_HEIGHT);

this.setScrollsOnExpand(true);

this.setToggleClickCount(2);

this.setAutoscrolls(true);

this.setRootVisible(false);

this.setShowsRootHandles(true);

}

/** TreeSelectionListener - sets selected node */

public void valueChanged(TreeSelectionEvent evt)

{

this.SelectedTreePath = evt.getNewLeadSelectionPath();

if (this.SelectedTreePath == null)

{

this.SelectedNode = null;

return;

}

this.SelectedNode = (DefaultMutableTreeNode)SelectedTreePath.getLastPathComponent();

VehicleTreePane.this.fireRowSelected(evt);

}

public void dragGestureRecognized(DragGestureEvent e)

{

DefaultMutableTreeNode dragNode = this.getSelectedNode();

if (dragNode != null)

{

Transferable transferable = (Transferable)dragNode.getUserObject();

Cursor cursor = this.selectCursor(e.getDragAction());

dragSource.startDrag(e, cursor, transferable, this);

}

}

// Welcher Cursor wird angezeigt

private Cursor selectCursor(int action)

{

return (action == DnDConstants.ACTION_MOVE) ? DragSource.DefaultMoveDrop : DragSource.DefaultCopyDrop;

}

/** Returns The selected node */

public DefaultMutableTreeNode getSelectedNode()

{

return this.SelectedNode;

}

// The Drag operation has terminated with a Drop on this DropTarget

public void drop(DropTargetDropEvent dropEvent)

{

Transferable transferable = dropEvent.getTransferable();

// flavor not supported, reject drop

if (!transferable.isDataFlavorSupported(RentalVehicle.VEHICLE_FLAVOR))

{

dropEvent.rejectDrop();

return;

}

if (dropEvent.getDropAction() == DnDConstants.ACTION_MOVE)

{

Point loc = dropEvent.getLocation();

TreePath destinationPath = this.getPathForLocation(loc.x, loc.y);

RentalVehicle target = (RentalVehicle)((DefaultMutableTreeNode)destinationPath.getLastPathComponent()).getUserObject();

RentalVehicle source = null;

try

{

source = (RentalVehicle)transferable.getTransferData(RentalVehicle.VEHICLE_FLAVOR);

}

catch (UnsupportedFlavorException e)

{

e.printStackTrace();

}

catch (IOException e)

{

e.printStackTrace();

}

VehicleTreePane.this.moveReservations(source, target);

}

// newParent.add(newChild);

}

/**

* {@inheritDoc}

*

* @see javax.swing.JTree#getToolTipText(java.awt.event.MouseEvent)

*/

public String getToolTipText(MouseEvent event)

{

DefaultMutableTreeNode node = (DefaultMutableTreeNode)this.getClosestPathForLocation(event.getX(), event.getY()).getLastPathComponent();

if (!node.isLeaf())

{

return null;

}

return VehicleTreePane.this.getToolTip((RentalVehicle)node.getUserObject());

}

public void dragEnter(DropTargetDragEvent dragEvent)

{

TreeNode node = getNodeForEvent(dragEvent);

if (node.isLeaf())

{

dragEvent.acceptDrag(dragEvent.getDropAction());

}

else

{

// start by supporting move operations

// dtde.acceptDrag(DnDConstants.ACTION_MOVE);

dragEvent.rejectDrag();

}

}

public void dragExit(DropTargetEvent dte)

{

// TODO Auto-generated method stub

}

public void dragOver(DropTargetDragEvent dragEvent)

{

boolean dragable = false;

Point location = dragEvent.getLocation();

TreePath path = this.getPathForLocation(location.x, location.y);

if (path != null)

{

if (((DefaultMutableTreeNode)path.getLastPathComponent()).getUserObject() instanceof RentalVehicle)

{

this.setSelectionPath(path);

this.scrollPathToVisible(path);

dragEvent.acceptDrag(dragEvent.getDropAction());

dragable = true;

}

}

if (!dragable)

{

dragEvent.rejectDrag();

}

}

public void dropActionChanged(DropTargetDragEvent dtde)

{

// TODO Auto-generated method stub

}

public void dragDropEnd(DragSourceDropEvent dsde)

{

// TODO Auto-generated method stub

}

public void dragEnter(DragSourceDragEvent dragEvent)

{

}

public void dragExit(DragSourceEvent dse)

{

// TODO Auto-generated method stub

}

public void dragOver(DragSourceDragEvent dragEvent)

{

}

public void dropActionChanged(DragSourceDragEvent dsde)

{

// TODO Auto-generated method stub

}

private TreeNode getNodeForEvent(DropTargetDragEvent dtde)

{

Point p = dtde.getLocation();

DropTargetContext dtc = dtde.getDropTargetContext();

JTree tree = (JTree)dtc.getComponent();

TreePath path = tree.getClosestPathForLocation(p.x, p.y);

return (TreeNode)path.getLastPathComponent();

}

}

Wladimira at 2007-7-10 2:07:25 > top of Java-index,Desktop,Core GUI APIs...
# 4

You have to change this line:

dragSource.startDrag(e, cursor, transferable, this);

to:

dragSource.startDrag(e, null, transferable, this);

To get the default behavior for the icon.

Message was edited by:

Rodney_McKay

And don't forget to feed me dukes...

Rodney_McKaya at 2007-7-10 2:07:25 > top of Java-index,Desktop,Core GUI APIs...
# 5
great, now I got it runningthanks for the autoscroll too5 dukes for you Rodney_McKay
Wladimira at 2007-7-10 2:07:25 > top of Java-index,Desktop,Core GUI APIs...