Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package be.ugent.maf.cellmissy.gui.controller.load.generic; import be.ugent.maf.cellmissy.entity.Algorithm; import be.ugent.maf.cellmissy.entity.ImagingType; import be.ugent.maf.cellmissy.entity.WellHasImagingType; import be.ugent.maf.cellmissy.gui.plate.ImagedPlatePanel; import be.ugent.maf.cellmissy.gui.plate.WellGui; import java.awt.Cursor; import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetAdapter; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetListener; import java.awt.geom.Ellipse2D; import java.io.File; import java.io.IOException; import java.util.List; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import org.apache.log4j.Logger; import org.jdesktop.observablecollections.ObservableList; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * A controller to take care of the DnD logic between a JTree (directory tree -- * source, drag) and an ImagedPlatePanel (wellGuis -- target, drop). * * @author Paola */ @SuppressWarnings("MagicConstant") @Component("dragAndDropController") public class DragAndDropController { private static final Logger LOG = Logger.getLogger(DragAndDropController.class); // model private ImagingType currentImagingType; private Algorithm currentAlgorithm; // view // parent controller @Autowired private GenericImagedPlateController genericImagedPlateController; // child controllers /** * Initialize controller */ public void init() { // init the Drag And Drop logic initDnD(); } /** * Public methods */ public ImagingType getCurrentImagingType() { return currentImagingType; } public Algorithm getCurrentAlgorithm() { return currentAlgorithm; } /** * Private methods and classes */ /** * Initialize the DnD */ private void initDnD() { // create a new Drag Source DragSource ds = new DragSource(); // get the directory tree from the parent controller JTree directoryTree = genericImagedPlateController.getLoadFromGenericInputPlatePanel().getDirectoryTree(); // get the JPanel from the parent controller ImagedPlatePanel imagedPlatePanel = genericImagedPlateController.getImagedPlatePanel(); // the DRAG gesture (source -- JTree) -- needs a DragGestureListener to notify ds.createDefaultDragGestureRecognizer(directoryTree, DnDConstants.ACTION_COPY, new JTreeDragGestureListener()); // the DROP action onto the target JPanelDropTargetListener jPanelDropTargetListener = new JPanelDropTargetListener(imagedPlatePanel); } /** * An implementation of the DragGestureListener to notify the drag source. */ private class JTreeDragGestureListener implements DragGestureListener { @Override public void dragGestureRecognized(DragGestureEvent dge) { Cursor cursor = null; // our component that triggered the drag event is a JTree JTree directoryTree = (JTree) dge.getComponent(); // prevents any sort of strange behaviour (combination with keyboard and so on) if (dge.getDragAction() == DnDConstants.ACTION_COPY) { cursor = DragSource.DefaultCopyDrop; } // the selected element in the JTree DefaultMutableTreeNode draggedNode = (DefaultMutableTreeNode) directoryTree.getSelectionPath() .getLastPathComponent(); // set the combination of current imaging type - algorithm setCombinationImagingTypeAlgorithm(draggedNode); // initiate a drag operation dge.startDrag(cursor, new TransferableNode(draggedNode)); } } /** * An implementation of the Transferable interface. It contains data to be * transferred during drag and drop operations. */ private class TransferableNode implements Transferable { // the selected node whose corresponding file needs to be 'transferred' over the plate private final DefaultMutableTreeNode selectedNode; /** * The constructor * * @param selectedNode */ public TransferableNode(DefaultMutableTreeNode selectedNode) { this.selectedNode = selectedNode; } @Override public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[] { DataFlavor.stringFlavor }; } @Override public boolean isDataFlavorSupported(DataFlavor flavor) { return flavor.equals(DataFlavor.stringFlavor); } @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (flavor.equals(DataFlavor.stringFlavor)) { return selectedNode; } else { throw new UnsupportedFlavorException(flavor); } } } /** * An implementation of the drop target listener --> what needs to be done * at the drop phase? */ private class JPanelDropTargetListener extends DropTargetAdapter implements DropTargetListener { // we need to connect the target with a component final DropTarget dropTarget; // in our case, the component is a JPanel /** * The constructor * * @param panel: the drop target */ public JPanelDropTargetListener(JPanel panel) { // construct a new drop target dropTarget = new DropTarget(panel, DnDConstants.ACTION_COPY, this, true); } @Override public void drop(DropTargetDropEvent event) { try { Transferable tr = event.getTransferable(); DefaultMutableTreeNode node = (DefaultMutableTreeNode) tr.getTransferData(DataFlavor.stringFlavor); // check that the flavor is supported if (event.isDataFlavorSupported(DataFlavor.stringFlavor)) { event.acceptDrop(DnDConstants.ACTION_COPY); // the drop location Point location = event.getLocation(); // action on drop actionOnDrop(location, node); // drop is complete event.dropComplete(true); return; } event.rejectDrop(); } catch (UnsupportedFlavorException | IOException e) { event.rejectDrop(); } } } /** * Action on drop onto the target component: 1. get the wellGui * correspondent to the drop-point location; 2. validate this wellGui; 3. * check if the well has already some data. If this is not the case, just * load new data into it, otherwise, ask the user how to proceed. In this * last case, 3 things are possible: 1. Overwrite the data (drag&drop was * wrong, e;g.); 2. Clear data for the well (just reset the well); 3. Add * more data to the well (same combination of algorithm-imaging type: a new * location). * * @param point * @param node */ private void actionOnDrop(Point point, DefaultMutableTreeNode node) { WellGui wellGuiDropTarget = getWellGuiDropTarget(point); if (wellGuiDropTarget != null) { if (validateDropTarget(wellGuiDropTarget)) { // new wellHasImagingType (for selected well and current imaging type/algorithm) WellHasImagingType newWellHasImagingType = new WellHasImagingType(wellGuiDropTarget.getWell(), currentImagingType, currentAlgorithm); // get the list of WellHasImagingType for the selected well List<WellHasImagingType> wellHasImagingTypeList = wellGuiDropTarget.getWell() .getWellHasImagingTypeList(); // check if the wellHasImagingType was already processed // this is comparing objects with column, row numbers, and algorithm,imaging types if (!wellHasImagingTypeList.contains(newWellHasImagingType)) { genericImagedPlateController.loadData(getDataFile(node), newWellHasImagingType, wellGuiDropTarget); // update relation with algorithm and imaging type currentAlgorithm.getWellHasImagingTypeList().add(newWellHasImagingType); currentImagingType.getWellHasImagingTypeList().add(newWellHasImagingType); // highlight imaged well highlightImagedWell(wellGuiDropTarget); } else { // warn the user that data was already loaded for the selected combination of well/dataset/imaging type Object[] options = { "Overwrite", "Add location on same well", "Clear data for this well", "Cancel" }; int showOptionDialog = JOptionPane.showOptionDialog(null, "Data already loaded for this well / dataset / imaging type.\nWhat do you want to do now?", "", JOptionPane.CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[3]); switch (showOptionDialog) { case 0: // overwrite loaded data: genericImagedPlateController.overwriteDataForWell(getDataFile(node), wellGuiDropTarget, newWellHasImagingType); break; case 1: // add location on the same well: genericImagedPlateController.loadData(getDataFile(node), newWellHasImagingType, wellGuiDropTarget); break; case 2: // clear data for current well genericImagedPlateController.clearDataForWell(wellGuiDropTarget); break; //cancel: do nothing } } } else { //show a warning message String message = "The well you selected does not belong to a condition.\nPlease drag somewhere else!"; genericImagedPlateController.showMessage(message, "Well's selection error", JOptionPane.WARNING_MESSAGE); } } } /** * Highlight the imaged well for which we are importing data. * * @param selectedWellGui */ private void highlightImagedWell(WellGui selectedWellGui) { List<Ellipse2D> ellipsi = selectedWellGui.getEllipsi(); List<WellHasImagingType> wellHasImagingTypeList = selectedWellGui.getWell().getWellHasImagingTypeList(); ImagedPlatePanel imagedPlatePanel = genericImagedPlateController.getImagedPlatePanel(); List<ImagingType> uniqueImagingTypes = imagedPlatePanel.getUniqueImagingTypes(wellHasImagingTypeList); // if size is one, only one imaging type was processed: do not add eny ellipsi if (uniqueImagingTypes.size() != 1) { if (ellipsi.size() < uniqueImagingTypes.size()) { int lastIndex = uniqueImagingTypes.size() - 2; Ellipse2D lastEllipse = ellipsi.get(lastIndex); // calculate factors for new ellipse double size = lastEllipse.getHeight(); double newSize = (size / uniqueImagingTypes.size()); double newTopLeftX = lastEllipse.getCenterX() - (newSize / 2); double newTopLeftY = lastEllipse.getCenterY() - (newSize / 2); if (newSize != size) { Ellipse2D ellipseToAdd = new Ellipse2D.Double(newTopLeftX, newTopLeftY, newSize, newSize); // add the new Ellipse2D to the ellipsi List ellipsi.add(ellipseToAdd); } } } imagedPlatePanel.repaint(); } /** * Taking the directory of the parent controller, and knowing the current * combination imaging type-algorithm, get the data file to parse. * * @param node * @return */ private File getDataFile(DefaultMutableTreeNode node) { String directoryPath = genericImagedPlateController.getDirectory().getAbsolutePath(); String textFile = "" + node.getUserObject(); return new File(directoryPath + File.separator + currentAlgorithm + File.separator + currentImagingType + File.separator + textFile); } /** * Given the node being dragged, * * @param node */ private void setCombinationImagingTypeAlgorithm(DefaultMutableTreeNode draggedNode) { // look for imaging type selected ObservableList<ImagingType> imagingTypesBindingList = genericImagedPlateController .getImagingTypesBindingList(); for (ImagingType imagingType : imagingTypesBindingList) { if (imagingType.getName().equals(draggedNode.getParent().toString())) { // imaging type that was selected currentImagingType = imagingType; // look for associated algorithm currentAlgorithm = findAlgorithm(draggedNode); } } } /** * Given an imaging node, find the upper dataset * * @param draggedNode * @return */ private Algorithm findAlgorithm(DefaultMutableTreeNode draggedNode) { ObservableList<Algorithm> algorithmsBindingList = genericImagedPlateController.getAlgorithmsBindingList(); Algorithm foundAlgorithm = null; DefaultMutableTreeNode algoNode = (DefaultMutableTreeNode) draggedNode.getParent().getParent(); for (Algorithm algorithm : algorithmsBindingList) { if (algorithm.getAlgorithmName().equals(algoNode.toString())) { foundAlgorithm = algorithm; } } return foundAlgorithm; } /** * Given a location (point), get the wellGui correspondent on the plate * panel. * * @param point * @return the wellGui correspondent */ private WellGui getWellGuiDropTarget(Point point) { WellGui wellGuiDropTarget = null; for (WellGui wellGui : genericImagedPlateController.getImagedPlatePanel().getWellGuiList()) { List<Ellipse2D> ellipsi = wellGui.getEllipsi(); if (ellipsi.get(0).contains(point.getX(), point.getY())) { wellGuiDropTarget = wellGui; break; } } return wellGuiDropTarget; } /** * * @param wellGui * @return */ private boolean validateDropTarget(WellGui wellGui) { boolean isSelectionValid = true; //check if the imaged wellGui has a condition if (wellGui.getRectangle() == null) { isSelectionValid = false; } return isSelectionValid; } }