Package com.mxgraph.swing.handler

Source Code of com.mxgraph.swing.handler.mxGraphHandler

/**
* $Id: mxGraphHandler.java,v 1.72 2011-05-07 09:46:58 gaudenz Exp $
* Copyright (c) 2008, Gaudenz Alder
*
* Known issue: Drag image size depends on the initial position and may sometimes
* not align with the grid when dragging. This is because the rounding of the width
* and height at the initial position may be different than that at the current
* position as the left and bottom side of the shape must align to the grid lines.
*/
package com.mxgraph.swing.handler;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceAdapter;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.TooManyListenersException;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;

import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.util.mxGraphTransferable;
import com.mxgraph.swing.util.mxMouseAdapter;
import com.mxgraph.util.mxCellRenderer;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxEventSource.mxIEventListener;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraph;

public class mxGraphHandler extends mxMouseAdapter implements
    DropTargetListener
{

  /**
   *
   */
  private static final long serialVersionUID = 3241109976696510225L;

  /**
   * Default is Cursor.DEFAULT_CURSOR.
   */
  public static Cursor DEFAULT_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);

  /**
   * Default is Cursor.MOVE_CURSOR.
   */
  public static Cursor MOVE_CURSOR = new Cursor(Cursor.MOVE_CURSOR);

  /**
   * Default is Cursor.HAND_CURSOR.
   */
  public static Cursor FOLD_CURSOR = new Cursor(Cursor.HAND_CURSOR);

  /**
   * Reference to the enclosing graph component.
   */
  protected mxGraphComponent graphComponent;

  /**
   * Specifies if the handler is enabled. Default is true.
   */
  protected boolean enabled = true;

  /**
   * Specifies if cloning by control-drag is enabled. Default is true.
   */
  protected boolean cloneEnabled = true;

  /**
   * Specifies if moving is enabled. Default is true.
   */
  protected boolean moveEnabled = true;

  /**
   * Specifies if moving is enabled. Default is true.
   */
  protected boolean selectEnabled = true;

  /**
   * Specifies if the cell marker should be called (for splitting edges and
   * dropping cells into groups). Default is true.
   */
  protected boolean markerEnabled = true;

  /**
   * Specifies if cells may be moved out of their parents. Default is true.
   */
  protected boolean removeCellsFromParent = true;

  /**
   *
   */
  protected mxMovePreview movePreview;

  /**
   * Specifies if live preview should be used if possible. Default is false.
   */
  protected boolean livePreview = false;

  /**
   * Specifies if an image should be used for preview. Default is true.
   */
  protected boolean imagePreview = true;

  /**
   * Specifies if the preview should be centered around the mouse cursor if there
   * was no mouse click to define the offset within the shape (eg. drag from
   * external source). Default is true.
   */
  protected boolean centerPreview = true;

  /**
   * Specifies if this handler should be painted on top of all other components.
   * Default is true.
   */
  protected boolean keepOnTop = true;

  /**
   * Holds the cells that are being moved by this handler.
   */
  protected transient Object[] cells;

  /**
   * Holds the image that is being used for the preview.
   */
  protected transient ImageIcon dragImage;

  /**
   * Holds the start location of the mouse gesture.
   */
  protected transient Point first;

  /**
   *
   */
  protected transient Object cell;

  /**
   *
   */
  protected transient Object initialCell;

  /**
   *
   */
  protected transient Object[] dragCells;

  /**
   *
   */
  protected transient mxCellMarker marker;

  /**
   *
   */
  protected transient boolean canImport;

  /**
   * Scaled, translated bounds of the selection cells.
   */
  protected transient mxRectangle cellBounds;

  /**
   * Scaled, translated bounding box of the selection cells.
   */
  protected transient mxRectangle bbox;

  /**
   * Unscaled, untranslated bounding box of the selection cells.
   */
  protected transient mxRectangle transferBounds;

  /**
   *
   */
  protected transient boolean visible = false;

  /**
   *
   */
  protected transient Rectangle previewBounds = null;

  /**
   * Workaround for alt-key-state not correct in mouseReleased. Note: State
   * of the alt-key is not available during drag-and-drop.
   */
  private transient boolean gridEnabledEvent = false;

  /**
   * Workaround for shift-key-state not correct in mouseReleased.
   */
  protected transient boolean constrainedEvent = false;

  /**
   * Reference to the current drop target.
   */
  protected transient DropTarget currentDropTarget = null;

  /**
   *
   * @param graphComponent
   */
  public mxGraphHandler(final mxGraphComponent graphComponent)
  {
    this.graphComponent = graphComponent;
    marker = createMarker();
    movePreview = createMovePreview();

    // Installs the paint handler
    graphComponent.addListener(mxEvent.AFTER_PAINT, new mxIEventListener()
    {
      public void invoke(Object sender, mxEventObject evt)
      {
        Graphics g = (Graphics) evt.getProperty("g");
        paint(g);
      }
    });

    // Listens to all mouse events on the rendering control
    graphComponent.getGraphControl().addMouseListener(this);
    graphComponent.getGraphControl().addMouseMotionListener(this);

    // Drag target creates preview image
    installDragGestureHandler();

    // Listens to dropped graph cells
    installDropTargetHandler();
   
    // Listens to changes of the transferhandler
    graphComponent.addPropertyChangeListener(new PropertyChangeListener()
    {
     
      public void propertyChange(PropertyChangeEvent evt)
      {
        if (evt.getPropertyName().equals("transferHandler"))
        {
          if (currentDropTarget != null)
          {
            currentDropTarget.removeDropTargetListener(mxGraphHandler.this);
          }
         
          installDropTargetHandler();
        }
      }
    });

    setVisible(false);
  }

  /**
   *
   */
  protected void installDragGestureHandler()
  {
    DragGestureListener dragGestureListener = new DragGestureListener()
    {
      public void dragGestureRecognized(DragGestureEvent e)
      {
        if (graphComponent.isDragEnabled() && first != null)
        {
          final TransferHandler th = graphComponent
              .getTransferHandler();

          if (th instanceof mxGraphTransferHandler)
          {
            final mxGraphTransferable t = (mxGraphTransferable) ((mxGraphTransferHandler) th)
                .createTransferable(graphComponent);

            if (t != null)
            {
              e.startDrag(null, mxConstants.EMPTY_IMAGE,
                  new Point(), t, new DragSourceAdapter()
                  {

                    /**
                     *
                     */
                    public void dragDropEnd(
                        DragSourceDropEvent dsde)
                    {
                      ((mxGraphTransferHandler) th)
                          .exportDone(
                              graphComponent,
                              t,
                              TransferHandler.NONE);
                      first = null;
                    }
                  });
            }
          }
        }
      }
    };

    DragSource dragSource = new DragSource();
    dragSource.createDefaultDragGestureRecognizer(
        graphComponent.getGraphControl(),
        DnDConstants.ACTION_COPY_OR_MOVE, dragGestureListener);
  }

  /**
   *
   */
  protected void installDropTargetHandler()
  {
    DropTarget dropTarget = graphComponent.getDropTarget();

    try
    {
      if (dropTarget != null)
      {
        dropTarget.addDropTargetListener(this);
        currentDropTarget = dropTarget;
      }
    }
    catch (TooManyListenersException tmle)
    {
      // should not happen... swing drop target is multicast
    }
  }

  /**
   *
   */
  public boolean isVisible()
  {
    return visible;
  }

  /**
   *
   */
  public void setVisible(boolean value)
  {
    if (visible != value)
    {
      visible = value;

      if (previewBounds != null)
      {
        graphComponent.getGraphControl().repaint(previewBounds);
      }
    }
  }

  /**
   *
   */
  public void setPreviewBounds(Rectangle bounds)
  {
    if ((bounds == null && previewBounds != null)
        || (bounds != null && previewBounds == null)
        || (bounds != null && previewBounds != null && !bounds
            .equals(previewBounds)))
    {
      Rectangle dirty = null;

      if (isVisible())
      {
        dirty = previewBounds;

        if (dirty != null)
        {
          dirty.add(bounds);
        }
        else
        {
          dirty = bounds;
        }
      }

      previewBounds = bounds;

      if (dirty != null)
      {
        graphComponent.getGraphControl().repaint(dirty.x - 1,
            dirty.y - 1, dirty.width + 2, dirty.height + 2);
      }
    }
  }

  /**
   *
   */
  protected mxMovePreview createMovePreview()
  {
    return new mxMovePreview(graphComponent);
  }

  /**
   *
   */
  protected mxCellMarker createMarker()
  {
    mxCellMarker marker = new mxCellMarker(graphComponent, Color.BLUE)
    {
      /**
       *
       */
      private static final long serialVersionUID = -8451338653189373347L;

      /**
       *
       */
      public boolean isEnabled()
      {
        return graphComponent.getGraph().isDropEnabled();
      }

      /**
       *
       */
      public Object getCell(MouseEvent e)
      {
        TransferHandler th = graphComponent.getTransferHandler();
        boolean isLocal = th instanceof mxGraphTransferHandler
            && ((mxGraphTransferHandler) th).isLocalDrag();

        mxGraph graph = graphComponent.getGraph();
        Object cell = super.getCell(e);
        Object[] cells = (isLocal) ? graph.getSelectionCells()
            : dragCells;
        cell = graph.getDropTarget(cells, e.getPoint(), cell);
        boolean clone = graphComponent.isCloneEvent(e) && cloneEnabled;

        if (isLocal && cell != null && cells.length > 0 && !clone
            && graph.getModel().getParent(cells[0]) == cell)
        {
          cell = null;
        }

        return cell;
      }

    };

    // Swimlane content area will not be transparent drop targets
    marker.setSwimlaneContentEnabled(true);

    return marker;
  }

  /**
   *
   */
  public mxGraphComponent getGraphComponent()
  {
    return graphComponent;
  }

  /**
   *
   */
  public boolean isEnabled()
  {
    return enabled;
  }

  /**
   *
   */
  public void setEnabled(boolean value)
  {
    enabled = value;
  }

  /**
   *
   */
  public boolean isCloneEnabled()
  {
    return cloneEnabled;
  }

  /**
   *
   */
  public void setCloneEnabled(boolean value)
  {
    cloneEnabled = value;
  }

  /**
   *
   */
  public boolean isMoveEnabled()
  {
    return moveEnabled;
  }

  /**
   *
   */
  public void setMoveEnabled(boolean value)
  {
    moveEnabled = value;
  }

  /**
   *
   */
  public boolean isMarkerEnabled()
  {
    return markerEnabled;
  }

  /**
   *
   */
  public void setMarkerEnabled(boolean value)
  {
    markerEnabled = value;
  }

  /**
   *
   */
  public boolean isSelectEnabled()
  {
    return selectEnabled;
  }

  /**
   *
   */
  public void setSelectEnabled(boolean value)
  {
    selectEnabled = value;
  }

  /**
   *
   */
  public boolean isRemoveCellsFromParent()
  {
    return removeCellsFromParent;
  }

  /**
   *
   */
  public void setRemoveCellsFromParent(boolean value)
  {
    removeCellsFromParent = value;
  }

  /**
   *
   */
  public boolean isLivePreview()
  {
    return livePreview;
  }

  /**
   *
   */
  public void setLivePreview(boolean value)
  {
    livePreview = value;
  }

  /**
   *
   */
  public boolean isImagePreview()
  {
    return imagePreview;
  }

  /**
   *
   */
  public void setImagePreview(boolean value)
  {
    imagePreview = value;
  }

  /**
   *
   */
  public boolean isCenterPreview()
  {
    return centerPreview;
  }

  /**
   *
   */
  public void setCenterPreview(boolean value)
  {
    centerPreview = value;
  }

  /**
   *
   */
  public void updateDragImage(Object[] cells)
  {
    dragImage = null;

    if (cells != null && cells.length > 0)
    {
      Image img = mxCellRenderer.createBufferedImage(
          graphComponent.getGraph(), cells, graphComponent.getGraph()
              .getView().getScale(), null,
          graphComponent.isAntiAlias(), null,
          graphComponent.getCanvas());

      if (img != null)
      {
        dragImage = new ImageIcon(img);
        previewBounds.setSize(dragImage.getIconWidth(),
            dragImage.getIconHeight());
      }
    }
  }

  /**
   *
   */
  public void mouseMoved(MouseEvent e)
  {
    if (graphComponent.isEnabled() && isEnabled() && !e.isConsumed())
    {
      Cursor cursor = getCursor(e);

      if (cursor != null)
      {
        graphComponent.getGraphControl().setCursor(cursor);
        e.consume();
      }
      else
      {
        graphComponent.getGraphControl().setCursor(DEFAULT_CURSOR);
      }
    }
  }

  /**
   *
   */
  protected Cursor getCursor(MouseEvent e)
  {
    Cursor cursor = null;

    if (isMoveEnabled())
    {
      Object cell = graphComponent.getCellAt(e.getX(), e.getY(), false);

      if (cell != null)
      {
        if (graphComponent.isFoldingEnabled()
            && graphComponent.hitFoldingIcon(cell, e.getX(),
                e.getY()))
        {
          cursor = FOLD_CURSOR;
        }
        else if (graphComponent.getGraph().isCellMovable(cell))
        {
          cursor = MOVE_CURSOR;
        }
      }
    }

    return cursor;
  }

  /**
   *
   */
  public void dragEnter(DropTargetDragEvent e)
  {
    JComponent component = getDropTarget(e);
    TransferHandler th = component.getTransferHandler();
    boolean isLocal = th instanceof mxGraphTransferHandler
        && ((mxGraphTransferHandler) th).isLocalDrag();

    if (isLocal)
    {
      canImport = true;
    }
    else
    {
      canImport = graphComponent.isImportEnabled()
          && th.canImport(component, e.getCurrentDataFlavors());
    }

    if (canImport)
    {
      transferBounds = null;
      setVisible(false);

      try
      {
        Transferable t = e.getTransferable();

        if (t.isDataFlavorSupported(mxGraphTransferable.dataFlavor))
        {
          mxGraphTransferable gt = (mxGraphTransferable) t
              .getTransferData(mxGraphTransferable.dataFlavor);
          dragCells = gt.getCells();

          if (gt.getBounds() != null)
          {
            mxGraph graph = graphComponent.getGraph();
            double scale = graph.getView().getScale();
            transferBounds = gt.getBounds();
            int w = (int) Math.ceil((transferBounds.getWidth() + 1)
                * scale);
            int h = (int) Math
                .ceil((transferBounds.getHeight() + 1) * scale);
            setPreviewBounds(new Rectangle(
                (int) transferBounds.getX(),
                (int) transferBounds.getY(), w, h));

            if (imagePreview)
            {
              // Does not render fixed cells for local preview
              // but ignores movable state for non-local previews
              if (isLocal)
              {
                if (!isLivePreview())
                {
                  updateDragImage(graph
                      .getMovableCells(dragCells));
                }
              }
              else
              {
                Object[] tmp = graphComponent
                    .getImportableCells(dragCells);
                updateDragImage(tmp);

                // Shows no drag icon if import is allowed but none
                // of the cells can be imported
                if (tmp == null || tmp.length == 0)
                {
                  canImport = false;
                  e.rejectDrag();

                  return;
                }
              }
            }

            setVisible(true);
          }
        }

        e.acceptDrag(TransferHandler.COPY_OR_MOVE);
      }
      catch (Exception ex)
      {
        // do nothing
        ex.printStackTrace();
      }

    }
    else
    {
      e.rejectDrag();
    }
  }

  /**
   *
   */
  public void mousePressed(MouseEvent e)
  {
    if (graphComponent.isEnabled() && isEnabled() && !e.isConsumed()
        && !graphComponent.isForceMarqueeEvent(e))
    {
      cell = graphComponent.getCellAt(e.getX(), e.getY(), false);
      initialCell = cell;

      if (cell != null)
      {
        if (isSelectEnabled()
            && !graphComponent.getGraph().isCellSelected(cell))
        {
          graphComponent.selectCellForEvent(cell, e);
          cell = null;
        }

        // Starts move if the cell under the mouse is movable and/or any
        // cells of the selection are movable
        if (isMoveEnabled() && !e.isPopupTrigger())
        {
          start(e);
          e.consume();
        }
      }
      else if (e.isPopupTrigger())
      {
        graphComponent.getGraph().clearSelection();
      }
    }
  }

  /**
   *
   */
  public Object[] getCells(Object initialCell)
  {
    mxGraph graph = graphComponent.getGraph();

    return graph.getMovableCells(graph.getSelectionCells());
  }

  /**
   *
   */
  public void start(MouseEvent e)
  {
    if (isLivePreview())
    {
      movePreview.start(e,
          graphComponent.getGraph().getView().getState(initialCell));
    }
    else
    {
      mxGraph graph = graphComponent.getGraph();

      // Constructs an array with cells that are indeed movable
      cells = getCells(initialCell);
      cellBounds = graph.getView().getBounds(cells);

      if (cellBounds != null)
      {
        // Updates the size of the graph handler that is in
        // charge of painting all other handlers
        bbox = graph.getView().getBoundingBox(cells);

        Rectangle bounds = cellBounds.getRectangle();
        bounds.width += 1;
        bounds.height += 1;
        setPreviewBounds(bounds);
      }
    }

    first = e.getPoint();
  }

  /**
   *
   */
  public void dropActionChanged(DropTargetDragEvent e)
  {
    // do nothing
  }

  /**
   *
   * @param e
   */
  public void dragOver(DropTargetDragEvent e)
  {
    if (canImport)
    {
      mouseDragged(createEvent(e));
      mxGraphTransferHandler handler = getGraphTransferHandler(e);

      if (handler != null)
      {
        mxGraph graph = graphComponent.getGraph();
        double scale = graph.getView().getScale();
        Point pt = SwingUtilities.convertPoint(graphComponent,
            e.getLocation(), graphComponent.getGraphControl());

        pt = graphComponent.snapScaledPoint(new mxPoint(pt)).getPoint();
        handler.setLocation(new Point(pt));

        int dx = 0;
        int dy = 0;

        // Centers the preview image
        if (centerPreview && transferBounds != null)
        {
          dx -= Math.round(transferBounds.getWidth() * scale / 2);
          dy -= Math.round(transferBounds.getHeight() * scale / 2);
        }

        // Sets the drop offset so that the location in the transfer
        // handler reflects the actual mouse position
        handler.setOffset(new Point((int) graph.snap(dx / scale),
            (int) graph.snap(dy / scale)));
        pt.translate(dx, dy);

        // Shifts the preview so that overlapping parts do not
        // affect the centering
        if (transferBounds != null && dragImage != null)
        {
          dx = (int) Math
              .round((dragImage.getIconWidth() - 2 - transferBounds
                  .getWidth() * scale) / 2);
          dy = (int) Math
              .round((dragImage.getIconHeight() - 2 - transferBounds
                  .getHeight() * scale) / 2);
          pt.translate(-dx, -dy);
        }

        if (!handler.isLocalDrag() && previewBounds != null)
        {
          setPreviewBounds(new Rectangle(pt, previewBounds.getSize()));
        }
      }
    }
    else
    {
      e.rejectDrag();
    }
  }

  /**
   *
   */
  public Point convertPoint(Point pt)
  {
    pt = SwingUtilities.convertPoint(graphComponent, pt,
        graphComponent.getGraphControl());

    pt.x -= graphComponent.getHorizontalScrollBar().getValue();
    pt.y -= graphComponent.getVerticalScrollBar().getValue();

    return pt;
  }

  /**
   *
   */
  public void mouseDragged(MouseEvent e)
  {
    // LATER: Check scrollborder, use scroll-increments, do not
    // scroll when over ruler dragging from library
    graphComponent.getGraphControl().scrollRectToVisible(
        new Rectangle(e.getPoint()));

    if (!e.isConsumed())
    {
      gridEnabledEvent = graphComponent.isGridEnabledEvent(e);
      constrainedEvent = graphComponent.isConstrainedEvent(e);

      if (constrainedEvent && first != null)
      {
        int x = e.getX();
        int y = e.getY();

        if (Math.abs(e.getX() - first.x) > Math.abs(e.getY() - first.y))
        {
          y = first.y;
        }
        else
        {
          x = first.x;
        }

        e = new MouseEvent(e.getComponent(), e.getID(), e.getWhen(),
            e.getModifiers(), x, y, e.getClickCount(),
            e.isPopupTrigger(), e.getButton());
      }

      if (isVisible() && isMarkerEnabled())
      {
        marker.process(e);
      }

      if (first != null)
      {
        if (movePreview.isActive())
        {
          double dx = e.getX() - first.x;
          double dy = e.getY() - first.y;

          if (graphComponent.isGridEnabledEvent(e))
          {
            mxGraph graph = graphComponent.getGraph();

            dx = graph.snap(dx);
            dy = graph.snap(dy);
          }

          boolean clone = isCloneEnabled()
              && graphComponent.isCloneEvent(e);
          movePreview.update(e, dx, dy, clone);
          e.consume();
        }
        else if (cellBounds != null)
        {
          double dx = e.getX() - first.x;
          double dy = e.getY() - first.y;

          if (previewBounds != null)
          {
            setPreviewBounds(new Rectangle(getPreviewLocation(e,
                gridEnabledEvent), previewBounds.getSize()));
          }

          if (!isVisible() && graphComponent.isSignificant(dx, dy))
          {
            if (imagePreview && dragImage == null
                && !graphComponent.isDragEnabled())
            {
              updateDragImage(cells);
            }

            setVisible(true);
          }

          e.consume();
        }
      }
    }
  }

  /**
   *
   */
  protected Point getPreviewLocation(MouseEvent e, boolean gridEnabled)
  {
    int x = 0;
    int y = 0;

    if (first != null && cellBounds != null)
    {
      mxGraph graph = graphComponent.getGraph();
      double scale = graph.getView().getScale();
      mxPoint trans = graph.getView().getTranslate();

      // LATER: Drag image _size_ depends on the initial position and may sometimes
      // not align with the grid when dragging. This is because the rounding of the width
      // and height at the initial position may be different than that at the current
      // position as the left and bottom side of the shape must align to the grid lines.
      // Only fix is a full repaint of the drag cells at each new mouse location.
      double dx = e.getX() - first.x;
      double dy = e.getY() - first.y;

      double dxg = ((cellBounds.getX() + dx) / scale) - trans.getX();
      double dyg = ((cellBounds.getY() + dy) / scale) - trans.getY();

      if (gridEnabled)
      {
        dxg = graph.snap(dxg);
        dyg = graph.snap(dyg);
      }

      x = (int) Math.round((dxg + trans.getX()) * scale)
          + (int) Math.round(bbox.getX())
          - (int) Math.round(cellBounds.getX());
      y = (int) Math.round((dyg + trans.getY()) * scale)
          + (int) Math.round(bbox.getY())
          - (int) Math.round(cellBounds.getY());
    }

    return new Point(x, y);
  }

  /**
   *
   * @param e
   */
  public void dragExit(DropTargetEvent e)
  {
    mxGraphTransferHandler handler = getGraphTransferHandler(e);

    if (handler != null)
    {
      handler.setLocation(null);
    }

    dragCells = null;
    setVisible(false);
    marker.reset();
    reset();
  }

  /**
   *
   * @param e
   */
  public void drop(DropTargetDropEvent e)
  {
    if (canImport)
    {
      mxGraphTransferHandler handler = getGraphTransferHandler(e);
      MouseEvent event = createEvent(e);

      // Ignores the event in mouseReleased if it is
      // handled by the transfer handler as a drop
      if (handler != null && !handler.isLocalDrag())
      {
        event.consume();
      }

      mouseReleased(event);
    }
  }

  /**
   *
   */
  public void mouseReleased(MouseEvent e)
  {
    if (graphComponent.isEnabled() && isEnabled() && !e.isConsumed())
    {
      mxGraph graph = graphComponent.getGraph();
      double dx = 0;
      double dy = 0;

      if (first != null && (cellBounds != null || movePreview.isActive()))
      {
        double scale = graph.getView().getScale();
        mxPoint trans = graph.getView().getTranslate();

        // TODO: Simplify math below, this was copy pasted from
        // getPreviewLocation with the rounding removed
        dx = e.getX() - first.x;
        dy = e.getY() - first.y;

        if (cellBounds != null)
        {
          double dxg = ((cellBounds.getX() + dx) / scale)
              - trans.getX();
          double dyg = ((cellBounds.getY() + dy) / scale)
              - trans.getY();

          if (gridEnabledEvent)
          {
            dxg = graph.snap(dxg);
            dyg = graph.snap(dyg);
          }

          double x = ((dxg + trans.getX()) * scale) + (bbox.getX())
              - (cellBounds.getX());
          double y = ((dyg + trans.getY()) * scale) + (bbox.getY())
              - (cellBounds.getY());

          dx = Math.round((x - bbox.getX()) / scale);
          dy = Math.round((y - bbox.getY()) / scale);
        }
      }

      if (first == null
          || !graphComponent.isSignificant(e.getX() - first.x,
              e.getY() - first.y))
      {
        // Delayed handling of selection
        if (cell != null && !e.isPopupTrigger() && isSelectEnabled()
            && (first != null || !isMoveEnabled()))
        {
          graphComponent.selectCellForEvent(cell, e);
        }

        // Delayed folding for cell that was initially under the mouse
        if (graphComponent.isFoldingEnabled()
            && graphComponent.hitFoldingIcon(initialCell, e.getX(),
                e.getY()))
        {
          fold(initialCell);
        }
        else
        {
          // Handles selection if no cell was initially under the mouse
          Object tmp = graphComponent.getCellAt(e.getX(), e.getY(),
              graphComponent.isSwimlaneSelectionEnabled());

          if (cell == null && first == null)
          {
            if (tmp == null)
            {
              if (!graphComponent.isToggleEvent(e))
              {
                graph.clearSelection();
              }
            }
            else if (graph.isSwimlane(tmp)
                && graphComponent.getCanvas()
                    .hitSwimlaneContent(graphComponent,
                        graph.getView().getState(tmp),
                        e.getX(), e.getY()))
            {
              graphComponent.selectCellForEvent(tmp, e);
            }
          }

          if (graphComponent.isFoldingEnabled()
              && graphComponent.hitFoldingIcon(tmp, e.getX(),
                  e.getY()))
          {
            fold(tmp);
            e.consume();
          }
        }
      }
      else if (movePreview.isActive())
      {
        if (graphComponent.isConstrainedEvent(e))
        {
          if (Math.abs(dx) > Math.abs(dy))
          {
            dy = 0;
          }
          else
          {
            dx = 0;
          }
        }

        mxCellState markedState = marker.getMarkedState();
        Object target = (markedState != null) ? markedState.getCell()
            : null;

        // FIXME: Cell is null if selection was carried out, need other variable
        //trace("cell", cell);

        if (target == null
            && isRemoveCellsFromParent()
            && shouldRemoveCellFromParent(graph.getModel()
                .getParent(initialCell), cells, e))
        {
          target = graph.getDefaultParent();
        }

        boolean clone = isCloneEnabled()
            && graphComponent.isCloneEvent(e);
        Object[] result = movePreview.stop(true, e, dx, dy, clone,
            target);

        if (cells != result)
        {
          graph.setSelectionCells(result);
        }

        e.consume();
      }
      else if (isVisible())
      {
        if (constrainedEvent)
        {
          if (Math.abs(dx) > Math.abs(dy))
          {
            dy = 0;
          }
          else
          {
            dx = 0;
          }
        }

        mxCellState targetState = marker.getValidState();
        Object target = (targetState != null) ? targetState.getCell()
            : null;

        if (graph.isSplitEnabled()
            && graph.isSplitTarget(target, cells))
        {
          graph.splitEdge(target, cells, dx, dy);
        }
        else
        {
          moveCells(cells, dx, dy, target, e);
        }

        e.consume();
      }
    }

    reset();
  }

  /**
   *
   */
  protected void fold(Object cell)
  {
    boolean collapse = !graphComponent.getGraph().isCellCollapsed(cell);
    graphComponent.getGraph().foldCells(collapse, false,
        new Object[] { cell });
  }

  /**
   *
   */
  public void reset()
  {
    if (movePreview.isActive())
    {
      movePreview.stop(false, null, 0, 0, false, null);
    }

    setVisible(false);
    marker.reset();
    initialCell = null;
    dragCells = null;
    dragImage = null;
    cells = null;
    first = null;
    cell = null;
  }

  /**
   * Returns true if the given cells should be removed from the parent for the specified
   * mousereleased event.
   */
  protected boolean shouldRemoveCellFromParent(Object parent, Object[] cells,
      MouseEvent e)
  {
    if (graphComponent.getGraph().getModel().isVertex(parent))
    {
      mxCellState pState = graphComponent.getGraph().getView()
          .getState(parent);

      return pState != null && !pState.contains(e.getX(), e.getY());
    }

    return false;
  }

  /**
   *
   * @param dx
   * @param dy
   * @param e
   */
  protected void moveCells(Object[] cells, double dx, double dy,
      Object target, MouseEvent e)
  {
    mxGraph graph = graphComponent.getGraph();
    boolean clone = e.isControlDown() && isCloneEnabled();

    if (clone)
    {
      cells = graph.getCloneableCells(cells);
    }

    // Removes cells from parent
    if (target == null
        && isRemoveCellsFromParent()
        && shouldRemoveCellFromParent(
            graph.getModel().getParent(initialCell), cells, e))
    {
      target = graph.getDefaultParent();
    }

    Object[] tmp = graph.moveCells(cells, dx, dy, clone, target,
        e.getPoint());

    if (isSelectEnabled() && clone && tmp != null
        && tmp.length == cells.length)
    {
      graph.setSelectionCells(tmp);
    }
  }

  /**
   *
   */
  public void paint(Graphics g)
  {
    if (isVisible() && previewBounds != null)
    {
      if (dragImage != null)
      {
        // LATER: Clipping with mxUtils doesnt fix the problem
        // of the drawImage being painted over the scrollbars
        Graphics2D tmp = (Graphics2D) g.create();

        if (graphComponent.getPreviewAlpha() < 1)
        {
          tmp.setComposite(AlphaComposite.getInstance(
              AlphaComposite.SRC_OVER,
              graphComponent.getPreviewAlpha()));
        }

        tmp.drawImage(dragImage.getImage(), previewBounds.x,
            previewBounds.y, dragImage.getIconWidth(),
            dragImage.getIconHeight(), null);
        tmp.dispose();
      }
      else if (!imagePreview)
      {
        mxConstants.PREVIEW_BORDER.paintBorder(graphComponent, g,
            previewBounds.x, previewBounds.y, previewBounds.width,
            previewBounds.height);
      }
    }
  }

  /**
   *
   */
  protected MouseEvent createEvent(DropTargetEvent e)
  {
    JComponent component = getDropTarget(e);
    Point location = null;
    int action = 0;

    if (e instanceof DropTargetDropEvent)
    {
      location = ((DropTargetDropEvent) e).getLocation();
      action = ((DropTargetDropEvent) e).getDropAction();
    }
    else if (e instanceof DropTargetDragEvent)
    {
      location = ((DropTargetDragEvent) e).getLocation();
      action = ((DropTargetDragEvent) e).getDropAction();
    }

    if (location != null)
    {
      location = convertPoint(location);
      Rectangle r = graphComponent.getViewport().getViewRect();
      location.translate(r.x, r.y);
    }

    // LATER: Fetch state of modifier keys from event or via global
    // key listener using Toolkit.getDefaultToolkit().addAWTEventListener(
    // new AWTEventListener() {...}, AWTEvent.KEY_EVENT_MASK). Problem
    // is the event does not contain the modifier keys and the global
    // handler is not called during drag and drop.
    int mod = (action == TransferHandler.COPY) ? InputEvent.CTRL_MASK : 0;

    return new MouseEvent(component, 0, System.currentTimeMillis(), mod,
        location.x, location.y, 1, false, MouseEvent.BUTTON1);
  }

  /**
   * Helper method to return the component for a drop target event.
   */
  protected static final mxGraphTransferHandler getGraphTransferHandler(
      DropTargetEvent e)
  {
    JComponent component = getDropTarget(e);
    TransferHandler transferHandler = component.getTransferHandler();

    if (transferHandler instanceof mxGraphTransferHandler)
    {
      return (mxGraphTransferHandler) transferHandler;
    }

    return null;
  }

  /**
   * Helper method to return the component for a drop target event.
   */
  protected static final JComponent getDropTarget(DropTargetEvent e)
  {
    return (JComponent) e.getDropTargetContext().getComponent();
  }

}
TOP

Related Classes of com.mxgraph.swing.handler.mxGraphHandler

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.