package com.horstmann.violet.workspace.editorpart.behavior; import java.awt.Color; import java.awt.Graphics2D; import java.awt.event.MouseEvent; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; import com.horstmann.violet.product.diagram.abstracts.IGraph; import com.horstmann.violet.product.diagram.abstracts.IGridSticker; import com.horstmann.violet.product.diagram.abstracts.Id; import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; import com.horstmann.violet.product.diagram.abstracts.node.INode; import com.horstmann.violet.workspace.editorpart.IEditorPart; import com.horstmann.violet.workspace.editorpart.IEditorPartBehaviorManager; import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler; import com.horstmann.violet.workspace.sidebar.graphtools.GraphTool; import com.horstmann.violet.workspace.sidebar.graphtools.IGraphToolsBar; public class AddEdgeBehavior extends AbstractEditorPartBehavior { public AddEdgeBehavior(IEditorPart editorPart, IGraphToolsBar graphToolsBar) { this.editorPart = editorPart; this.graph = editorPart.getGraph(); this.grid = editorPart.getGraph().getGridSticker(); this.selectionHandler = editorPart.getSelectionHandler(); this.behaviorManager = editorPart.getBehaviorManager(); this.graphToolsBar = graphToolsBar; } @Override public void onMousePressed(MouseEvent event) { if (!isConditionOK(event)) { cancel(); return; } if (!this.isLinkingInProgress) { startAction(event); return; } if (this.isLinkingInProgress && this.isLinkBySeparatedClicks) { if (isRecognizedAsTransitionAction()) { transitionAction(event); return; } endAction(event); return; } } @Override public void onMouseDragged(MouseEvent event) { if (!this.isLinkingInProgress) { return; } repaintOnMouseMoved(event); } @Override public void onMouseMoved(MouseEvent event) { if (!this.isLinkingInProgress) { return; } this.isLinkBySeparatedClicks = true; repaintOnMouseMoved(event); } @Override public void onMouseReleased(MouseEvent event) { this.editorPart.getSwingComponent().invalidate(); if (this.isLinkBySeparatedClicks) { return; } if (this.isLinkingInProgress) { endAction(event); return; } } private void repaintOnMouseMoved(MouseEvent event) { double zoom = this.editorPart.getZoomFactor(); Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); Point2D snappedMousePoint = grid.snap(mousePoint); if (!snappedMousePoint.equals(lastMousePoint)) { this.editorPart.getSwingComponent().invalidate(); this.editorPart.getSwingComponent().repaint(); } this.lastMousePoint = snappedMousePoint; } private boolean isConditionOK(MouseEvent event) { if (event.getClickCount() > 1) { return false; } if (event.getButton() != MouseEvent.BUTTON1) { return false; } if (GraphTool.SELECTION_TOOL.equals(this.graphToolsBar.getSelectedTool())) { return false; } GraphTool selectedTool = this.selectionHandler.getSelectedTool(); if (!IEdge.class.isInstance(selectedTool.getNodeOrEdge())) { return false; } return true; } private boolean isRecognizedAsTransitionAction() { if (this.newEdge == null) { return false; } if (!this.newEdge.isTransitionPointsSupported()) { return false; } INode aNode = graph.findNode(this.lastMousePoint); if (aNode == null) { return true; } return false; } private void startAction(MouseEvent event) { double zoom = editorPart.getZoomFactor(); final Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); INode targetNode = graph.findNode(mousePoint); this.isLinkingInProgress = (targetNode != null); this.firstMousePoint = grid.snap(mousePoint); this.lastMousePoint = grid.snap(mousePoint); GraphTool selectedTool = this.selectionHandler.getSelectedTool(); IEdge prototype = (IEdge) selectedTool.getNodeOrEdge(); this.newEdge = (IEdge) prototype.clone(); this.newEdge.setId(new Id()); } private void transitionAction(MouseEvent event) { this.transitionPoints.add(this.lastMousePoint); } private void endAction(MouseEvent event) { boolean added = addEdgeAtPoints(this.newEdge, firstMousePoint, lastMousePoint); if (added) { this.selectionHandler.setSelectedElement(this.newEdge); } this.isLinkingInProgress = false; this.isLinkBySeparatedClicks = false; this.transitionPoints.clear(); this.newEdge = null; } private void cancel() { this.isLinkingInProgress = false; this.isLinkBySeparatedClicks = false; this.transitionPoints.clear(); this.newEdge = null; } /** * Adds an edge at a specific location * * @param newEdge * @param startPoint * @param endPoint * @return true id the edge has been added */ public boolean addEdgeAtPoints(IEdge newEdge, Point2D startPoint, Point2D endPoint) { boolean isAdded = false; if (startPoint.distance(endPoint) > CONNECT_THRESHOLD) { this.behaviorManager.fireBeforeAddingEdgeAtPoints(newEdge, startPoint, endPoint); try { INode startNode = graph.findNode(startPoint); INode endNode = graph.findNode(endPoint); Point2D relativeStartPoint = null; Point2D relativeEndPoint = null; Point2D[] transitionPointsAsArray = this.transitionPoints.toArray(new Point2D[this.transitionPoints.size()]); if (startNode != null) { Point2D startNodeLocationOnGraph = startNode.getLocationOnGraph(); double relativeStartX = startPoint.getX() - startNodeLocationOnGraph.getX(); double relativeStartY = startPoint.getY() - startNodeLocationOnGraph.getY(); relativeStartPoint = new Point2D.Double(relativeStartX, relativeStartY); } if (endNode != null) { Point2D endNodeLocationOnGraph = endNode.getLocationOnGraph(); double relativeEndX = endPoint.getX() - endNodeLocationOnGraph.getX(); double relativeEndY = endPoint.getY() - endNodeLocationOnGraph.getY(); relativeEndPoint = new Point2D.Double(relativeEndX, relativeEndY); } if (graph.connect(newEdge, startNode, relativeStartPoint, endNode, relativeEndPoint, transitionPointsAsArray)) { newEdge.incrementRevision(); isAdded = true; } } finally { this.behaviorManager.fireAfterAddingEdgeAtPoints(newEdge, startPoint, endPoint); } } return isAdded; } @Override public void onPaint(Graphics2D g2) { if (!isLinkingInProgress) { return; } Color oldColor = g2.getColor(); g2.setColor(PURPLE); GeneralPath path = new GeneralPath(); path.moveTo(this.firstMousePoint.getX(), this.firstMousePoint.getY()); for (Point2D aTransitionPoint : this.transitionPoints) { path.lineTo(aTransitionPoint.getX(), aTransitionPoint.getY()); } path.lineTo(this.lastMousePoint.getX(), this.lastMousePoint.getY()); g2.draw(path); g2.setColor(oldColor); } private static final Color PURPLE = new Color(0.7f, 0.4f, 0.7f); private static final int CONNECT_THRESHOLD = 8; private Point2D firstMousePoint = null; private Point2D lastMousePoint = null; private IEditorPart editorPart; private IGraph graph; private IGridSticker grid; private IEditorPartSelectionHandler selectionHandler; private IEditorPartBehaviorManager behaviorManager; private IGraphToolsBar graphToolsBar; private boolean isLinkingInProgress = false; private boolean isLinkBySeparatedClicks = false; private List<Point2D> transitionPoints = new ArrayList<Point2D>(); private IEdge newEdge = null; }