Package org.jgraph.graph

Source Code of org.jgraph.graph.GraphTransferHandler

/*
* @(#)GraphTransferHandler.java  1.0 31-DEC-04
*
* Copyright (c) 2001-2004 Gaudenz Alder
*/
package org.jgraph.graph;

import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import javax.swing.JComponent;
import javax.swing.TransferHandler;

import org.jgraph.JGraph;

/**
* @author Gaudenz Alder
*
* Default datatransfer handler.
*/
public class GraphTransferHandler extends TransferHandler {

  /**
   * Controls if all inserts should be handled as external drops even if all
   * cells are already in the graph model. This is useful if the enclosing
   * component does not allow moving.
   */
  protected boolean alwaysReceiveAsCopyAction = false;

  /* Pointer to the last inserted array of cells. */
  protected Object out, in;

  /* How many times the last transferable was inserted. */
  protected int inCount = 0;

  public boolean canImport(JComponent comp, DataFlavor[] flavors) {
    for (int i = 0; i < flavors.length; i++)
      if (flavors[i] == GraphTransferable.dataFlavor)
        return true;
    return false;
  }

  /* Public entry point to create a Transferable. */
  public Transferable createTransferableForGraph(JGraph graph) {
    return createTransferable(graph);
  }

  protected Transferable createTransferable(JComponent c) {
    if (c instanceof JGraph) {
      JGraph graph = (JGraph) c;
      if (!graph.isSelectionEmpty()) {
        return createTransferable(graph, graph.getDescendants(graph
            .order(graph.getSelectionCells())));
      }
    }
    return null;
  }

  protected Transferable createTransferable(JGraph graph, Object[] cells) {
    Object[] flat = graph.getDescendants(graph.order(cells));
    ParentMap pm = ParentMap.create(graph.getModel(), flat, false, true);
    ConnectionSet cs = ConnectionSet.create(graph.getModel(), flat, false);
    Map viewAttributes = GraphConstants.createAttributes(flat, graph
        .getGraphLayoutCache());
    Rectangle2D bounds = graph.getCellBounds(graph.getSelectionCells());
    bounds = new AttributeMap.SerializableRectangle2D(bounds.getX(), bounds
        .getY(), bounds.getWidth(), bounds.getHeight());
    out = flat;
    return create(graph, flat, viewAttributes, bounds, cs, pm);
  }

  protected GraphTransferable create(JGraph graph, Object[] cells,
      Map viewAttributes, Rectangle2D bounds, ConnectionSet cs,
      ParentMap pm) {
    return new GraphTransferable(cells, viewAttributes, bounds, cs, pm);
  }

  protected void exportDone(JComponent comp, Transferable data, int action) {
    if (comp instanceof JGraph && data instanceof GraphTransferable) {
      JGraph graph = (JGraph) comp;
      if (action == TransferHandler.MOVE) {
        Object[] cells = ((GraphTransferable) data).getCells();
        graph.getGraphLayoutCache().remove(cells);
      }
      graph.getUI().updateHandle();
      graph.getUI().setInsertionLocation(null);
    }
  }

  public int getSourceActions(JComponent c) {
    return COPY_OR_MOVE;
  }

  // NOTE: 1. We abuse return value to signal removal to the sender.
  // 2. We always clone cells when transferred between two models
  // This is because they contain parts of the model's data.
  // 3. Transfer is passed to importDataImpl for unsupported
  // dataflavors (becaue method may return false, see 1.)
  public boolean importData(JComponent comp, Transferable t) {
    try {
      if (comp instanceof JGraph) {
        JGraph graph = (JGraph) comp;
        GraphModel model = graph.getModel();
        GraphLayoutCache cache = graph.getGraphLayoutCache();
        if (t.isDataFlavorSupported(GraphTransferable.dataFlavor)
            && graph.isEnabled()) {
          // May be null
          Point p = graph.getUI().getInsertionLocation();

          // Get Local Machine Flavor
          Object obj = t
              .getTransferData(GraphTransferable.dataFlavor);
          GraphTransferable gt = (GraphTransferable) obj;

          // Get Transferred Cells
          Object[] cells = gt.getCells();

          // Check if all cells are in the model
          boolean allInModel = true;
          for (int i = 0; i < cells.length && allInModel; i++)
            allInModel = allInModel && model.contains(cells[i]);

          // Count repetitive inserts
          if (in == cells)
            inCount++;
          else
            inCount = (allInModel) ? 1 : 0;
          in = cells;

          // Delegate to handle
          if (p != null && in == out
              && graph.getUI().getHandle() != null) {
            int mod = (graph.getUI().getDropAction() == TransferHandler.COPY) ? InputEvent.CTRL_MASK
                : 0;
            graph.getUI().getHandle().mouseReleased(
                new MouseEvent(comp, 0, 0, mod, p.x, p.y, 1,
                    false));
            return false;
          }

          // Get more Transfer Data
          Rectangle2D bounds = gt.getBounds();
          Map nested = gt.getAttributeMap();
          ConnectionSet cs = gt.getConnectionSet();
          ParentMap pm = gt.getParentMap();

          // Move across models or via clipboard always clones
          if (!allInModel
              || p == null
              || alwaysReceiveAsCopyAction
              || graph.getUI().getDropAction() == TransferHandler.COPY) {

            // Translate cells
            double dx = 0, dy = 0;

            // Cloned via Drag and Drop
            if (nested != null) {
              if (p != null && bounds != null) {
                Point2D insert = graph.fromScreen(graph
                    .snap((Point2D) p.clone()));
                dx = insert.getX() - bounds.getX();
                dy = insert.getY() - bounds.getY();

                // Cloned via Clipboard
              } else {
                dx = inCount * graph.getGridSize();
                dy = inCount * graph.getGridSize();
              }
            }

            handleExternalDrop(graph, cells, nested, cs, pm, dx, dy);

            // Signal sender to remove only if moved between
            // different models
            return (graph.getUI().getDropAction() == TransferHandler.MOVE && !allInModel);
          }

          // We are dealing with a move across multiple views
          // of the same model
          else {

            // Moved via Drag and Drop
            if (p != null) {
              // Scale insertion location
              Point2D insert = graph.fromScreen(graph
                  .snap(new Point(p)));

              // Compute translation vector and translate all
              // attribute maps.
              if (bounds != null && nested != null) {
                double dx = insert.getX() - bounds.getX();
                double dy = insert.getY() - bounds.getY();
                AttributeMap.translate(nested.values(), dx, dy);
              } else if (bounds == null) {

                // Prevents overwriting view-local
                // attributes
                // for known cells. Note: This is because
                // if bounds is null, the caller wants
                // to signal that the bounds were
                // not available, which is typically the
                // case if no graph layout cache
                // is at hand. To avoid overriding the
                // local attributes such as the bounds
                // with the default bounds from the model,
                // we remove all attributes that travel
                // along with the transferable. (Since
                // all cells are already in the model
                // no information is lost by doing this.)
                double gs2 = 2 * graph.getGridSize();
                nested = new Hashtable();
                Map emptyMap = new Hashtable();
                for (int i = 0; i < cells.length; i++) {

                  // This also gives us the chance to
                  // provide useful default location and
                  // resize if there are no useful bounds
                  // that travel along with the cells.
                  if (!model.isEdge(cells[i])
                      && !model.isPort(cells[i])) {

                    // Check if there are useful bounds
                    // defined in the model, otherwise
                    // resize,
                    // because the view does not yet exist.
                    Rectangle2D tmp = graph
                        .getCellBounds(cells[i]);
                    if (tmp == null)
                      tmp = GraphConstants
                          .getBounds(model
                              .getAttributes(cells[i]));

                    // Clone the rectangle to force a
                    // repaint
                    if (tmp != null)
                      tmp = (Rectangle2D) tmp.clone();

                    Hashtable attrs = new Hashtable();
                    Object parent = model
                        .getParent(cells[i]);
                    if (tmp == null) {
                      tmp = new Rectangle2D.Double(p
                          .getX(), p.getY(), gs2 / 2,
                          gs2);
                      GraphConstants.setResize(attrs,
                          true);

                      // Shift
                      p.setLocation(p.getX() + gs2, p
                          .getY()
                          + gs2);
                      graph.snap(p);
                      // If parent processed then childs
                      // are already located
                    } else if (parent == null
                        || !nested
                            .keySet()
                            .contains(
                                model
                                    .getParent(cells[i]))) {
                      CellView view = graph
                          .getGraphLayoutCache()
                          .getMapping(cells[i], false);
                      if (view != null && !view.isLeaf()) {
                        double dx = p.getX()
                            - tmp.getX();
                        double dy = p.getY()
                            - tmp.getY();
                        GraphLayoutCache
                            .translateViews(
                                new CellView[] { view },
                                dx, dy);
                      } else {
                        tmp.setFrame(p.getX(),
                            p.getY(), tmp
                                .getWidth(),
                            tmp.getHeight());
                      }

                      // Shift
                      p.setLocation(p.getX() + gs2, p
                          .getY()
                          + gs2);
                      graph.snap(p);
                    }
                    GraphConstants.setBounds(attrs, tmp);
                    nested.put(cells[i], attrs);
                  } else {
                    nested.put(cells[i], emptyMap);
                  }
                }
              }

              // Edit cells (and make visible)
              cache.edit(nested, null, null, null);
            }

            // Select topmost cells in group-structure
            graph.setSelectionCells(DefaultGraphModel
                .getTopmostCells(model, cells));

            // Don't remove at sender
            return false;
          }
        } else
          return importDataImpl(comp, t);
      }
    } catch (Exception exception) {
      // System.err.println("Cannot import: " +
      // exception.getMessage());
      exception.printStackTrace();
    }
    return false;
  }

  protected void handleExternalDrop(JGraph graph, Object[] cells, Map nested,
      ConnectionSet cs, ParentMap pm, double dx, double dy) {

    // Removes all connections for which the port is neither
    // passed in the parent map nor already in the model.
    Iterator it = cs.connections();
    while (it.hasNext()) {
      ConnectionSet.Connection conn = (ConnectionSet.Connection) it
          .next();
      if (!pm.getChangedNodes().contains(conn.getPort())
          && !graph.getModel().contains(conn.getPort())) {
        it.remove();
      }
    }
    Map clones = graph.cloneCells(cells);
    graph.getGraphLayoutCache().insertClones(cells, clones, nested, cs, pm,
        dx, dy);
  }

  // For subclassers if above does not handle the insertion
  protected boolean importDataImpl(JComponent comp, Transferable t) {
    return false;
  }

  /**
   * @return Returns the alwaysReceiveAsCopyAction.
   */
  public boolean isAlwaysReceiveAsCopyAction() {
    return alwaysReceiveAsCopyAction;
  }

  /**
   * @param alwaysReceiveAsCopyAction
   *            The alwaysReceiveAsCopyAction to set.
   */
  public void setAlwaysReceiveAsCopyAction(boolean alwaysReceiveAsCopyAction) {
    this.alwaysReceiveAsCopyAction = alwaysReceiveAsCopyAction;
  }

}
TOP

Related Classes of org.jgraph.graph.GraphTransferHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.