/* 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.node; import java.awt.*; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.List; import com.horstmann.violet.framework.graphics.content.*; import com.horstmann.violet.framework.graphics.shape.ContentInsideRectangle; import com.horstmann.violet.product.diagram.abstracts.node.AbstractNode; import com.horstmann.violet.product.diagram.common.node.ColorableNode; import com.horstmann.violet.product.diagram.property.ArrowheadChoiceList; import com.horstmann.violet.product.diagram.property.text.LineText; import com.horstmann.violet.product.diagram.property.text.decorator.*; import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; import com.horstmann.violet.product.diagram.abstracts.node.INode; import com.horstmann.violet.product.diagram.property.text.SingleLineText; import com.horstmann.violet.product.diagram.sequence.SequenceDiagramConstant; import com.horstmann.violet.product.diagram.sequence.edge.CallEdge; /** * An object node_old in a scenario diagram. * * @author Adrian Bobrowski <adrian071993@gmail.com> */ public class LifelineNode extends ColorableNode { /** * Construct an object node_old with a default size */ public LifelineNode() { super(); name = new SingleLineText(nameConverter); name.setPadding(5, 10, 5, 2); type = new SingleLineText(typeConverter); type.setPadding(5, 2, 5, 10); createContentStructure(); } protected LifelineNode(LifelineNode node) throws CloneNotSupportedException { super(node); name = node.name.clone(); type = node.type.clone(); createContentStructure(); } @Override protected void beforeReconstruction() { super.beforeReconstruction(); if(null==name) { name = new SingleLineText(); } if(null==type) { type = new SingleLineText(); } name.reconstruction(nameConverter); type.reconstruction(typeConverter); name.setPadding(5, 10, 5, 2); type.setPadding(5, 2, 5, 10); } @Override protected void afterReconstruction() { for(INode child : getChildren()) { if (child instanceof ActivationBarNode) { child.reconstruction(); activationsGroup.add(((ActivationBarNode) child).getContent()); onChildChangeLocation(child); } } super.afterReconstruction(); } @Override protected INode copy() throws CloneNotSupportedException { return new LifelineNode(this); } @Override protected void createContentStructure() { TextContent nameContent = new TextContent(name); TextContent typeContent = new TextContent(type); nameContent.setMinHeight(TOP_HEIGHT); nameContent.setMinWidth(5); typeContent.setMinHeight(TOP_HEIGHT); typeContent.setMinWidth(5); HorizontalLayout horizontalLayout = new HorizontalLayout(); horizontalLayout.add(nameContent); horizontalLayout.add(typeContent); CenterContent centerContent = new CenterContent(horizontalLayout); centerContent.setMinWidth(MIN_WIDTH); ContentInsideShape contentInsideShape = new ContentInsideRectangle(centerContent); setBorder(new ContentBorder(contentInsideShape, getBorderColor())); setBackground(new ContentBackground(getBorder(), getBackgroundColor())); activationsGroup = new RelativeLayout(); activationsGroup.setMinWidth(ActivationBarNode.WIDTH); EmptyContent padding = new EmptyContent(); padding.setMinHeight(ACTIVATIONS_PADDING); VerticalLayout verticalLayout = new VerticalLayout(); verticalLayout.add(getBackground()); verticalLayout.add(padding); verticalLayout.add(activationsGroup); verticalLayout.add(padding); verticalLayout.setMinHeight(MIN_HEIGHT); setContent(verticalLayout); setTextColor(getTextColor()); setName(getName()); } @Override public void setTextColor(Color textColor) { name.setTextColor(textColor); type.setTextColor(textColor); super.setTextColor(textColor); } @Override public String getToolTip() { return SequenceDiagramConstant.SEQUENCE_DIAGRAM_RESOURCE.getString("tooltip.lifeline_node"); } @Override public Rectangle2D getBounds() { Rectangle2D bounds = super.getBounds(); return new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), getMaxYOverAllLifeLineNodes()-bounds.getY()); } @Override public void removeChild(INode node) { activationsGroup.remove(((ActivationBarNode) node).getContent()); super.removeChild(node); } @Override public boolean addChild(INode node, Point2D point) { List<INode> activations = getChildren(); if (!(node instanceof ActivationBarNode)) { return false; } if (activations.contains(node)) { return true; } addChild(node, activations.size()); ActivationBarNode activationBarNode = (ActivationBarNode) node; activationBarNode.setTextColor(getTextColor()); activationBarNode.setBackgroundColor(getBackgroundColor()); activationBarNode.setBorderColor(getBorderColor()); activationsGroup.add(activationBarNode.getContent()); activationBarNode.setLocation(point); activationBarNode.setGraph(getGraph()); activationBarNode.setParent(this); return true; } /** * Ensure that child node_old respects the minimum gap with package borders * * @param child */ protected void onChildChangeLocation(INode child) { activationsGroup.setPosition(((AbstractNode) child).getContent(), getChildRelativeLocation(child)); } protected Point2D getChildRelativeLocation(INode node) { double relativeCenteredX = getRelativeCenteredPositionX(); Point2D nodeLocation = node.getLocation(); if(TOP_HEIGHT + ACTIVATIONS_PADDING > nodeLocation.getY() || relativeCenteredX != nodeLocation.getX()) { nodeLocation.setLocation(relativeCenteredX, Math.max(nodeLocation.getY(), TOP_HEIGHT + ACTIVATIONS_PADDING)); node.setLocation(nodeLocation); } return new Point2D.Double(nodeLocation.getX(), nodeLocation.getY() - TOP_HEIGHT - ACTIVATIONS_PADDING); } @Override public Point2D getLocation() { double y = 0; for (IEdge edge : getGraph().getAllEdges()) { if (edge instanceof CallEdge) { if (this == edge.getEndNode()) { y = edge.getStartNode().getLocationOnGraph().getY() - TOP_HEIGHT /2 + ActivationBarNode.CALL_Y_GAP / 2; break; } } } return new Point2D.Double(super.getLocation().getX(), y); } public void draw(Graphics2D graphics) { Rectangle2D bounds = getBounds(); Point2D startPoint = new Point2D.Double(bounds.getCenterX(), bounds.getMinY()); Point2D endPoint = new Point2D.Double(bounds.getCenterX(), getMaxYOverAllLifeLineNodes()); Color oldColor = graphics.getColor(); Stroke oldStroke = graphics.getStroke(); graphics.setColor(getBorderColor()); if(!name.toEdit().isEmpty()) { graphics.setStroke(new BasicStroke( 1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0.0f, new float[]{5.0f, 5.0f}, 0.0f )); } graphics.draw(new Line2D.Double(startPoint, endPoint)); graphics.setStroke(oldStroke); if(endOfLife) { ArrowheadChoiceList.X.draw(graphics, startPoint, endPoint); } graphics.setColor(oldColor); super.draw(graphics); } public boolean contains(Point2D p) { double maxYOverAllLifeLineNodes = getMaxYOverAllLifeLineNodes(); Rectangle2D bounds = getBounds(); if((maxYOverAllLifeLineNodes >= p.getY() && ActivationBarNode.WIDTH /2 >= p.getX() - bounds.getCenterX() && ActivationBarNode.WIDTH /2 >= bounds.getCenterX() - p.getX()) || (bounds.getX() <= p.getX() && p.getX() <= bounds.getX() + bounds.getWidth())) { return true; } return false; } @Override public boolean addConnection(IEdge edge) { return false; } @Override public Point2D getConnectionPoint(IEdge edge) { Point2D locationOnGraph = getLocationOnGraph(); double x = locationOnGraph.getX(); if (0 <= edge.getDirection(this).getX()) { x += getContent().getWidth(); } return new Point2D.Double(x, locationOnGraph.getY() + TOP_HEIGHT / 2); } private double getMaxY() { return getContent().getHeight() + getLocationOnGraph().getY(); } private double getMaxYOverAllLifeLineNodes() { double maxY = getMaxY(); for (INode node : getGraph().getAllNodes()) { if (node instanceof LifelineNode) { maxY = Math.max(maxY, ((LifelineNode) node).getMaxY() + ACTIVATIONS_PADDING); } } return maxY; } private void centeredActivationsGroup() { double relativeCenteredX = getRelativeCenteredPositionX(); for(INode child : getChildren()) { child.setLocation(new Point.Double( relativeCenteredX, child.getLocation().getY() )); } } private double getRelativeCenteredPositionX() { return (getContent().getWidth()- ActivationBarNode.WIDTH)/2; } /** * Sets the name property value. * * @param newValue the name of this object */ public void setName(LineText newValue) { name.setText(newValue.toEdit()); setType(getType()); // centeredActivationsGroup(); } /** * Gets the name property value. * * @return the name of this object */ public LineText getName() { return name; } /** * Sets the type property value. * * @param newValue the type of this object */ public void setType(LineText newValue) { type.setText(newValue.toEdit()); centeredActivationsGroup(); } /** * Gets the type property value. * * @return the name of this object */ public LineText getType() { return type; } /** * Sets the end of life property value. * * @param newValue the end of life of this object */ public void setEndOfLife(boolean newValue) { endOfLife = newValue; } /** * Gets the end of life property value. * * @return the end of life of this object */ public boolean isEndOfLife() { return endOfLife; } private SingleLineText name; private SingleLineText type; private boolean endOfLife; private transient RelativeLayout activationsGroup = null; public static final int TOP_HEIGHT = 60; private static final int MIN_WIDTH = 100; private static final int MIN_HEIGHT = 100; private static final int ACTIVATIONS_PADDING = 15; private static final LineText.Converter nameConverter = new LineText.Converter(){ @Override public OneLineText toLineString(String text) { return new LargeSizeDecorator(new OneLineText(text)); } }; private static final LineText.Converter typeConverter = new LineText.Converter(){ @Override public OneLineText toLineString(String text) { return new LargeSizeDecorator(new RemoveSentenceDecorator(new PrefixDecorator(new OneLineText(text), ":"), " ")); } }; }