|
@@ -0,0 +1,216 @@
|
|
|
|
+package ui.view.outliner;
|
|
|
|
+
|
|
|
|
+import java.awt.datatransfer.DataFlavor;
|
|
|
|
+import java.awt.datatransfer.Transferable;
|
|
|
|
+import java.awt.datatransfer.UnsupportedFlavorException;
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.List;
|
|
|
|
+
|
|
|
|
+import javax.swing.JComponent;
|
|
|
|
+import javax.swing.JTree;
|
|
|
|
+import javax.swing.TransferHandler;
|
|
|
|
+import javax.swing.tree.TreePath;
|
|
|
|
+
|
|
|
|
+import classes.Holon;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class HolonTreeTransferHandler extends TransferHandler {
|
|
|
|
+ DataFlavor nodesFlavor;
|
|
|
|
+ DataFlavor[] flavors = new DataFlavor[1];
|
|
|
|
+ Holon[] nodesToRemove;
|
|
|
|
+
|
|
|
|
+ public HolonTreeTransferHandler() {
|
|
|
|
+ try {
|
|
|
|
+ String mimeType = DataFlavor.javaJVMLocalObjectMimeType +
|
|
|
|
+ ";class=\"" +
|
|
|
|
+ classes.Holon.class.getName() +
|
|
|
|
+ "\"";
|
|
|
|
+ nodesFlavor = new DataFlavor(mimeType);
|
|
|
|
+ flavors[0] = nodesFlavor;
|
|
|
|
+ } catch(ClassNotFoundException e) {
|
|
|
|
+ System.out.println("ClassNotFound: " + e.getMessage());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public boolean canImport(TransferHandler.TransferSupport support) {
|
|
|
|
+ if(!support.isDrop()) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ support.setShowDropLocation(true);
|
|
|
|
+ if(!support.isDataFlavorSupported(nodesFlavor)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ // Do not allow a drop on the drag source selections.
|
|
|
|
+ JTree.DropLocation dl =
|
|
|
|
+ (JTree.DropLocation)support.getDropLocation();
|
|
|
|
+ JTree tree = (JTree)support.getComponent();
|
|
|
|
+ int dropRow = tree.getRowForPath(dl.getPath());
|
|
|
|
+ int[] selRows = tree.getSelectionRows();
|
|
|
|
+ for(int i = 0; i < selRows.length; i++) {
|
|
|
|
+ if(selRows[i] == dropRow) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // Do not allow MOVE-action drops if a non-leaf node is
|
|
|
|
+ // selected unless all of its children are also selected.
|
|
|
|
+ int action = support.getDropAction();
|
|
|
|
+ if(action == MOVE) {
|
|
|
|
+ return haveCompleteNode(tree);
|
|
|
|
+ }
|
|
|
|
+ // Do not allow a non-leaf node to be copied to a level
|
|
|
|
+ // which is less than its source level.
|
|
|
|
+ TreePath dest = dl.getPath();
|
|
|
|
+ Holon target =
|
|
|
|
+ (Holon)dest.getLastPathComponent();
|
|
|
|
+ TreePath path = tree.getPathForRow(selRows[0]);
|
|
|
|
+ Holon firstNode =
|
|
|
|
+ (Holon)path.getLastPathComponent();
|
|
|
|
+ if(firstNode.getChildCount() > 0 &&
|
|
|
|
+ target.getLevel() < firstNode.getLevel()) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private boolean haveCompleteNode(JTree tree) {
|
|
|
|
+ int[] selRows = tree.getSelectionRows();
|
|
|
|
+ TreePath path = tree.getPathForRow(selRows[0]);
|
|
|
|
+ Holon first =
|
|
|
|
+ (Holon)path.getLastPathComponent();
|
|
|
|
+ int childCount = first.getChildCount();
|
|
|
|
+ // first has children and no children are selected.
|
|
|
|
+ if(childCount > 0 && selRows.length == 1)
|
|
|
|
+ return false;
|
|
|
|
+ // first may have children.
|
|
|
|
+ for(int i = 1; i < selRows.length; i++) {
|
|
|
|
+ path = tree.getPathForRow(selRows[i]);
|
|
|
|
+ Holon next =
|
|
|
|
+ (Holon)path.getLastPathComponent();
|
|
|
|
+ if(first.getChildView().contains(next)) {
|
|
|
|
+ // Found a child of first.
|
|
|
|
+ if(childCount > selRows.length-1) {
|
|
|
|
+ // Not all children of first are selected.
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ protected Transferable createTransferable(JComponent c) {
|
|
|
|
+ JTree tree = (JTree)c;
|
|
|
|
+ TreePath[] paths = tree.getSelectionPaths();
|
|
|
|
+ if(paths != null) {
|
|
|
|
+ // Make up a node array of copies for transfer and
|
|
|
|
+ // another for/of the nodes that will be removed in
|
|
|
|
+ // exportDone after a successful drop.
|
|
|
|
+ List<Holon> copies =
|
|
|
|
+ new ArrayList<Holon>();
|
|
|
|
+ List<Holon> toRemove =
|
|
|
|
+ new ArrayList<Holon>();
|
|
|
|
+ Holon node =
|
|
|
|
+ (Holon)paths[0].getLastPathComponent();
|
|
|
|
+ Holon copy = node.cloneWithoutParent();
|
|
|
|
+ copies.add(copy);
|
|
|
|
+ toRemove.add(node);
|
|
|
|
+ for(int i = 1; i < paths.length; i++) {
|
|
|
|
+ Holon next =
|
|
|
|
+ (Holon)paths[i].getLastPathComponent();
|
|
|
|
+ // Do not allow higher level nodes to be added to list.
|
|
|
|
+ if(next.getLevel() < node.getLevel()) {
|
|
|
|
+ break;
|
|
|
|
+ } else if(next.getLevel() > node.getLevel()) { // child node
|
|
|
|
+ copy.addChildHolon(next.cloneWithoutParent());
|
|
|
|
+ // node already contains child
|
|
|
|
+ } else { // sibling
|
|
|
|
+ copies.add(next.cloneWithoutParent());
|
|
|
|
+ toRemove.add(next);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ Holon[] nodes =
|
|
|
|
+ copies.toArray(new Holon[copies.size()]);
|
|
|
|
+ nodesToRemove =
|
|
|
|
+ toRemove.toArray(new Holon[toRemove.size()]);
|
|
|
|
+ return new NodesTransferable(nodes);
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ protected void exportDone(JComponent source, Transferable data, int action) {
|
|
|
|
+ if((action & MOVE) == MOVE) {
|
|
|
|
+ JTree tree = (JTree)source;
|
|
|
|
+ HolonTreeModel model = (HolonTreeModel)tree.getModel();
|
|
|
|
+ // Remove nodes saved in nodesToRemove in createTransferable.
|
|
|
|
+ for(int i = 0; i < nodesToRemove.length; i++) {
|
|
|
|
+ model.removeNodeFromParent(nodesToRemove[i]);
|
|
|
|
+ }
|
|
|
|
+ tree.updateUI();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public int getSourceActions(JComponent c) {
|
|
|
|
+ return COPY_OR_MOVE;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public boolean importData(TransferHandler.TransferSupport support) {
|
|
|
|
+ if(!canImport(support)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ // Extract transfer data.
|
|
|
|
+ Holon[] nodes = null;
|
|
|
|
+ try {
|
|
|
|
+ Transferable t = support.getTransferable();
|
|
|
|
+ nodes = (Holon[])t.getTransferData(nodesFlavor);
|
|
|
|
+ } catch(UnsupportedFlavorException ufe) {
|
|
|
|
+ System.out.println("UnsupportedFlavor: " + ufe.getMessage());
|
|
|
|
+ } catch(java.io.IOException ioe) {
|
|
|
|
+ System.out.println("I/O error: " + ioe.getMessage());
|
|
|
|
+ }
|
|
|
|
+ // Get drop location info.
|
|
|
|
+ JTree.DropLocation dl =
|
|
|
|
+ (JTree.DropLocation)support.getDropLocation();
|
|
|
|
+ int childIndex = dl.getChildIndex();
|
|
|
|
+ TreePath dest = dl.getPath();
|
|
|
|
+ Holon parent =
|
|
|
|
+ (Holon)dest.getLastPathComponent();
|
|
|
|
+ JTree tree = (JTree)support.getComponent();
|
|
|
|
+ HolonTreeModel model = (HolonTreeModel)tree.getModel();
|
|
|
|
+ // Configure for drop mode.
|
|
|
|
+ int index = childIndex; // DropMode.INSERT
|
|
|
|
+ if(childIndex == -1) { // DropMode.ON
|
|
|
|
+ index = parent.getChildCount();
|
|
|
|
+ }
|
|
|
|
+ // Add data to model.
|
|
|
|
+ for(int i = 0; i < nodes.length; i++) {
|
|
|
|
+ model.insertNodeInto(nodes[i], parent, index++);
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public String toString() {
|
|
|
|
+ return getClass().getName();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public class NodesTransferable implements Transferable {
|
|
|
|
+ Holon[] nodes;
|
|
|
|
+
|
|
|
|
+ public NodesTransferable(Holon[] nodes) {
|
|
|
|
+ this.nodes = nodes;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public Object getTransferData(DataFlavor flavor)
|
|
|
|
+ throws UnsupportedFlavorException {
|
|
|
|
+ if(!isDataFlavorSupported(flavor))
|
|
|
|
+ throw new UnsupportedFlavorException(flavor);
|
|
|
|
+ return nodes;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public DataFlavor[] getTransferDataFlavors() {
|
|
|
|
+ return flavors;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public boolean isDataFlavorSupported(DataFlavor flavor) {
|
|
|
|
+ return nodesFlavor.equals(flavor);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|