package robotbuilder.robottree; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.InputEvent; import java.io.IOException; import java.util.HashSet; import java.util.Set; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JOptionPane; import javax.swing.JTree; import javax.swing.TransferHandler; import javax.swing.TransferHandler.TransferSupport; import javax.swing.tree.TreePath; import lombok.SneakyThrows; import robotbuilder.data.PaletteComponent; import robotbuilder.data.RobotComponent; import robotbuilder.palette.Palette; /** * A transfer handler for that wraps the default transfer handler of RobotTree. * * @author Alex Henning * @author Sam Carlberg */ class TreeTransferHandler extends TransferHandler { private TransferHandler delegate; private RobotTree robotTree; public TreeTransferHandler(TransferHandler delegate, RobotTree robotTree) { this.delegate = delegate; this.robotTree = robotTree; } @Override public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) { return delegate.canImport(comp, transferFlavors); } @Override public boolean canImport(TransferSupport support) { if (!support.isDrop()) { return false; } support.setShowDropLocation(true); JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation(); TreePath path = dl.getPath(); if (path == null) { return false; } RobotComponent target = (RobotComponent) path.getLastPathComponent(); if (support.getTransferable().isDataFlavorSupported(DataFlavor.stringFlavor)) { String data; try { data = (String) support.getTransferable().getTransferData(DataFlavor.stringFlavor); } catch (UnsupportedFlavorException | IOException e) { return false; } PaletteComponent base = Palette.getInstance().getItem(data); assert base != null; // TODO: Handle more gracefully return target.supports(base); } else if (support.getTransferable().isDataFlavorSupported(RobotTree.ROBOT_COMPONENT_FLAVOR)) { RobotComponent data; try { data = (RobotComponent) support.getTransferable().getTransferData(RobotTree.ROBOT_COMPONENT_FLAVOR); } catch (UnsupportedFlavorException e) { System.out.println("UnsupportedFlavor"); return false; } catch (IOException e) { e.printStackTrace(); System.out.println("IOException"); return false; } if (dl.getChildIndex() == -1 && target.getChildren().contains(data)) { // Cannot drag something into its own parent // (This allows for reordering) return false; } Set<String> invalid = new HashSet(); invalid.add("Robot"); invalid.add("Subsystems"); invalid.add("OI"); invalid.add("Commands"); if (data == null) { return false; } if (invalid.contains(data.getBase().getType())) { return false; } return target.supports(data); } else { System.out.println("Unsupported flavor. The flavor you have chosen is not sufficiently delicious."); return false; } } @Override protected Transferable createTransferable(final JComponent c) { return new Transferable() { DataFlavor[] flavors = {RobotTree.ROBOT_COMPONENT_FLAVOR}; @Override public DataFlavor[] getTransferDataFlavors() { return flavors; } @Override public boolean isDataFlavorSupported(DataFlavor df) { for (DataFlavor flavor : flavors) { if (flavor.equals(df)) { return true; } } return false; } @Override public Object getTransferData(DataFlavor df) { return robotTree.getDndData(); } }; } @Override public void exportAsDrag(JComponent comp, InputEvent event, int action) { delegate.exportAsDrag(comp, event, action); } @Override protected void exportDone(JComponent source, Transferable data, int action) { robotTree.update(); robotTree.selectEditingComponent(); } @Override public int getSourceActions(JComponent c) { //return COPY_OR_MOVE; return delegate.getSourceActions(c); } @Override public Icon getVisualRepresentation(Transferable t) { return delegate.getVisualRepresentation(t); } @Override public boolean importData(JComponent comp, Transferable t) { return delegate.importData(comp, t); } /** * Handles DnD from the palette to the tree or reordering of components in * the tree. */ @Override @SneakyThrows public boolean importData(TransferHandler.TransferSupport support) { if (!canImport(support)) { return false; } JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation(); TreePath path = dl.getPath(); int childIndex = dl.getChildIndex(); if (childIndex == -1) { childIndex = robotTree.tree.getModel().getChildCount(path.getLastPathComponent()); } RobotComponent parentNode = (RobotComponent) path.getLastPathComponent(); RobotComponent newNode; if (support.getTransferable().isDataFlavorSupported(DataFlavor.stringFlavor)) { String data; try { data = (String) support.getTransferable().getTransferData(DataFlavor.stringFlavor); } catch (UnsupportedFlavorException e) { System.out.println("UFE"); return false; } catch (IOException e) { System.out.println("IOE"); return false; } PaletteComponent base = Palette.getInstance().getItem(data); if (base == null) { // very unlikely, but notify the user anyway JOptionPane.showMessageDialog(robotTree, "A robot component could not be found for the pallet item " + base, "", JOptionPane.ERROR_MESSAGE); return false; } newNode = new RobotComponent(robotTree.getDefaultComponentName(base, ((RobotComponent) parentNode).getSubsystem()), base, robotTree); } else if (support.getTransferable().isDataFlavorSupported(RobotTree.ROBOT_COMPONENT_FLAVOR)) { try { newNode = (RobotComponent) support.getTransferable().getTransferData(RobotTree.ROBOT_COMPONENT_FLAVOR); } catch (UnsupportedFlavorException | IOException e) { return false; } } else { return false; } if (!parentNode.getChildren().contains(newNode) && robotTree.getComponentByName(newNode.getFullName()) != null) { // If a component is dragged from one folder to another (e.g. between subsystems), // DnD will not remove it from the tree, so we have to do it manually robotTree.delete(newNode); robotTree.update(); } robotTree.treeModel.insertNodeInto(newNode, parentNode, childIndex); robotTree.treeModel.reload(parentNode); // reloads the tree without reverting to the root robotTree.update(); robotTree.tree.makeVisible(path.pathByAddingChild(newNode)); robotTree.selectRobotComponent(newNode); robotTree.tree.setSelectionPath(path.pathByAddingChild(newNode)); robotTree.properties.setCurrentComponent(newNode); robotTree.properties.setEditName(); robotTree.tree.scrollRectToVisible(robotTree.tree.getPathBounds(path.pathByAddingChild(newNode))); robotTree.takeSnapshot(); return true; } }