Package org.nanograph.interaction

Source Code of org.nanograph.interaction.InteractionManager$MoveNodeMode

/*
* NanoGraph, a small footprint java graph drawing component
*
*    Copyright 2004 Jeroen van Grondelle
*              2013 Xander Uiterlinden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package org.nanograph.interaction;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.nanograph.drawing.NanoGraph;
import org.nanograph.drawing.graphicsadapter.GraphicsAdapter;
import org.nanograph.interaction.events.GraphActionEvent;
import org.nanograph.interaction.events.GraphActionListener;
import org.nanograph.interaction.events.GraphSelectionEvent;
import org.nanograph.interaction.events.GraphSelectionListener;
import org.nanograph.interaction.selection.Selection;

/**
* InteractionManager implements generic interaction behaviour for managing
* selections and event listeners by interpreting mouse clicks and keyboard
* shortcuts.
*
* The interactionmanager uses the Strategy pattern to implement complex
* selection manipulation: each possible function a mouse operation has is
* implemented in a Strategy class, the strategies decide on their own succesor.
*
* @author Jeroen van Grondelle
*/
public class InteractionManager {

  public final static int MODIFIER_CTRL = 1, MODIFIER_SHIFT = 2, MODIFIER_ALT = 4, MODIFIER_RIGHTCLICK = 8;

  public final SelectionMode DEFAULT = new DefaultSelectionMode(), SELECT = new GroupSelectSelectionMode(), MOVE_GROUP = new MoveSelectionMode(), MOVE_NODE = new MoveNodeMode(), ACTION = new ActionMode();

  NanoGraph nanograph = null;

  Selection nodeSelection = new Selection();

  Object selectedEdge = null;

  SelectionMode selectionMode = DEFAULT;

  Rectangle2D selectionBox = null, moveBox = null;

  private boolean interactionEnabled = true;

  private boolean draggingSelection = false;

  public InteractionManager(NanoGraph nanograph) {
    this.nanograph = nanograph;
  }

  public void handleMouseDown(int x, int y, int modifiers) {
    if (!interactionEnabled) {
      return;
    }
    fireGraphActionResetEvent();
    selectionMode.startSelection(x, y, modifiers);
  }

  public void handleMouseDrag(int x, int y) {
    if (!interactionEnabled) {
      return;
    }
    if (!draggingSelection && !nodeSelection.isEmpty()) {
      draggingSelection = true;
    }
    selectionMode.dragSelection(x, y);
  }

  public void handleMouseUp(int x, int y) {
    if (!interactionEnabled) {
      return;
    }
    if (draggingSelection && !nodeSelection.isEmpty()) {
      draggingSelection = false;
      fireSelectionMoveEvent();
    }
    selectionMode.endSelection(x, y);
  }

  public void handleKeyboardShortcut(int character, int modifiers) {
    if (!interactionEnabled) {
      return;
    }
    if ((modifiers & MODIFIER_CTRL) == 1) {
      if (character == 'a' || character == 'A') {
        // ctrl+a
        // select all
        nodeSelection.reset();
        selectedEdge = null;
        for (int i = 0; i < nanograph.getModel().getNodeCount(); i++) {
          nodeSelection.add(nanograph.getModel().getNode(i));
        }
        setNodeSelection(nodeSelection);
        fireSelectionChangedEvent();
      }
      if (character == 'c' || character == 'C') {
        // ctrl+c
      }
      if (character == 'x' || character == 'X') {
        // ctrl+x
      }
      if (character == 'v' || character == 'V') {
        // ctrl+v
      }
      if (character == 'z' || character == 'Z') {
        // ctrl+z
      }
    }
  }

  private void switchMode(SelectionMode mode) {
    selectionMode = mode;
  }

  private interface SelectionMode {
    void startSelection(int x, int y, int modifiers);

    void dragSelection(int x, int y);

    void endSelection(int x, int y);

  }

  private class DefaultSelectionMode implements SelectionMode {

    Object selectedNode = null;

    int dragCount = 0, modifiers;

    int xdown = 0, ydown = 0;

    public void startSelection(int x, int y, int modifiers) {
      // empty selection
      dragCount = 0;
      this.modifiers = modifiers;
      xdown = x;
      ydown = y;
      selectedNode = nanograph.getNodeForLocation(new Point2D.Double(x, y));
      selectedEdge = nanograph.getEdgeForLocation(new Point2D.Double(x, y));
      if (selectedNode != null) {
        nanograph.setSelectedEdge(null);
      } else {
        nanograph.setSelectedEdge(selectedEdge);
        nodeSelection.reset();
        fireSelectionChangedEvent();
      }
      // handle rightclick
      if ((modifiers & MODIFIER_RIGHTCLICK) != 0) {
        switchMode(ACTION);
        selectionMode.startSelection(x, y, modifiers);
        // handle others
      } else if (selectedNode == null && selectedEdge == null) {
        // A click outside nodes suggest a group selection operation
        switchMode(SELECT);
        selectionMode.startSelection(x, y, modifiers);
      }
    }

    public void dragSelection(int x, int y) {
      dragCount++;
      if (selectedNode != null && !nodeSelection.contains(selectedNode)) {
        nodeSelection.reset();
        fireSelectionResetEvent();
        nodeSelection.add(selectedNode);
        fireSelectionChangedEvent();
      }
      // a drag on one of the selected nodes indicates a move
      if (nodeSelection.size() == 0) {

      } else if (nodeSelection.size() == 1) {
        switchMode(MOVE_NODE);
        selectionMode.startSelection(xdown, ydown, modifiers);
      } else {
        switchMode(MOVE_GROUP);
        selectionMode.startSelection(xdown, ydown, modifiers);
      }
    }

    public void endSelection(int x, int y) {
      if (dragCount == 0) {
        if ((modifiers & MODIFIER_CTRL) == 0) {
          // ctrl not pressed
          nodeSelection.reset();
          if (selectedNode != null) {
            nodeSelection.toggle(selectedNode);
          }
        } else {
          if (selectedNode != null) {
            nodeSelection.toggle(selectedNode);
          } else {
            nodeSelection.reset();
          }
        }
      }
      if (!nodeSelection.isEmpty()) {
        fireSelectionChangedEvent();
      } else if (selectedEdge == null) {
        fireSelectionResetEvent();
      }
    }

    public String toString() {
      return "DEFAULT";
    }
  }

  class GroupSelectSelectionMode implements SelectionMode {
    int startx = 0, starty = 0;

    public void startSelection(int x, int y, int modifiers) {
      startx = x;
      starty = y;
      // if shift is not down
      if ((modifiers & MODIFIER_CTRL) == 0) {
        nodeSelection.reset();
        fireSelectionResetEvent();
      }
    }

    public void dragSelection(int x, int y) {
      // update selectionbox
      selectionBox = new Rectangle2D.Double(Math.min((double) startx, x), Math.min((double) starty, y), Math.max((double) startx, x) - Math.min((double) startx, x), Math.max((double) starty, y) - Math.min((double) starty, y));
    }

    public void endSelection(int x, int y) {
      // update Selection according to SelectionBox
      //
      if (selectionBox != null) {
        nodeSelection.addAll(nanograph.getNodesForBounds(selectionBox));
        fireSelectionChangedEvent();
      }
      selectionBox = null;
      switchMode(DEFAULT);
    }

    public String toString() {
      return "SELECT";
    }
  }

  class MoveSelectionMode implements SelectionMode {

    int startx = 0, starty = 0;

    // movebox parameters
    int deltax = 0, deltay = 0;

    double width, height;

    public void startSelection(int x, int y, int modifiers) {
      startx = x;
      starty = y;

      // create moveBox
      moveBox = nanograph.getBoundsForNodes(nodeSelection.getSelectedObjects());
      deltax = (int) moveBox.getMinX() - x;
      deltay = (int) moveBox.getMinY() - y;
      width = moveBox.getWidth();
      height = moveBox.getHeight();
    }

    public void dragSelection(int x, int y) {
      int newX = x + deltax - 10;
      int newY = y + deltay - 10;

      double mdeltax = newX - moveBox.getX();
      double mdeltay = newY - moveBox.getY();
      moveBox.setFrame((newX > 0 ? newX : 0), (newY > 0 ? newY : 0), width + 20, height + 20);
      //         
      double moveX = mdeltax;
      double moveY = mdeltay;
      for (Iterator iter = nodeSelection.getSelectedObjects().iterator(); iter.hasNext();) {
        Object node = iter.next();
        Point2D p = nanograph.getModel().getLocation(node);
        double bnewX = p.getX() + moveX;
        double bnewY = p.getY() + moveY;
        double tempMoveX = moveX + 0 - bnewX;
        double tempMoveY = moveY + 0 - bnewY;
        if (newX < 0 && Math.abs(tempMoveX) < Math.abs(moveX)) {
          moveX = tempMoveX;
        }
        if (newY < 0 && Math.abs(tempMoveY) < Math.abs(moveY)) {
          moveY = tempMoveY;
        }
      }
      // use a tempmap because we don't know how the locations are stored
      // in the model.
      // this way, we first calculated the new positions and then set them
      // on the model
      Map tempMap = new HashMap();
      for (Iterator iter = nodeSelection.getSelectedObjects().iterator(); iter.hasNext();) {
        Object node = iter.next();
        Point2D p = nanograph.getModel().getLocation(node);
        double bnewX = p.getX() + moveX;
        double bnewY = p.getY() + moveY;
        // put the new value in the tempMap
        tempMap.put(node, new Point2D.Double((bnewX > 0 ? bnewX : 0), (bnewY > 0 ? bnewY : 0)));
      }
      // set the new positions on the model
      for (Iterator iter = tempMap.keySet().iterator(); iter.hasNext();) {
        Object node = iter.next();
        nanograph.getModel().setLocation(node, (Point2D) tempMap.get(node));
      }
    }

    public void endSelection(int x, int y) {
      moveBox = null;
      switchMode(DEFAULT);
    }

    public String toString() {
      return "MOVE GROUP";
    }
  }

  class MoveNodeMode implements SelectionMode {

    int startx = 0, starty = 0;

    int deltax = 0, deltay = 0;

    public void startSelection(int x, int y, int modifiers) {
      startx = x;
      starty = y;
      Point2D p = nanograph.getModel().getLocation(nodeSelection.getSelectedObjects().iterator().next());
      if (p != null) {
        deltax = (int) p.getX() - x;
        deltay = (int) p.getY() - y;
      }
    }

    public void dragSelection(int x, int y) {
      int newX = x + deltax;
      int newY = y + deltay;
      nanograph.getModel().setLocation(nodeSelection.getSelectedObjects().iterator().next(), new Point2D.Double((newX > 0 ? newX : 0), (newY > 0 ? newY : 0)));
    }

    public void endSelection(int x, int y) {
      int newX = x + deltax;
      int newY = y + deltay;
      nanograph.getModel().setLocation(nodeSelection.getSelectedObjects().iterator().next(), new Point2D.Double((newX > 0 ? newX : 0), (newY > 0 ? newY : 0)));
      switchMode(DEFAULT);
    }

    public String toString() {
      return "MOVE NODE";
    }
  }

  class ActionMode implements SelectionMode {

    boolean moved = false;

    public void startSelection(int x, int y, int modifiers) {
      moved = false;
      if ((modifiers & MODIFIER_CTRL) == 0) {
        nodeSelection.reset();
        fireSelectionResetEvent();
      }
    }

    public void dragSelection(int x, int y) {
      moved = true;
    }

    public void endSelection(int x, int y) {
      if (!moved) {
        Object node = nanograph.getNodeForLocation(new Point2D.Double(x, y));
        selectedEdge = nanograph.getEdgeForLocation(new Point2D.Double(x, y));
        nanograph.setSelectedEdge(selectedEdge);
        if (node != null) {
          selectedEdge = null;
          nodeSelection.toggle(node);
          nanograph.setSelectedEdge(null);
          fireGraphActionEvent(node, GraphActionEvent.NODE_ACTION);
        } else {
          if (selectedEdge != null) {
            nodeSelection.reset();
            fireGraphActionEvent(selectedEdge, GraphActionEvent.EDGE_ACTION);
          }
        }
      }
      switchMode(DEFAULT);
    }

    public String toString() {
      return "ACTION";
    }
  }

  public void setSelectedEdge(Object edge) {
    selectedEdge = edge;
    nanograph.setSelectedEdge(edge);
    fireSelectionChangedEvent();
  }

  /**
   * This method paints the selection box and other visble aspects of
   * interaction over the graph.
   *
   * This methods is invoked by components that use an interactionManager
   * after the paintGraph() method.
   *
   * @param g
   *            Graphics object
   */
  public void paintInteractionMask(GraphicsAdapter g) {

    if (selectionBox != null) {
      g.setColor("#CCCCCC");
      g.drawRectangle((int) selectionBox.getX(), (int) selectionBox.getY(), (int) selectionBox.getWidth(), (int) selectionBox.getHeight());

      g.setFillColor("#00FF00");
      g.setAlpha(20);
      g.fillRectangle((int) selectionBox.getX(), (int) selectionBox.getY(), (int) selectionBox.getWidth(), (int) selectionBox.getHeight());

    }

    // if (moveBox != null) {
    // g2d.setColor(Color.darkGray);
    // //g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT,
    // BasicStroke.JOIN_MITER, 10, new float[] { 5, 2, 3, 2 }, 0));
    //
    // g2d.draw(moveBox);
    // }
  }

  // ///////////////////////////////////////
  // Listeners
  private ArrayList selectionListeners = new ArrayList();

  private ArrayList graphActionListeners = new ArrayList();

  private ArrayList selectionMoveListeners = new ArrayList();

  public void addSelectionListener(GraphSelectionListener l) {
    selectionListeners.add(l);
  }

  public void removeSelectionListener(GraphSelectionListener l) {
    selectionListeners.remove(l);
  }

  public void addActionListener(GraphActionListener l) {
    graphActionListeners.add(l);
  }

  public void removeActionListener(GraphActionListener l) {
    graphActionListeners.remove(l);
  }

  public void fireSelectionChangedEvent() {
    Iterator iter = selectionListeners.iterator();
    while (iter.hasNext()) {
      GraphSelectionListener l = (GraphSelectionListener) iter.next();
      l.selectionChanged(new GraphSelectionEvent(null, nodeSelection));
    }
  }

  public void fireSelectionResetEvent() {
    Iterator iter = selectionListeners.iterator();
    while (iter.hasNext()) {
      GraphSelectionListener l = (GraphSelectionListener) iter.next();
      l.selectionReset(new GraphSelectionEvent(null, nodeSelection));
    }
  }

  private void fireSelectionMoveEvent() {
    Iterator iter = selectionListeners.iterator();
    while (iter.hasNext()) {
      GraphSelectionListener l = (GraphSelectionListener) iter.next();
      l.selectionMove(new GraphSelectionEvent(null, nodeSelection));
    }
  }

  public void fireGraphActionEvent(Object object, int type) {
    Iterator iter = graphActionListeners.iterator();
    while (iter.hasNext()) {
      GraphActionListener l = (GraphActionListener) iter.next();
      l.graphActionPerformed(new GraphActionEvent(null, object, type));
    }
  }

  public void fireGraphActionResetEvent() {
    Iterator iter = graphActionListeners.iterator();
    while (iter.hasNext()) {
      GraphActionListener l = (GraphActionListener) iter.next();
      l.graphActionReset();
    }
  }

  // //////////////////
  // selection stuff
  public Selection getNodeSelection() {
    return nodeSelection;
  }

  public void setNodeSelection(Selection nodeSelection) {
    this.nodeSelection = nodeSelection;
  }

  public void reset() {
    nodeSelection.reset();
    selectedEdge = null;
    fireSelectionResetEvent();
    fireGraphActionResetEvent();
  }

  public boolean isInteractionEnabled() {
    return interactionEnabled;
  }

  public void setInteractionEnabled(boolean interactionEnabled) {
    this.interactionEnabled = interactionEnabled;
  }
}
TOP

Related Classes of org.nanograph.interaction.InteractionManager$MoveNodeMode

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.