/** * $Id: mxGraphTransferHandler.java,v 1.1 2012/11/15 13:26:44 gaudenz Exp $ * Copyright (c) 2008, Gaudenz Alder */ package com.mxgraph.swing.handler; import java.awt.Color; import java.awt.Image; import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.TransferHandler; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.swing.util.mxGraphTransferable; import com.mxgraph.util.mxCellRenderer; import com.mxgraph.util.mxPoint; import com.mxgraph.util.mxRectangle; import com.mxgraph.view.mxGraph; /** * */ public class mxGraphTransferHandler extends TransferHandler { /** * */ private static final long serialVersionUID = -6443287704811197675L; /** * Boolean that specifies if an image of the cells should be created for * each transferable. Default is true. */ public static boolean DEFAULT_TRANSFER_IMAGE_ENABLED = true; /** * Specifies the background color of the transfer image. If no * color is given here then the background color of the enclosing * graph component is used. Default is Color.WHITE. */ public static Color DEFAULT_BACKGROUNDCOLOR = Color.WHITE; /** * Reference to the original cells for removal after a move. */ protected Object[] originalCells; /** * Reference to the last imported cell array. */ protected Transferable lastImported; /** * Sets the value for the initialImportCount. Default is 1. Updated in * exportDone to contain 0 after a cut and 1 after a copy. */ protected int initialImportCount = 1; /** * Counter for the last imported cell array. */ protected int importCount = 0; /** * Specifies if a transfer image should be created for the transferable. * Default is DEFAULT_TRANSFER_IMAGE. */ protected boolean transferImageEnabled = DEFAULT_TRANSFER_IMAGE_ENABLED; /** * Specifies the background color for the transfer image. Default is * DEFAULT_BACKGROUNDCOLOR. */ protected Color transferImageBackground = DEFAULT_BACKGROUNDCOLOR; /** * */ protected Point location; /** * */ protected Point offset; /** * */ public int getImportCount() { return importCount; } /** * */ public void setImportCount(int value) { importCount = value; } /** * */ public void setTransferImageEnabled(boolean transferImageEnabled) { this.transferImageEnabled = transferImageEnabled; } /** * */ public boolean isTransferImageEnabled() { return this.transferImageEnabled; } /** * */ public void setTransferImageBackground(Color transferImageBackground) { this.transferImageBackground = transferImageBackground; } /** * */ public Color getTransferImageBackground() { return this.transferImageBackground; } /** * Returns true if the DnD operation started from this handler. */ public boolean isLocalDrag() { return originalCells != null; } /** * */ public void setLocation(Point value) { location = value; } /** * */ public void setOffset(Point value) { offset = value; } /** * */ public boolean canImport(JComponent comp, DataFlavor[] flavors) { for (int i = 0; i < flavors.length; i++) { if (flavors[i] != null && flavors[i].equals(mxGraphTransferable.dataFlavor)) { return true; } } return false; } /** * (non-Javadoc) * * @see javax.swing.TransferHandler#createTransferable(javax.swing.JComponent) */ public Transferable createTransferable(JComponent c) { if (c instanceof mxGraphComponent) { mxGraphComponent graphComponent = (mxGraphComponent) c; mxGraph graph = graphComponent.getGraph(); if (!graph.isSelectionEmpty()) { originalCells = graphComponent.getExportableCells(graph .getSelectionCells()); if (originalCells.length > 0) { ImageIcon icon = (transferImageEnabled) ? createTransferableImage( graphComponent, originalCells) : null; return createGraphTransferable(graphComponent, originalCells, icon); } } } return null; } /** * */ public mxGraphTransferable createGraphTransferable( mxGraphComponent graphComponent, Object[] cells, ImageIcon icon) { mxGraph graph = graphComponent.getGraph(); mxPoint tr = graph.getView().getTranslate(); double scale = graph.getView().getScale(); mxRectangle bounds = graph.getPaintBounds(cells); // Removes the scale and translation from the bounds bounds.setX(bounds.getX() / scale - tr.getX()); bounds.setY(bounds.getY() / scale - tr.getY()); bounds.setWidth(bounds.getWidth() / scale); bounds.setHeight(bounds.getHeight() / scale); return createGraphTransferable(graphComponent, cells, bounds, icon); } /** * */ public mxGraphTransferable createGraphTransferable( mxGraphComponent graphComponent, Object[] cells, mxRectangle bounds, ImageIcon icon) { return new mxGraphTransferable(graphComponent.getGraph().cloneCells( cells), bounds, icon); } /** * */ public ImageIcon createTransferableImage(mxGraphComponent graphComponent, Object[] cells) { ImageIcon icon = null; Color bg = (transferImageBackground != null) ? transferImageBackground : graphComponent.getBackground(); Image img = mxCellRenderer.createBufferedImage( graphComponent.getGraph(), cells, 1, bg, graphComponent.isAntiAlias(), null, graphComponent.getCanvas()); if (img != null) { icon = new ImageIcon(img); } return icon; } /** * */ public void exportDone(JComponent c, Transferable data, int action) { initialImportCount = 1; if (c instanceof mxGraphComponent && data instanceof mxGraphTransferable) { // Requires that the graph handler resets the location to null if the drag leaves the // component. This is the condition to identify a cross-component move. boolean isLocalDrop = location != null; if (action == TransferHandler.MOVE && !isLocalDrop) { removeCells((mxGraphComponent) c, originalCells); initialImportCount = 0; } } originalCells = null; location = null; offset = null; } /** * */ protected void removeCells(mxGraphComponent graphComponent, Object[] cells) { graphComponent.getGraph().removeCells(cells); } /** * */ public int getSourceActions(JComponent c) { return COPY_OR_MOVE; } /** * Checks if the mxGraphTransferable data flavour is supported and calls * importGraphTransferable if possible. */ public boolean importData(JComponent c, Transferable t) { boolean result = false; if (isLocalDrag()) { // Enables visual feedback on the Mac result = true; } else { try { updateImportCount(t); if (c instanceof mxGraphComponent) { mxGraphComponent graphComponent = (mxGraphComponent) c; if (graphComponent.isEnabled() && t.isDataFlavorSupported(mxGraphTransferable.dataFlavor)) { mxGraphTransferable gt = (mxGraphTransferable) t .getTransferData(mxGraphTransferable.dataFlavor); if (gt.getCells() != null) { result = importGraphTransferable(graphComponent, gt); } } } } catch (Exception ex) { ex.printStackTrace(); } } return result; } /** * Counts the number of times that the given transferable has been imported. */ protected void updateImportCount(Transferable t) { if (lastImported != t) { importCount = initialImportCount; } else { importCount++; } lastImported = t; } /** * Returns true if the cells have been imported using importCells. */ protected boolean importGraphTransferable(mxGraphComponent graphComponent, mxGraphTransferable gt) { boolean result = false; try { mxGraph graph = graphComponent.getGraph(); double scale = graph.getView().getScale(); mxRectangle bounds = gt.getBounds(); double dx = 0, dy = 0; // Computes the offset for the placement of the imported cells if (location != null && bounds != null) { mxPoint translate = graph.getView().getTranslate(); dx = location.getX() - (bounds.getX() + translate.getX()) * scale; dy = location.getY() - (bounds.getY() + translate.getY()) * scale; // Keeps the cells aligned to the grid dx = graph.snap(dx / scale); dy = graph.snap(dy / scale); } else { int gs = graph.getGridSize(); dx = importCount * gs; dy = importCount * gs; } if (offset != null) { dx += offset.x; dy += offset.y; } importCells(graphComponent, gt, dx, dy); location = null; offset = null; result = true; // Requests the focus after an import graphComponent.requestFocus(); } catch (Exception e) { e.printStackTrace(); } return result; } /** * Returns the drop target for the given transferable and location. */ protected Object getDropTarget(mxGraphComponent graphComponent, mxGraphTransferable gt) { Object[] cells = gt.getCells(); Object target = null; // Finds the target cell at the given location and checks if the // target is not already the parent of the first imported cell if (location != null) { target = graphComponent.getGraph().getDropTarget(cells, location, graphComponent.getCellAt(location.x, location.y)); if (cells.length > 0 && graphComponent.getGraph().getModel().getParent(cells[0]) == target) { target = null; } } return target; } /** * Gets a drop target using getDropTarget and imports the cells using * mxGraph.splitEdge or mxGraphComponent.importCells depending on the * drop target and the return values of mxGraph.isSplitEnabled and * mxGraph.isSplitTarget. Selects and returns the cells that have been * imported. */ protected Object[] importCells(mxGraphComponent graphComponent, mxGraphTransferable gt, double dx, double dy) { Object target = getDropTarget(graphComponent, gt); mxGraph graph = graphComponent.getGraph(); Object[] cells = gt.getCells(); cells = graphComponent.getImportableCells(cells); if (graph.isSplitEnabled() && graph.isSplitTarget(target, cells)) { graph.splitEdge(target, cells, dx, dy); } else { cells = graphComponent.importCells(cells, dx, dy, target, location); graph.setSelectionCells(cells); } return cells; } }