diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/.classpath b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/.classpath
new file mode 100644
index 0000000..2d9e006
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/.classpath
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/.project b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/.project
new file mode 100644
index 0000000..b37e6b7
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/.project
@@ -0,0 +1,17 @@
+
+
+ VioletPlugin.SequenceDiagram
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/horstmann.eclipse.code.formatter.xml b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/horstmann.eclipse.code.formatter.xml
new file mode 100644
index 0000000..bef0923
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/horstmann.eclipse.code.formatter.xml
@@ -0,0 +1,246 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/pom.xml b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/pom.xml
new file mode 100644
index 0000000..1336f5e
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/pom.xml
@@ -0,0 +1,62 @@
+
+
+ 4.0.0
+ com.horstmann.violet.plugin
+ com.horstmann.violet.plugin.sequencediagram
+ Violet UML Editor Sequence Diagram Plugin
+ 2.0.0-SNAPSHOT
+
+ jar
+
+
+ com.horstmann.violet.framework
+ com.horstmann.violet.framework
+ 2.0.0-SNAPSHOT
+
+
+
+
+
+ src/main/resources
+
+
+ src/main/java
+
+ **/*.gif
+ **/*.jpg
+ **/*.properties
+ **/*.xml
+
+
+
+
+
+ maven-compiler-plugin
+ 2.0.2
+
+
+ 1.6
+
+
+
+
+
+
+ web.sourceforge.net
+ Violet's Maven Repository
+
+ sftp://web.sourceforge.net/home/groups/v/vi/violet/htdocs/maven2/repo
+
+
+
+
+
+ violet.repo
+ Violet's Maven repository (public access)
+ http://violet.sourceforge.net/maven2/repo/
+
+
+
\ No newline at end of file
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/ActivationBarNode.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/ActivationBarNode.java
new file mode 100644
index 0000000..640a781
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/ActivationBarNode.java
@@ -0,0 +1,727 @@
+/*
+ Violet - A program for editing UML diagrams.
+
+ Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com)
+ Alexandre de Pellegrin (http://alexdp.free.fr);
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package com.horstmann.violet.product.diagram.sequence;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.lang.model.type.NullType;
+
+import com.horstmann.violet.product.diagram.abstracts.Direction;
+import com.horstmann.violet.product.diagram.abstracts.edge.IEdge;
+import com.horstmann.violet.product.diagram.abstracts.node.INode;
+import com.horstmann.violet.product.diagram.abstracts.node.RectangularNode;
+
+/**
+ * An activation bar in a sequence diagram. This activation bar is hang on a lifeline (implicit parameter)
+ */
+public class ActivationBarNode extends RectangularNode
+{
+
+ @Override
+ public boolean addChild(INode n, Point2D p)
+ {
+ if (!n.getClass().isAssignableFrom(ActivationBarNode.class))
+ {
+ return false;
+ }
+ n.setParent(this);
+ n.setGraph(getGraph());
+ n.setLocation(p);
+ addChild(n, getChildren().size());
+ return true;
+ }
+
+
+ @Override
+ public void removeChild(INode node)
+ {
+ super.removeChild(node);
+ }
+
+ @Override
+ public boolean addConnection(IEdge edge)
+ {
+ INode endingNode = edge.getEnd();
+ INode startingNode = edge.getStart();
+ if (startingNode == endingNode)
+ {
+ return false;
+ }
+ if (edge instanceof CallEdge)
+ {
+ return isCallEdgeAcceptable((CallEdge) edge);
+
+ }
+ else if (edge instanceof ReturnEdge)
+ {
+ return isReturnEdgeAcceptable((ReturnEdge) edge);
+ }
+ return false;
+ }
+
+ @Override
+ public void removeConnection(IEdge e)
+ {
+ super.removeConnection(e);
+ }
+
+ @Override
+ public Point2D getConnectionPoint(IEdge e)
+ {
+ boolean isCallEdge = e.getClass().isAssignableFrom(CallEdge.class);
+ boolean isReturnEdge = e.getClass().isAssignableFrom(ReturnEdge.class);
+ boolean isActivationBarNodeOnStart = e.getStart() != null
+ && e.getStart().getClass().isAssignableFrom(ActivationBarNode.class);
+ boolean isActivationBarNodeOnEnd = e.getEnd() != null && e.getEnd().getClass().isAssignableFrom(ActivationBarNode.class);
+ boolean isLifelineNodeOnEnd = e.getEnd() != null && e.getEnd().getClass().isAssignableFrom(LifelineNode.class);
+ if (isCallEdge)
+ {
+ if (isActivationBarNodeOnStart && isActivationBarNodeOnEnd)
+ {
+ ActivationBarNode startingNode = (ActivationBarNode) e.getStart();
+ ActivationBarNode endingNode = (ActivationBarNode) e.getEnd();
+ LifelineNode startingLifelineNode = startingNode.getImplicitParameter();
+ LifelineNode endingLifelineNode = endingNode.getImplicitParameter();
+ boolean isSameLifelineNode = startingLifelineNode != null && endingLifelineNode != null
+ && startingLifelineNode.equals(endingLifelineNode);
+ boolean isDifferentLifelineNodes = startingLifelineNode != null && endingLifelineNode != null
+ && !startingLifelineNode.equals(endingLifelineNode);
+ // Case 1 : two activation bars connected on differents
+ // LifeLines
+ if (isDifferentLifelineNodes && isActivationBarNodeOnStart && isActivationBarNodeOnEnd)
+ {
+ boolean isStartingNode = this.equals(e.getStart());
+ boolean isEndingNode = this.equals(e.getEnd());
+ if (isStartingNode)
+ {
+ Point2D startingNodeLocation = getLocationOnGraph();
+ Point2D endingNodeLocation = e.getEnd().getLocationOnGraph();
+ Direction d = e.getDirection(this);
+ if (d.getX() > 0)
+ {
+ double x = startingNodeLocation.getX();
+ double y = endingNodeLocation.getY();
+ return new Point2D.Double(x, y);
+ }
+ else
+ {
+ double x = startingNodeLocation.getX() + DEFAULT_WIDTH;
+ double y = endingNodeLocation.getY();
+ return new Point2D.Double(x, y);
+ }
+ }
+ if (isEndingNode)
+ {
+ Point2D endingNodeLocation = getLocationOnGraph();
+ Direction d = e.getDirection(this);
+ if (d.getX() > 0)
+ {
+ double x = endingNodeLocation.getX();
+ double y = endingNodeLocation.getY();
+ return new Point2D.Double(x, y);
+ }
+ else
+ {
+ double x = endingNodeLocation.getX() + DEFAULT_WIDTH;
+ double y = endingNodeLocation.getY();
+ return new Point2D.Double(x, y);
+ }
+ }
+ }
+ // Case 2 : two activation bars connected on same lifeline (self
+ // call)
+ if (isSameLifelineNode && isActivationBarNodeOnStart && isActivationBarNodeOnEnd)
+ {
+ boolean isStartingNode = this.equals(e.getStart());
+ boolean isEndingNode = this.equals(e.getEnd());
+ if (isStartingNode)
+ {
+ Point2D startingNodeLocation = getLocationOnGraph();
+ Point2D endingNodeLocation = e.getEnd().getLocation();
+ double x = startingNodeLocation.getX() + DEFAULT_WIDTH;
+ double y = startingNodeLocation.getY() + endingNodeLocation.getY() - CALL_YGAP / 2;
+ return new Point2D.Double(x, y);
+ }
+ if (isEndingNode)
+ {
+ Point2D endingNodeLocation = getLocationOnGraph();
+ double x = endingNodeLocation.getX() + DEFAULT_WIDTH;
+ double y = endingNodeLocation.getY();
+ return new Point2D.Double(x, y);
+ }
+ }
+ }
+ if (isActivationBarNodeOnStart && isLifelineNodeOnEnd)
+ {
+ Direction d = e.getDirection(this);
+ Point2D startingNodeLocation = getLocationOnGraph();
+ if (d.getX() > 0)
+ {
+ double x = startingNodeLocation.getX();
+ double y = startingNodeLocation.getY() + CALL_YGAP / 2;
+ return new Point2D.Double(x, y);
+ }
+ else
+ {
+ double x = startingNodeLocation.getX() + DEFAULT_WIDTH;
+ double y = startingNodeLocation.getY() + CALL_YGAP / 2;
+ return new Point2D.Double(x, y);
+ }
+ }
+ }
+ if (isReturnEdge)
+ {
+ if (isActivationBarNodeOnStart && isActivationBarNodeOnEnd)
+ {
+ ActivationBarNode startingNode = (ActivationBarNode) e.getStart();
+ ActivationBarNode endingNode = (ActivationBarNode) e.getEnd();
+ LifelineNode startingLifelineNode = startingNode.getImplicitParameter();
+ LifelineNode endingLifelineNode = endingNode.getImplicitParameter();
+ boolean isDifferentLifelineNodes = startingLifelineNode != null && endingLifelineNode != null
+ && !startingLifelineNode.equals(endingLifelineNode);
+ // Case 1 : two activation bars connected on differents
+ // LifeLines
+ if (isDifferentLifelineNodes && isActivationBarNodeOnStart && isActivationBarNodeOnEnd)
+ {
+ boolean isStartingNode = this.equals(e.getStart());
+ boolean isEndingNode = this.equals(e.getEnd());
+ if (isStartingNode)
+ {
+ Point2D startingNodeLocation = getLocationOnGraph();
+ Rectangle2D startingNodeBounds = getBounds();
+ Direction d = e.getDirection(this);
+ if (d.getX() > 0)
+ {
+ double x = startingNodeLocation.getX();
+ double y = startingNodeLocation.getY() + startingNodeBounds.getHeight();
+ return new Point2D.Double(x, y);
+ }
+ else
+ {
+ double x = startingNodeLocation.getX() + DEFAULT_WIDTH;
+ double y = startingNodeLocation.getY() + startingNodeBounds.getHeight();
+ return new Point2D.Double(x, y);
+ }
+ }
+ if (isEndingNode)
+ {
+ Point2D startingNodeLocation = e.getStart().getLocationOnGraph();
+ Rectangle2D startingNodeBounds = e.getStart().getBounds();
+ Point2D endingNodeLocation = getLocationOnGraph();
+ Direction d = e.getDirection(this);
+ if (d.getX() > 0)
+ {
+ double x = endingNodeLocation.getX();
+ double y = startingNodeLocation.getY() + startingNodeBounds.getHeight();
+ return new Point2D.Double(x, y);
+ }
+ else
+ {
+ double x = endingNodeLocation.getX() + DEFAULT_WIDTH;
+ double y = startingNodeLocation.getY() + startingNodeBounds.getHeight();
+ return new Point2D.Double(x, y);
+ }
+ }
+ }
+ }
+ }
+ // Default case
+ Direction d = e.getDirection(this);
+ if (d.getX() > 0)
+ {
+ double y = getBounds().getMinY();
+ double x = getBounds().getMaxX();
+ return new Point2D.Double(x, y);
+ }
+ else
+ {
+ double y = getBounds().getMinY();
+ double x = getBounds().getX();
+ return new Point2D.Double(x, y);
+ }
+
+ }
+
+
+
+ /**
+ *
+ * @return true if this activation bar is connected to another one from another lifeline with a CallEdge AND if this activation
+ * bar is the STARTING node of this edge
+ */
+ private boolean isCallingNode()
+ {
+ LifelineNode currentLifelineNode = getImplicitParameter();
+ for (IEdge edge : getGraph().getAllEdges())
+ {
+ if (edge.getStart() != this)
+ {
+ continue;
+ }
+ if (!edge.getClass().isAssignableFrom(CallEdge.class))
+ {
+ continue;
+ }
+ INode endingNode = edge.getEnd();
+ if (!endingNode.getClass().isAssignableFrom(ActivationBarNode.class))
+ {
+ continue;
+ }
+ if (((ActivationBarNode) endingNode).getImplicitParameter() == currentLifelineNode)
+ {
+ continue;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ *
+ * @return true if this activation bar has been called by another activation bar
+ */
+ private boolean isCalledNode()
+ {
+ LifelineNode currentLifelineNode = getImplicitParameter();
+ for (IEdge edge : getGraph().getAllEdges())
+ {
+ if (edge.getEnd() != this)
+ {
+ continue;
+ }
+ if (!edge.getClass().isAssignableFrom(CallEdge.class))
+ {
+ continue;
+ }
+ INode startingNode = edge.getStart();
+ if (!startingNode.getClass().isAssignableFrom(ActivationBarNode.class))
+ {
+ continue;
+ }
+ if (((ActivationBarNode) startingNode).getImplicitParameter() == currentLifelineNode)
+ {
+ continue;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public Rectangle2D getBounds()
+ {
+ Point2D nodeLocation = getLocation();
+ // Height
+ double height = getHeight();
+ // TODO : manage openbottom
+ Rectangle2D currentBounds = new Rectangle2D.Double(nodeLocation.getX(), nodeLocation.getY(), DEFAULT_WIDTH, height);
+ Rectangle2D snappedBounds = getGraph().getGrid().snap(currentBounds);
+ return snappedBounds;
+ }
+
+ public double getHeight()
+ {
+ double height = DEFAULT_HEIGHT;
+ boolean isCallingNode = isCallingNode();
+ if (isCallingNode)
+ {
+ height = getHeightWhenLinked();
+ }
+ if (!isCallingNode)
+ {
+ height = getHeightWhenHasChildren();
+ }
+ return height;
+ }
+
+ /**
+ * If this activation bar calls another activation bar on another life line, its height must be greater than the activation bar
+ * which is called
+ *
+ * @return h
+ */
+ private double getHeightWhenLinked()
+ {
+ double height = 0;
+ for (IEdge edge : getGraph().getAllEdges())
+ {
+ if (!edge.getClass().isAssignableFrom(CallEdge.class))
+ {
+ continue;
+ }
+ if (edge.getStart() == this)
+ {
+ INode endingNode = edge.getEnd();
+ boolean isActivationBarNode = endingNode instanceof ActivationBarNode;
+ if (isActivationBarNode)
+ {
+ Rectangle2D endingNodeBounds = endingNode.getBounds();
+ double newHeight = CALL_YGAP / 2 + endingNodeBounds.getHeight() + (endingNode.getLocationOnGraph().getY() - this.getLocationOnGraph().getY()) + CALL_YGAP / 2;
+ height = Math.max(height, newHeight);
+ }
+ }
+ }
+ return Math.max(DEFAULT_HEIGHT, height);
+ }
+
+ /**
+ *
+ * @return
+ */
+ private double getHeightWhenHasChildren()
+ {
+ double height = DEFAULT_HEIGHT;
+ int childVisibleNodesCounter = 0;
+ for (INode aNode : getChildren())
+ {
+ if (aNode instanceof ActivationBarNode)
+ {
+ childVisibleNodesCounter++;
+ }
+ }
+ if (childVisibleNodesCounter > 0)
+ {
+ for (INode aNode : getChildren())
+ {
+ if (aNode instanceof ActivationBarNode)
+ {
+ ActivationBarNode anActivationBarNode = (ActivationBarNode) aNode;
+ double h = anActivationBarNode.getHeight();
+ double v = anActivationBarNode.getLocation().getY();
+ double maxY = v + h;
+ height = Math.max(height, maxY);
+ }
+ }
+ height = height + CALL_YGAP;
+ }
+ return height;
+ }
+
+ @Override
+ public void setLocation(Point2D aPoint)
+ {
+ INode parentNode = getParent();
+ // Special use case : when this node is connected to another activation bar on another life line,
+ // we adjust its location to keep it relative to the node it is connected to
+ if (parentNode != null && parentNode.getClass().isAssignableFrom(LifelineNode.class)) {
+ LifelineNode lifelineNode = getImplicitParameter();
+ Rectangle2D topRectangle = lifelineNode.getTopRectangle();
+ if (aPoint.getY() <= topRectangle.getHeight() + CALL_YGAP / 2) {
+ aPoint = new Point2D.Double(aPoint.getX(), topRectangle.getHeight() + CALL_YGAP / 2);
+ }
+ for (IEdge edge : getConnectedEdges()) {
+ if (!edge.getClass().isAssignableFrom(CallEdge.class))
+ {
+ continue;
+ }
+ if (edge.getEnd() == this)
+ {
+ INode startingNode = edge.getStart();
+ Point2D startingNodeLocationOnGraph = startingNode.getLocationOnGraph();
+ Point2D lifelineLocationOnGraph = getImplicitParameter().getLocationOnGraph();
+ this.verticalGapBetweenConnectedActivationBars = aPoint.getY() - startingNodeLocationOnGraph.getY() + lifelineLocationOnGraph.getY();
+ if (this.verticalGapBetweenConnectedActivationBars < CALL_YGAP / 2) {
+ double minY = startingNodeLocationOnGraph.getY() - lifelineLocationOnGraph.getY() + CALL_YGAP / 2;
+ aPoint = new Point2D.Double(aPoint.getX(), minY);
+ this.verticalGapBetweenConnectedActivationBars = CALL_YGAP / 2;
+ }
+ break;
+ }
+ }
+ }
+ super.setLocation(aPoint);
+ }
+
+ @Override
+ public Point2D getLocation()
+ {
+ INode parentNode = getParent();
+ // Case 1 : self call on activation bar
+ if (parentNode != null && parentNode.getClass().isAssignableFrom(ActivationBarNode.class)) {
+ Point2D rawLocation = super.getLocation();
+ double horizontalLocation = getHorizontalLocation();
+ double verticalLocation = rawLocation.getY();
+ verticalLocation = Math.max(verticalLocation, CALL_YGAP);
+ Point2D adjustedLocation = new Point2D.Double(horizontalLocation, verticalLocation);
+ super.setLocation(adjustedLocation);
+ return adjustedLocation;
+ }
+ // Case 2 : connected to another activation bar on another life line
+ if (parentNode != null && parentNode.getClass().isAssignableFrom(LifelineNode.class)) {
+ for (IEdge edge : getConnectedEdges()) {
+ if (!edge.getClass().isAssignableFrom(CallEdge.class))
+ {
+ continue;
+ }
+ if (edge.getEnd() == this)
+ {
+ INode startingNode = edge.getStart();
+ Point2D startingNodeLocationOnGraph = startingNode.getLocationOnGraph();
+ Point2D lifelineLocationOnGraph = getImplicitParameter().getLocationOnGraph();
+ double x = getHorizontalLocation();
+ double y = startingNodeLocationOnGraph.getY() - lifelineLocationOnGraph.getY() + this.verticalGapBetweenConnectedActivationBars;
+ Point2D adjustedLocation = new Point2D.Double(x, y);
+ super.setLocation(adjustedLocation);
+ return adjustedLocation;
+ }
+ }
+ }
+ // Case 3 : just attached to a lifeline
+ if (parentNode != null && parentNode.getClass().isAssignableFrom(LifelineNode.class)) {
+ Point2D rawLocation = super.getLocation();
+ double horizontalLocation = getHorizontalLocation();
+ double verticalLocation = rawLocation.getY();
+ Point2D adjustedLocation = new Point2D.Double(horizontalLocation, verticalLocation);
+ super.setLocation(adjustedLocation);
+ return adjustedLocation;
+ }
+
+ return super.getLocation();
+ }
+
+ @Override
+ public void draw(Graphics2D g2)
+ {
+ // Translate g2 if node has parent
+ Point2D nodeLocationOnGraph = getLocationOnGraph();
+ Point2D nodeLocation = getLocation();
+ Rectangle2D b = getBounds();
+ Point2D g2Location = new Point2D.Double(nodeLocationOnGraph.getX() - nodeLocation.getX(), nodeLocationOnGraph.getY()
+ - nodeLocation.getY());
+ g2.translate(g2Location.getX(), g2Location.getY());
+ // Perform painting
+ super.draw(g2);
+ Color oldColor = g2.getColor();
+ g2.setColor(Color.WHITE);
+ g2.fill(b);
+ g2.setColor(oldColor);
+ g2.draw(b);
+ // Restore g2 original location
+ g2.translate(-g2Location.getX(), -g2Location.getY());
+ // Reset location for next draw
+ // Draw its children
+ for (INode node : getChildren())
+ {
+ node.draw(g2);
+ }
+ }
+
+ /**
+ * Gets the participant's life line of this call. Note : method's name is ot set to getLifeLine to keep compatibility with older
+ * versions
+ *
+ * @return the participant's life line
+ */
+ public LifelineNode getImplicitParameter()
+ {
+ if (this.lifeline == null) {
+ INode aParent = this.getParent();
+ List nodeStack = new ArrayList();
+ nodeStack.add(aParent);
+ while (!nodeStack.isEmpty()) {
+ INode aNode = nodeStack.get(0);
+ if (LifelineNode.class.isInstance(aNode)) {
+ this.lifeline = (LifelineNode) aNode;
+ }
+ if (ActivationBarNode.class.isInstance(aNode)) {
+ INode aNodeParent = aNode.getParent();
+ nodeStack.add(aNodeParent);
+ }
+ nodeStack.remove(0);
+ }
+ }
+ return lifeline;
+ }
+
+ /**
+ * Sets the participant's life line of this call. Note : method's name is ot set to setLifeLine to keep compatibility with older
+ * versions
+ *
+ * @param newValue the participant's lifeline
+ */
+ public void setImplicitParameter(LifelineNode newValue)
+ {
+ // Nothing to do. Just here to keep compatibility.
+ }
+
+ private boolean isReturnEdgeAcceptable(ReturnEdge edge)
+ {
+ INode endingNode = edge.getEnd();
+ INode startingNode = edge.getStart();
+ Class extends INode> startingNodeClass = startingNode.getClass();
+ Class extends INode> endingNodeClass = endingNode.getClass();
+ if (!startingNodeClass.isAssignableFrom(ActivationBarNode.class)
+ || !endingNodeClass.isAssignableFrom(ActivationBarNode.class))
+ {
+ return false;
+ }
+ ActivationBarNode startingActivationBarNode = (ActivationBarNode) startingNode;
+ ActivationBarNode endingActivationBarNode = (ActivationBarNode) endingNode;
+ if (startingActivationBarNode.getImplicitParameter() == endingActivationBarNode.getImplicitParameter())
+ {
+ return false;
+ }
+ if (!isCalledNode())
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isCallEdgeAcceptable(CallEdge edge)
+ {
+ INode endingNode = edge.getEnd();
+ INode startingNode = edge.getStart();
+ Point2D endingNodePoint = edge.getEndLocation();
+ Class> startingNodeClass = (startingNode != null ? startingNode.getClass() : NullType.class);
+ Class> endingNodeClass = (endingNode != null ? endingNode.getClass() : NullType.class);
+ // Case 1 : classic connection between activation bars
+ if (startingNodeClass.isAssignableFrom(ActivationBarNode.class)
+ && endingNodeClass.isAssignableFrom(ActivationBarNode.class))
+ {
+ return true;
+ }
+ // Case 2 : an activation bar creates a new class instance
+ if (startingNodeClass.isAssignableFrom(ActivationBarNode.class) && endingNodeClass.isAssignableFrom(LifelineNode.class))
+ {
+ ActivationBarNode startingActivationBarNode = (ActivationBarNode) startingNode;
+ LifelineNode startingLifeLineNode = startingActivationBarNode.getImplicitParameter();
+ LifelineNode endingLifeLineNode = (LifelineNode) endingNode;
+ Rectangle2D topRectangle = endingLifeLineNode.getTopRectangle();
+ if (startingLifeLineNode != endingLifeLineNode && topRectangle.contains(endingNodePoint))
+ {
+ return true;
+ }
+ }
+ // Case 3 : classic connection between activation bars but the ending
+ // bar doesn't exist and need to be automatically created
+ if (startingNodeClass.isAssignableFrom(ActivationBarNode.class) && endingNodeClass.isAssignableFrom(LifelineNode.class))
+ {
+ ActivationBarNode startingActivationBarNode = (ActivationBarNode) startingNode;
+ LifelineNode startingLifeLineNode = startingActivationBarNode.getImplicitParameter();
+ LifelineNode endingLifeLineNode = (LifelineNode) endingNode;
+ Rectangle2D topRectangle = endingLifeLineNode.getTopRectangle();
+ if (startingLifeLineNode != endingLifeLineNode && !topRectangle.contains(endingNodePoint))
+ {
+ ActivationBarNode newActivationBar = new ActivationBarNode();
+ Point2D edgeStartLocation = edge.getEndLocation();
+ double x = edgeStartLocation.getX();
+ double y = edgeStartLocation.getY();
+ Point2D newActivationBarLocation = new Point2D.Double(x, y);
+ endingNode.addChild(newActivationBar, newActivationBarLocation);
+ edge.setEnd(newActivationBar);
+ return true;
+ }
+ }
+ // Case 4 : self call on an activation bar
+ if (startingNodeClass.isAssignableFrom(ActivationBarNode.class) && endingNodeClass.isAssignableFrom(LifelineNode.class))
+ {
+ ActivationBarNode startingActivationBarNode = (ActivationBarNode) startingNode;
+ LifelineNode startingLifeLineNode = startingActivationBarNode.getImplicitParameter();
+ LifelineNode endingLifeLineNode = (LifelineNode) endingNode;
+ Rectangle2D topRectangle = endingLifeLineNode.getTopRectangle();
+ if (startingLifeLineNode == endingLifeLineNode && !topRectangle.contains(endingNodePoint))
+ {
+ ActivationBarNode newActivationBar = new ActivationBarNode();
+ Point2D edgeStartLocation = edge.getStartLocation();
+ double x = edgeStartLocation.getX();
+ double y = edgeStartLocation.getY() + CALL_YGAP / 2;
+ Point2D newActivationBarLocation = new Point2D.Double(x, y);
+ startingNode.addChild(newActivationBar, newActivationBarLocation);
+ edge.setEnd(newActivationBar);
+ return true;
+ }
+ }
+ // Case 5 : self call on an activation bar but its child which is called
+ // doesn"t exist and need to be created automatically
+ if (startingNodeClass.isAssignableFrom(ActivationBarNode.class) && endingNodeClass.isAssignableFrom(NullType.class))
+ {
+ ActivationBarNode newActivationBar = new ActivationBarNode();
+ int lastNodePos = startingNode.getChildren().size();
+ startingNode.addChild(newActivationBar, lastNodePos);
+ edge.setEnd(newActivationBar);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Finds an edge in the graph connected to start and end nodes
+ *
+ * @param g the graph
+ * @param start the start node
+ * @param end the end node
+ * @return the edge or null if no one is found
+ */
+ private IEdge findEdge(INode start, INode end)
+ {
+ for (IEdge e : getGraph().getAllEdges())
+ {
+ if (e.getStart() == start && e.getEnd() == end) return e;
+ }
+ return null;
+ }
+
+ /**
+ * @return x location relative to the parent
+ */
+ private double getHorizontalLocation()
+ {
+ INode parentNode = getParent();
+ if (parentNode != null && parentNode.getClass().isAssignableFrom(ActivationBarNode.class))
+ {
+ return DEFAULT_WIDTH / 2;
+ }
+ if (parentNode != null && parentNode.getClass().isAssignableFrom(LifelineNode.class))
+ {
+ LifelineNode lifeLineNode = (LifelineNode) parentNode;
+ Rectangle2D lifeLineTopRectangle = lifeLineNode.getTopRectangle();
+ return lifeLineTopRectangle.getWidth() / 2 - DEFAULT_WIDTH / 2;
+ }
+ return 0;
+ }
+
+
+
+
+
+
+ /** The lifeline that embeds this activation bar in the sequence diagram */
+ private transient LifelineNode lifeline;
+
+ /** When this node is connected to another activation bar, we keep the vertical gap to be able to have ending node location relative to the starting one */
+ private double verticalGapBetweenConnectedActivationBars = CALL_YGAP / 2;
+
+ /** Default with */
+ private static int DEFAULT_WIDTH = 16;
+
+ /** Default height */
+ private static int DEFAULT_HEIGHT = 30;
+
+ /** Default vertical gap between two call nodes and a call node and an implicit node */
+ public static int CALL_YGAP = 20;
+}
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/CallEdge.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/CallEdge.java
new file mode 100644
index 0000000..3b5157e
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/CallEdge.java
@@ -0,0 +1,144 @@
+/*
+ Violet - A program for editing UML diagrams.
+
+ Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com)
+ Alexandre de Pellegrin (http://alexdp.free.fr);
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package com.horstmann.violet.product.diagram.sequence;
+
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+
+import com.horstmann.violet.product.diagram.abstracts.edge.SegmentedLineEdge;
+import com.horstmann.violet.product.diagram.abstracts.node.INode;
+import com.horstmann.violet.product.diagram.abstracts.property.ArrowHead;
+
+/**
+ * An edge that joins two call nodes. Typically, call edges are used in sequence diagram to represent calls between entities (call
+ * nodes).
+ */
+public class CallEdge extends SegmentedLineEdge
+{
+ /**
+ * Default constructor
+ */
+ public CallEdge()
+ {
+ setSignal(false);
+ }
+
+ /**
+ * Gets the signal property.
+ *
+ * @return true if this is an edge that represents an asynchronus signal
+ */
+ public boolean isSignal()
+ {
+ return signal;
+ }
+
+ /**
+ * Sets the signal property.
+ *
+ * @param newValue true if this is an edge that represents an asynchronus signal
+ */
+ public void setSignal(boolean newValue)
+ {
+ signal = newValue;
+
+ }
+
+ @Override
+ public ArrowHead getEndArrowHead()
+ {
+ if (signal)
+ {
+ return ArrowHead.V;
+ }
+ return ArrowHead.BLACK_TRIANGLE;
+ }
+
+ @Override
+ public Line2D getConnectionPoints()
+ {
+ ArrayList points = getPoints();
+ Point2D p1 = points.get(0);
+ Point2D p2 = points.get(points.size() - 1);
+ return new Line2D.Double(p1, p2);
+ }
+
+ @Override
+ public ArrayList getPoints()
+ {
+ INode endingNode = getEnd();
+ INode startingNode = getStart();
+ if (isActivationBarsOnSameLifeline(startingNode, endingNode))
+ {
+ return getPointsForLoopOnActivationBarNode(startingNode, endingNode);
+ }
+ return getPointsForActivationBarsOnDifferentLifeLines(startingNode, endingNode);
+ }
+
+ private boolean isActivationBarsOnSameLifeline(INode startingNode, INode endingNode)
+ {
+ if (startingNode.getClass().isAssignableFrom(ActivationBarNode.class)
+ && endingNode.getClass().isAssignableFrom(ActivationBarNode.class))
+ {
+ ActivationBarNode startingActivationBarNode = (ActivationBarNode) startingNode;
+ ActivationBarNode endingActivationBarNode = (ActivationBarNode) endingNode;
+ if (startingActivationBarNode.getImplicitParameter() == endingActivationBarNode.getImplicitParameter())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private ArrayList getPointsForActivationBarsOnDifferentLifeLines(INode startingNode, INode endingNode)
+ {
+ Point2D startingPoint = startingNode.getConnectionPoint(this);
+ Point2D endingPoint = endingNode.getConnectionPoint(this);
+ ArrayList a = new ArrayList();
+ a.add(startingPoint);
+ a.add(endingPoint);
+ return a;
+ }
+
+ private ArrayList getPointsForLoopOnActivationBarNode(INode startingNode, INode endingNode)
+ {
+ ArrayList a = new ArrayList();
+ Point2D startingPoint = startingNode.getConnectionPoint(this);
+ Point2D endingPoint = endingNode.getConnectionPoint(this);
+ Point2D p = startingPoint;
+ Point2D q = new Point2D.Double(endingPoint.getX() + LOOP_GAP, p.getY());
+ Point2D r = new Point2D.Double(q.getX(), endingPoint.getY());
+ Point2D s = endingPoint;
+ a.add(p);
+ a.add(q);
+ a.add(r);
+ a.add(s);
+ return a;
+ }
+
+ /** Indicate if the node represents an asynchonus signal */
+ private boolean signal;
+
+ /** Horizintal gap used to connected two activation bars on the same lifeline */
+ private static int LOOP_GAP = 15;
+}
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/CallEdgeBeanInfo.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/CallEdgeBeanInfo.java
new file mode 100644
index 0000000..aef2071
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/CallEdgeBeanInfo.java
@@ -0,0 +1,48 @@
+/*
+ Violet - A program for editing UML diagrams.
+
+ Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com)
+ Alexandre de Pellegrin (http://alexdp.free.fr);
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package com.horstmann.violet.product.diagram.sequence;
+
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.beans.SimpleBeanInfo;
+
+/**
+ * The bean info for the CallEdge type.
+ */
+public class CallEdgeBeanInfo extends SimpleBeanInfo
+{
+ public PropertyDescriptor[] getPropertyDescriptors()
+ {
+ try
+ {
+ return new PropertyDescriptor[]
+ {
+ new PropertyDescriptor("middleLabel", CallEdge.class),
+ new PropertyDescriptor("signal", CallEdge.class)
+ };
+ }
+ catch (IntrospectionException exception)
+ {
+ return null;
+ }
+ }
+}
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/LifelineNode.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/LifelineNode.java
new file mode 100644
index 0000000..680f657
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/LifelineNode.java
@@ -0,0 +1,326 @@
+/*
+ Violet - A program for editing UML diagrams.
+
+ Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com)
+ Alexandre de Pellegrin (http://alexdp.free.fr);
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package com.horstmann.violet.product.diagram.sequence;
+
+import java.awt.BasicStroke;
+import java.awt.Graphics2D;
+import java.awt.Stroke;
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.Collection;
+import java.util.List;
+
+import com.horstmann.violet.product.diagram.abstracts.Direction;
+import com.horstmann.violet.product.diagram.abstracts.IGraph;
+import com.horstmann.violet.product.diagram.abstracts.edge.IEdge;
+import com.horstmann.violet.product.diagram.abstracts.node.INode;
+import com.horstmann.violet.product.diagram.abstracts.node.RectangularNode;
+import com.horstmann.violet.product.diagram.abstracts.property.MultiLineString;
+
+/**
+ * An object node in a scenario diagram.
+ */
+public class LifelineNode extends RectangularNode
+{
+ /**
+ * Construct an object node with a default size
+ */
+ public LifelineNode()
+ {
+ name = new MultiLineString();
+ name.setUnderlined(true);
+ }
+
+ /**
+ * Sets the name property value.
+ *
+ * @param newValue the name of this object
+ */
+ public void setName(MultiLineString n)
+ {
+ name = n;
+ }
+
+ /**
+ * Gets the name property value.
+ *
+ * @return the name of this object
+ */
+ public MultiLineString getName()
+ {
+ return name;
+ }
+
+ public boolean addConnection(IEdge e)
+ {
+ return false;
+ }
+
+ @Override
+ public boolean addChild(INode n, Point2D p)
+ {
+ if (!n.getClass().isAssignableFrom(ActivationBarNode.class))
+ {
+ return false;
+ }
+ n.setParent(this);
+ n.setGraph(getGraph());
+ n.setLocation(p);
+ addChild(n, getChildren().size());
+ return true;
+ }
+
+ /**
+ * Looks for the node which is located just after the given point
+ *
+ * @param p
+ * @return the node we found or null if there's no node after this point
+ */
+ private INode getNearestNodeAfterThisPoint(Point2D p)
+ {
+ double y = p.getY();
+ INode nearestNodeAfterThisPoint = null;
+ // Step 1 : we look for the closest node
+ for (INode childNode : getChildren())
+ {
+ if (nearestNodeAfterThisPoint == null)
+ {
+ nearestNodeAfterThisPoint = childNode;
+ }
+ Point2D childLocation = childNode.getLocation();
+ Point2D nearestNodeLocation = nearestNodeAfterThisPoint.getLocation();
+ double childY = childLocation.getY();
+ double nearestY = nearestNodeLocation.getY();
+ double currentNodeGap = childY - y;
+ double nearestNodeGap = nearestY - y;
+ if (currentNodeGap > 0 && Math.abs(currentNodeGap) < Math.abs(nearestNodeGap))
+ {
+ nearestNodeAfterThisPoint = childNode;
+ }
+ }
+ // Step 2 : if nothing found, we return null
+ if (nearestNodeAfterThisPoint == null)
+ {
+ return null;
+ }
+ // Step 3 : as by default we set the first child node as the nearest one
+ // We check if it is not before p
+ Point2D nearestChildLocation = nearestNodeAfterThisPoint.getLocation();
+ if (y > nearestChildLocation.getY())
+ {
+ return null;
+ }
+ // Step 4 : we return the closest node after p
+ return nearestNodeAfterThisPoint;
+ }
+
+ @Override
+ public Point2D getConnectionPoint(IEdge e)
+ {
+ Direction d = e.getDirection(this);
+ Point2D locationOnGraph = getLocationOnGraph();
+ Rectangle2D topRectBounds = getTopRectangle();
+ if (d.getX() > 0)
+ {
+ return new Point2D.Double(locationOnGraph.getX(), locationOnGraph.getY() + topRectBounds.getHeight() / 2);
+ }
+ return new Point2D.Double(locationOnGraph.getX() + topRectBounds.getWidth(), locationOnGraph.getY() + topRectBounds.getHeight() / 2);
+ }
+
+ @Override
+ public Point2D getLocation()
+ {
+ IGraph currentGraph = getGraph();
+ if (currentGraph == null)
+ {
+ return new Point2D.Double(0, 0);
+ }
+ Collection edges = currentGraph.getAllEdges();
+ for (IEdge edge : edges)
+ {
+ if (edge instanceof CallEdge)
+ {
+ INode endingNode = edge.getEnd();
+ if (endingNode == this)
+ {
+ INode startingNode = edge.getStart();
+ Point2D locationOnGraph = startingNode.getLocationOnGraph();
+ Point2D realLocation = super.getLocation();
+ Point2D fixedLocation = new Point2D.Double(realLocation.getX(), locationOnGraph.getY()
+ - getTopRectangleHeight() / 2 + ActivationBarNode.CALL_YGAP / 2);
+ return fixedLocation;
+ }
+ }
+ }
+ Point2D realLocation = super.getLocation();
+ Point2D fixedLocation = new Point2D.Double(realLocation.getX(), 0);
+ return fixedLocation;
+ }
+
+ /**
+ * Returns the rectangle at the top of the object node.
+ *
+ * @return the top rectangle
+ */
+ public Rectangle2D getTopRectangle()
+ {
+ double topWidth = getTopRectangleWidth();
+ double topHeight = getTopRectangleHeight();
+ Rectangle2D topRectangle = new Rectangle2D.Double(0, 0, topWidth, topHeight);
+ Rectangle2D snappedRectangle = getGraph().getGrid().snap(topRectangle);
+ return snappedRectangle;
+ }
+
+ private double getTopRectangleHeight()
+ {
+ Rectangle2D bounds = name.getBounds();
+ double topHeight = Math.max(bounds.getHeight(), DEFAULT_TOP_HEIGHT);
+ return topHeight;
+ }
+
+ private double getTopRectangleWidth()
+ {
+ Rectangle2D bounds = name.getBounds();
+ double topWidth = Math.max(bounds.getWidth(), DEFAULT_WIDTH);
+ return topWidth;
+ }
+
+ @Override
+ public Rectangle2D getBounds()
+ {
+ double topRectWidth = getTopRectangle().getWidth();
+ double height = getLocalHeight();
+ Point2D nodeLocation = getLocation();
+ Rectangle2D bounds = new Rectangle2D.Double(nodeLocation.getX(), nodeLocation.getY(), topRectWidth, height);
+ Rectangle2D scaledBounds = getScaledBounds(bounds);
+ Rectangle2D snappedBounds = getGraph().getGrid().snap(scaledBounds);
+ return snappedBounds;
+ }
+
+ public double getLocalHeight() {
+ double topRectHeight = getTopRectangle().getHeight();
+ double height = topRectHeight; // default initial height
+ List children = getChildren();
+ for (INode n : children)
+ {
+ if (n.getClass().isAssignableFrom(ActivationBarNode.class))
+ {
+ // We are looking for the last activation bar node to get the total height needed
+ height = Math.max(height, n.getBounds().getMaxY());
+ }
+ }
+ height = height + ActivationBarNode.CALL_YGAP * 2;
+ return height;
+ }
+
+ private Rectangle2D getScaledBounds(Rectangle2D bounds)
+ {
+ double x = bounds.getX();
+ double y = bounds.getY();
+ double w = bounds.getWidth();
+ double h = bounds.getHeight();
+ double diffY = this.maxYOverAllLifeLineNodes - bounds.getMaxY();
+ if (diffY > 0)
+ {
+ h = h + diffY;
+ }
+ return new Rectangle2D.Double(x, y, w, h);
+ }
+
+ @Override
+ public Rectangle2D getShape()
+ {
+ Point2D currentLocation = getLocation();
+ Rectangle2D topRectangle = getTopRectangle();
+ double x = currentLocation.getX();
+ double y = currentLocation.getY();
+ double w = topRectangle.getWidth();
+ double h = topRectangle.getHeight();
+ return new Rectangle2D.Double(x, y, w, h);
+ }
+
+ public void draw(Graphics2D g2)
+ {
+ super.draw(g2);
+ Rectangle2D top = getShape();
+ g2.draw(top);
+ name.draw(g2, top);
+ double xmid = top.getCenterX();
+ Line2D line = new Line2D.Double(xmid, top.getMaxY(), xmid, getMaxYOverAllLifeLineNodes());
+ Stroke oldStroke = g2.getStroke();
+ g2.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0.0f, new float[]
+ {
+ 5.0f,
+ 5.0f
+ }, 0.0f));
+ g2.draw(line);
+ g2.setStroke(oldStroke);
+ // Draw its children
+ for (INode node : getChildren())
+ {
+ node.draw(g2);
+ }
+ }
+
+ private double getMaxYOverAllLifeLineNodes()
+ {
+ double maxY = this.getLocalHeight();
+ IGraph graph = getGraph();
+ if (graph == null)
+ {
+ return maxY;
+ }
+ Collection nodes = graph.getAllNodes();
+ for (INode node : nodes)
+ {
+ if (!node.getClass().isAssignableFrom(LifelineNode.class))
+ {
+ continue;
+ }
+ LifelineNode aLifeLineNode = (LifelineNode) node;
+ double localY = aLifeLineNode.getLocalHeight() + aLifeLineNode.getLocationOnGraph().getY();
+ maxY = Math.max(maxY, localY);
+ }
+ this.maxYOverAllLifeLineNodes = maxY;
+ return maxY;
+ }
+
+ public boolean contains(Point2D p)
+ {
+ Rectangle2D bounds = getBounds();
+ return bounds.getX() <= p.getX() && p.getX() <= bounds.getX() + bounds.getWidth();
+ }
+
+ public LifelineNode clone()
+ {
+ LifelineNode cloned = (LifelineNode) super.clone();
+ cloned.name = name.clone();
+ return cloned;
+ }
+
+ private MultiLineString name;
+ private transient double maxYOverAllLifeLineNodes = 0;
+ private static int DEFAULT_TOP_HEIGHT = 60;
+ private static int DEFAULT_WIDTH = 80;
+ private static int DEFAULT_HEIGHT = 120;
+}
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/ReturnEdge.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/ReturnEdge.java
new file mode 100644
index 0000000..906fbda
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/ReturnEdge.java
@@ -0,0 +1,78 @@
+/*
+ Violet - A program for editing UML diagrams.
+
+ Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com)
+ Alexandre de Pellegrin (http://alexdp.free.fr);
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package com.horstmann.violet.product.diagram.sequence;
+
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+
+import com.horstmann.violet.product.diagram.abstracts.edge.SegmentedLineEdge;
+import com.horstmann.violet.product.diagram.abstracts.node.INode;
+import com.horstmann.violet.product.diagram.abstracts.property.ArrowHead;
+import com.horstmann.violet.product.diagram.abstracts.property.LineStyle;
+
+/**
+ * An edge that joins two call nodes.
+ */
+public class ReturnEdge extends SegmentedLineEdge
+{
+
+ @Override
+ public ArrowHead getEndArrowHead()
+ {
+ return ArrowHead.V;
+ }
+
+ @Override
+ public LineStyle getLineStyle()
+ {
+ return LineStyle.DOTTED;
+ }
+
+ @Override
+ public Line2D getConnectionPoints()
+ {
+ ArrayList points = getPoints();
+ Point2D p1 = points.get(0);
+ Point2D p2 = points.get(points.size() - 1);
+ return new Line2D.Double(p1, p2);
+ }
+
+ @Override
+ public ArrayList getPoints()
+ {
+ INode endingNode = getEnd();
+ INode startingNode = getStart();
+ return getPointsForNodesOnDifferentLifeLines(startingNode, endingNode);
+ }
+
+ private ArrayList getPointsForNodesOnDifferentLifeLines(INode startingNode, INode endingNode)
+ {
+ Point2D startingPoint = startingNode.getConnectionPoint(this);
+ Point2D endingPoint = endingNode.getConnectionPoint(this);
+ ArrayList a = new ArrayList();
+ a.add(startingPoint);
+ a.add(endingPoint);
+ return a;
+ }
+
+}
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramConstant.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramConstant.java
new file mode 100644
index 0000000..5e34bd8
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramConstant.java
@@ -0,0 +1,8 @@
+package com.horstmann.violet.product.diagram.sequence;
+
+public interface SequenceDiagramConstant
+{
+
+ public static final String SEQUENCE_DIAGRAM_STRINGS = "properties.SequenceDiagramGraphStrings";
+
+}
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramGraph.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramGraph.java
new file mode 100644
index 0000000..c5562ed
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramGraph.java
@@ -0,0 +1,105 @@
+/*
+ Violet - A program for editing UML diagrams.
+
+ Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com)
+ Alexandre de Pellegrin (http://alexdp.free.fr);
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package com.horstmann.violet.product.diagram.sequence;
+
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import com.horstmann.violet.product.diagram.abstracts.AbstractGraph;
+import com.horstmann.violet.product.diagram.abstracts.edge.IEdge;
+import com.horstmann.violet.product.diagram.abstracts.node.INode;
+import com.horstmann.violet.product.diagram.common.DiagramLinkNode;
+import com.horstmann.violet.product.diagram.common.NoteEdge;
+import com.horstmann.violet.product.diagram.common.NoteNode;
+
+/**
+ * A UML sequence diagram.
+ */
+public class SequenceDiagramGraph extends AbstractGraph
+{
+
+
+
+
+ @Override
+ public boolean addNode(INode newNode, Point2D p)
+ {
+ INode foundNode = findNode(p);
+ if (foundNode == null && newNode.getClass().isAssignableFrom(ActivationBarNode.class)) {
+ return false;
+ }
+ return super.addNode(newNode, p);
+ }
+
+
+
+ public List getNodePrototypes()
+ {
+ return NODE_PROTOTYPES;
+ }
+
+ public List getEdgePrototypes()
+ {
+ return EDGE_PROTOTYPES;
+ }
+
+ private static final List NODE_PROTOTYPES = new ArrayList();
+
+ private static final List EDGE_PROTOTYPES = new ArrayList();
+
+ static
+ {
+ ResourceBundle rs = ResourceBundle.getBundle(SequenceDiagramConstant.SEQUENCE_DIAGRAM_STRINGS, Locale.getDefault());
+
+ LifelineNode lifelineNode = new LifelineNode();
+ lifelineNode.setToolTip(rs.getString("node0.tooltip"));
+ NODE_PROTOTYPES.add(lifelineNode);
+
+ ActivationBarNode activationBarNode = new ActivationBarNode();
+ activationBarNode.setToolTip(rs.getString("node1.tooltip"));
+ NODE_PROTOTYPES.add(activationBarNode);
+
+ NoteNode noteNode = new NoteNode();
+ noteNode.setToolTip(rs.getString("node2.tooltip"));
+ NODE_PROTOTYPES.add(noteNode);
+
+ DiagramLinkNode diagramLinkNode = new DiagramLinkNode();
+ diagramLinkNode.setToolTip(rs.getString("node3.tooltip"));
+ NODE_PROTOTYPES.add(diagramLinkNode);
+
+ CallEdge callEdge = new CallEdge();
+ callEdge.setToolTip(rs.getString("edge0.tooltip"));
+ EDGE_PROTOTYPES.add(callEdge);
+
+ ReturnEdge returnEdge = new ReturnEdge();
+ returnEdge.setToolTip(rs.getString("edge1.tooltip"));
+ EDGE_PROTOTYPES.add(returnEdge);
+
+ NoteEdge noteEdge = new NoteEdge();
+ noteEdge.setToolTip(rs.getString("edge2.tooltip"));
+ EDGE_PROTOTYPES.add(noteEdge);
+ }
+
+}
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramPlugin.java b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramPlugin.java
new file mode 100644
index 0000000..1fd221b
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/java/com/horstmann/violet/product/diagram/sequence/SequenceDiagramPlugin.java
@@ -0,0 +1,79 @@
+package com.horstmann.violet.product.diagram.sequence;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import com.horstmann.violet.framework.plugin.IDiagramPlugin;
+import com.horstmann.violet.framework.plugin.extensionpoint.Violet016FileFilterExtensionPoint;
+import com.horstmann.violet.product.diagram.abstracts.IGraph;
+
+/**
+ * Describes sequence diagram graph type
+ *
+ * @author Alexandre de Pellegrin
+ *
+ */
+public class SequenceDiagramPlugin implements IDiagramPlugin, Violet016FileFilterExtensionPoint
+{
+
+ @Override
+ public String getDescription()
+ {
+ return "Sequence UML diagram";
+ }
+
+ @Override
+ public String getProvider()
+ {
+ return "Alexandre de Pellegrin / Cays S. Horstmann";
+ }
+
+ @Override
+ public String getVersion()
+ {
+ return "1.0.0";
+ }
+
+ @Override
+ public String getName()
+ {
+ return this.rs.getString("menu.sequence_diagram.text");
+ }
+
+ @Override
+ public String getFileExtension()
+ {
+ return this.rs.getString("files.seq.extension");
+ }
+
+ @Override
+ public String getFileExtensionName()
+ {
+ return this.rs.getString("files.seq.name");
+ }
+
+ @Override
+ public Class extends IGraph> getGraphClass()
+ {
+ return SequenceDiagramGraph.class;
+ }
+
+
+
+ public Map getMappingToKeepViolet016Compatibility()
+ {
+ Map replaceMap = new HashMap();
+ replaceMap.put("com.horstmann.violet.CallEdge", CallEdge.class.getName());
+ replaceMap.put("com.horstmann.violet.CallNode", ActivationBarNode.class.getName());
+ replaceMap.put("com.horstmann.violet.ImplicitParameterNode", LifelineNode.class.getName());
+ replaceMap.put("com.horstmann.violet.ReturnEdge", ReturnEdge.class.getName());
+ replaceMap.put("com.horstmann.violet.SequenceDiagramGraph", SequenceDiagramGraph.class.getName());
+ return replaceMap;
+ }
+
+ private ResourceBundle rs = ResourceBundle.getBundle(SequenceDiagramConstant.SEQUENCE_DIAGRAM_STRINGS, Locale.getDefault());
+
+
+}
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/META-INF/services/com.horstmann.violet.framework.plugin.IDiagramPlugin b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/META-INF/services/com.horstmann.violet.framework.plugin.IDiagramPlugin
new file mode 100644
index 0000000..ef7ea16
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/META-INF/services/com.horstmann.violet.framework.plugin.IDiagramPlugin
@@ -0,0 +1 @@
+com.horstmann.violet.product.diagram.sequence.SequenceDiagramPlugin
\ No newline at end of file
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/properties/SequenceDiagramGraphStrings.properties b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/properties/SequenceDiagramGraphStrings.properties
new file mode 100644
index 0000000..05f01e4
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/properties/SequenceDiagramGraphStrings.properties
@@ -0,0 +1,10 @@
+node0.tooltip=Object lifeline
+node1.tooltip=Activation bar
+node2.tooltip=Note
+node3.tooltip=Linked diagram
+edge0.tooltip=Call / Create message
+edge1.tooltip=Return message
+edge2.tooltip=Note connector
+menu.sequence_diagram.text=Sequence diagram
+files.seq.name=Sequence Diagram Files
+files.seq.extension=.seq.violet
diff --git a/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/properties/SequenceDiagramGraphStrings_fr.properties b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/properties/SequenceDiagramGraphStrings_fr.properties
new file mode 100644
index 0000000..3e6bb8c
--- /dev/null
+++ b/VioletPluginSequenceDiagram/VioletPlugin.SequenceDiagram/src/main/resources/properties/SequenceDiagramGraphStrings_fr.properties
@@ -0,0 +1,10 @@
+node0.tooltip=Ligne de vie de l'objet
+node1.tooltip=Barre d'activation
+node2.tooltip=Note
+node3.tooltip=Diagramme li\u00E9
+edge0.tooltip=Message d'appel / de cr\u00E9ation
+edge1.tooltip=Message de retour
+edge2.tooltip=Connexion \u00E0 la note
+menu.sequence_diagram.text=Diagramme de s\u00e9quence
+files.seq.name=Diagramme de s\u00e9quence
+files.seq.extension=.seq.violet
\ No newline at end of file