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: * * * * @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; }/*<==*/ }