package simple_uml.view.canvas;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.Vector;
import simple_uml.model.SumlClassModel;
/**
* Handles how a class is displayed in the SumlCanvas.
*
* @author Eric Liebowitz
* @version 22jun11
*/
public class SumlClassView extends SumlShape
{
/* CONSTANTS ==>*/
/**
* Horizontal pad on the right side of the bounding box to be put between the
* box and its contents.
*/
private static final int X_BOX_BUF = 5;
/**
* Vertical pad between name, atts, and methods
*/
private static final int ATT_BUF = 0;
/**
* Extra length to be added to lines separating name, atts, and methods
*/
private static final int LINE_BUF = 5;
/*<==*/
/* INSTANCE VARIABLES ==>*/
/**
* Class model which contains the data we need to draw
*/
private SumlClassModel cm;
/**
* Methods name to be drawn
*/
private Text name;
/**
* Attribute list to be drawn
*/
private TextList atts;
/**
* List of methods to be drawn
*/
private TextList methods;
/**
* Points used to draw lines between name, atts, and methods
*/
private Point2D line1_src, line1_dst, line2_src, line2_dst;
/**
* Tells us what we're related to, if anything
*/
private Relationship rel_parent;
/**
* Actual class model we're related to, if any
*/
private SumlClassModel parent;
/*<==*/
/**
* Create a view to draw which'll represent a class model.
*
* @param cm The class model this view is to represent
*/
public SumlClassView (SumlCanvas canvas, SumlClassModel cm)/*==>*/
{
super (canvas, Color.RED);
this.cm = cm;
this.name = new Text(cm.getName(), // TODO: FIX
(int)BASE_ORIGIN.getX(),
(int)BASE_ORIGIN.getY());
this.atts = new TextList(cm.getAtts());
this.methods = new TextList(cm.getMethods());
this.parent = cm.getParent();
/*
* By convention, it is guaranteed that a child's parent shape already
* exists prior to the child's creation
*/
if (this.parent != null)
{
this.rel_parent = new Relationship(this, canvas.getClassShape(parent));
}
/*
* Determine the origin of our matrix
*/
this.origin = cm.getOrigin();
this.tMatrix.setToTranslation(origin.getX(), origin.getY());
}/*<==*/
/**
* Gets the SumlClassModel behind this view.
*
* @return The SumlClassModel from whence this object gets its data
*/
public SumlClassModel getModel ()
{
return this.cm;
}
/**
* Draws the UML class represention of the SumlClassView this class was
* instantiated with. In particular, the following things are drawn:
*
*
* - The class' name
* - [LINE]
* - The class' attributes
* - [LINE]
* - The class' methods
* - The box surrounding all these things to hold them together
*
*
* @param g2d The Graphics2D object used to draw everything.
*/
protected void draw (Graphics2D g2d)/*==>*/
{
int dy = 0; // Can also be seen as "how tall the box should be"
g2d.setFont(SumlCanvas.MONO_FONT);
g2d.setColor(this.color);
/*
* Drawing the name is easy...no calcs necessary
*/
this.name.paint(g2d);
dy = (int)this.name.getHeight() + ATT_BUF;
/*
* We'll adjust the atts to be beneathe the name
*/
this.atts.setOrigin(0, dy);
this.atts.paint(g2d);
dy += (int)this.atts.getHeight() + ATT_BUF;
/*
* We'll adjust the methods to be beneathe the atts
*/
this.methods.setOrigin(0, dy);
this.methods.paint(g2d);
dy += (int)this.methods.getHeight();
/*
* Once all the atts are drawn, we have the dimmensions we need to draw
* the lines separating name from atts from methods. We can also draw the
* box that surrounds everything.
*/
int width = computeWidth() + LINE_BUF + X_BOX_BUF;
drawLines(g2d);
drawBox(g2d, width, dy);
/*
* If we have one, draw our relationship
*/
if (rel_parent != null)
{
rel_parent.paint(g2d);
}
/*
* Now we can create our bounding box at the correct position
*/
bounds = new Rectangle(origin, new Dimension(width, dy));
}/*<==*/
/**
* Draws the box to surround all the attributes of the class.
*
* @param g2d The Graphics2D object to be used to draw the box
* @param width How wide the box needs to be.
* @param height How tall the box needs to be.
*/
private void drawBox (Graphics2D g2d, int width, int height)/*==>*/
{
g2d.draw(new Rectangle(width, height));
}/*<==*/
/**
* Draws the lines between name/atts and atts/methods. The LINE_BUF and
* X_BOX_BUF cosntants help determine the length of the lines beyond the
* required length needed to span the longest line of text.
*
* @param g2d The Graphics2D object used to draw everything.
*/
private void drawLines (Graphics2D g2d)/*==>*/
{
FontMetrics fm = g2d.getFontMetrics();
/*
* Get the maximum width necessary for the class, and draw our lines to
* that length.
*/
int width = computeWidth() + LINE_BUF + X_BOX_BUF;
/*
* First line goes directly beneathe the method's name
*/
Point p = name.getOrigin();
/*
* We'll get the correct y-coord by adding the text's height to its
* origin
*/
line1_src = new Point((int)p.getX(), (int)(p.getY() + fm.getHeight()));
line1_dst = new Point(width, (int)line1_src.getY());
/*
* Second line goes beneathe atts
*/
p = atts.getOrigin();
/*
* We get the right y-coord by adding the atts' height to its y-coord
* position
*/
line2_src = new Point((int)p.getX(), (int)(p.getY() + atts.getHeight()));
line2_dst = new Point(width, (int)line2_src.getY());
g2d.draw(new Line2D.Double(line1_src, line1_dst));
g2d.draw(new Line2D.Double(line2_src, line2_dst));
}/*<==*/
/**
* Computes the minimum width necessary to encompass all attributes in the
* view. NOTE: If you haven't already draw the name, atts, and
* methods, the measurement here may be incorrect, as it used the current
* widths of those three things to figure out the widest it needs to be.
*/
private int computeWidth ()/*==>*/
{
int width = (int)this.name.getWidth();
int temp = (int)this.atts.getWidth();
width = (temp > width) ? temp : width;
temp = (int)this.methods.getWidth();
width = (temp > width) ? temp : width;
return width;
}/*<==*/
}