/*
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.object.node;
import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.List;
import com.horstmann.violet.framework.graphics.Separator;
import com.horstmann.violet.framework.graphics.content.*;
import com.horstmann.violet.framework.graphics.content.HorizontalLayout;
import com.horstmann.violet.framework.graphics.shape.ContentInsideRectangle;
import com.horstmann.violet.product.diagram.abstracts.edge.IEdge;
import com.horstmann.violet.product.diagram.common.node.ColorableNode;
import com.horstmann.violet.product.diagram.abstracts.node.INode;
import com.horstmann.violet.product.diagram.object.ObjectDiagramConstant;
import com.horstmann.violet.product.diagram.object.edge.AssociationEdge;
import com.horstmann.violet.product.diagram.property.text.LineText;
import com.horstmann.violet.product.diagram.property.text.SingleLineText;
import com.horstmann.violet.product.diagram.object.edge.ObjectReferenceEdge;
/**
* A field node_old in an object diagram.
*/
public class FieldNode extends ColorableNode
{
/**
* Default constructor
*/
public FieldNode()
{
super();
name = new SingleLineText();
name.setAlignment(LineText.RIGHT);
name.setPadding(0, 10, 0, 15);
value = new SingleLineText();
value.setAlignment(LineText.LEFT);
value.setPadding(0, 15, 0, 10);
createContentStructure();
}
protected FieldNode(FieldNode node) throws CloneNotSupportedException
{
super(node);
name = node.name.clone();
value = node.value.clone();
createContentStructure();
}
@Override
protected void beforeReconstruction()
{
super.beforeReconstruction();
if(null == name)
{
name = new SingleLineText();
}
if(null == value)
{
value = new SingleLineText();
}
name.reconstruction();
value.reconstruction();
name.setPadding(0, 10, 0, 15);
value.setPadding(0, 15, 0, 10);
}
@Override
protected INode copy() throws CloneNotSupportedException
{
return new FieldNode(this);
}
@Override
protected void createContentStructure()
{
TextContent nameContent = new TextContent(name);
nameContent.setMinHeight(DEFAULT_HEIGHT);
nameContent.setMinWidth(DEFAULT_WIDTH/2);
TextContent valueContent = new TextContent(value);
valueContent.setMinHeight(DEFAULT_HEIGHT);
valueContent.setMinWidth(DEFAULT_WIDTH/2);
HorizontalLayout horizontalGroupContent = new HorizontalLayout();
horizontalGroupContent.add(nameContent);
horizontalGroupContent.add(valueContent);
horizontalGroupContent.setSeparator(EQUAL_SEPARATOR);
ContentInsideShape contentInsideShape = new ContentInsideRectangle(horizontalGroupContent);
setBorder(new ContentBorder(contentInsideShape, null));
setBackground(new ContentBackground(contentInsideShape, getBackgroundColor()));
RelativeLayout relativeLayout =new RelativeLayout();
relativeLayout.add(getBackground(), new Point2D.Double(1,0));
setContent(new ContentInsideRectangle(relativeLayout));
}
@Override
public Rectangle2D getBounds()
{
Point2D location = getLocationOnGraph();
Rectangle2D contentBounds = getContent().getBounds();
return new Rectangle2D.Double(location.getX(), location.getY(), contentBounds.getWidth(), contentBounds.getHeight());
}
@Override
public void setTextColor(Color textColor)
{
name.setTextColor(textColor);
value.setTextColor(textColor);
super.setTextColor(textColor);
}
@Override
public String getToolTip()
{
return ObjectDiagramConstant.OBJECT_DIAGRAM_RESOURCE.getString("tooltip.field_node");
}
@Override
public Point2D getLocation()
{
INode parent = getParent();
if (parent == null )
{
return new Point2D.Double(0, 0);
}
if (!(parent instanceof ObjectNode))
{
throw new IllegalStateException("Field node can be only ObjectNode child");
}
Point2D location = ((ObjectNode)parent).getFieldsGroup().getLocation(getContent());
return new Point2D.Double(location.getX(), location.getY()+((ObjectNode)parent).getFieldsTopOffset());
}
@Override
public boolean addConnection(IEdge edge)
{
INode endingINode = edge.getEndNode();
if (edge.getClass().isAssignableFrom(ObjectReferenceEdge.class) && endingINode.getClass().isAssignableFrom(ObjectNode.class))
{
value.setText( "" );
return true;
}
// Hack to allow drawing relationship edge over fields
if (edge instanceof AssociationEdge)
{
INode startingNode = edge.getStartNode();
INode endingNode = edge.getEndNode();
if (startingNode.getClass().isAssignableFrom(FieldNode.class))
{
startingNode = startingNode.getParent();
}
if (endingNode.getClass().isAssignableFrom(FieldNode.class))
{
endingNode = endingNode.getParent();
}
edge.setStartNode(startingNode);
edge.setEndNode(endingNode);
return getParent().addConnection(edge);
}
return false;
}
/**
* Hack to be able to add fields on object when we do a single click on another field READ THIS : due to this hack, when you
* dble click to edit this field, the first click triggers this methods (which is a correct framework behavior). The workaround
* for end users is to use right click instead of dble click to edit fields. It is so simple to find it so we accept to deal
* with this bug.
*/
@Override
public boolean addChild(INode node, Point2D point)
{
if (!node.getClass().isAssignableFrom(FieldNode.class))
{
return false;
}
INode parent = getParent();
List<INode> parentChildren = parent.getChildren();
int currentPosition = parentChildren.indexOf(this);
parent.addChild(node, currentPosition + 1);
return true;
}
@Override
public Point2D getConnectionPoint(IEdge edge)
{
Point2D location = getLocationOnGraph();
int x = (int)name.getBounds().getWidth();
if(0==x)
{
x = DEFAULT_WIDTH/2;
}
return new Point2D.Double(x + 20 + location.getX(), location.getY() + DEFAULT_HEIGHT/2);
}
/**
* Sets the name property value.
*
* @param newValue the field name
*/
public void setName(LineText newValue)
{
name.setText(newValue);
}
/**
* Gets the name property value.
*
* @return the field name
*/
public LineText getName()
{
return name;
}
/**
* Sets the value property value.
*
* @param newValue the field value
*/
public void setValue(LineText newValue)
{
if(0 == getConnectedEdges().size())
{
value.setText(newValue);
}
}
/**
* Gets the value property value.
*
* @return the field value
*/
public LineText getValue()
{
if(0<getConnectedEdges().size())
{
return null;
}
return value;
}
private SingleLineText name;
private SingleLineText value;
private static final int DEFAULT_WIDTH = 80;
private static final int DEFAULT_HEIGHT = 20;
private static final int XGAP = 5;
private static final int YGAP = 5;
private static final Separator EQUAL_SEPARATOR = new Separator()
{
@Override
public void draw(Graphics2D graphics, Point2D startPoint, Point2D endPoint)
{
graphics.drawLine((int)startPoint.getX()-3, (int)startPoint.getY() + DEFAULT_HEIGHT/2 -1, (int)startPoint.getX()+3, (int)startPoint.getY() + DEFAULT_HEIGHT/2 -1);
graphics.drawLine((int)startPoint.getX()-3, (int)startPoint.getY() + DEFAULT_HEIGHT/2 +2, (int)startPoint.getX()+3, (int)startPoint.getY() + DEFAULT_HEIGHT/2 +2);
}
};
}