    001: import java.awt.Font;
    002: import java.awt.Graphics2D;
    003: import java.awt.font.FontRenderContext;
    004: import java.awt.geom.Line2D;
    005: import java.awt.geom.Rectangle2D;
    006: import java.io.Serializable;
    007: import java.util.StringTokenizer;
    008:
    009: /**
    010:    A string that can extend over multiple lines.
    011: */
    012: public class MultiLineString implements Cloneable, Serializable
    013: {
    014:    /**
    015:       Constructs an empty, centered, normal size multiline
    016:       string that is not underlined.
    017:    */
    018:    public MultiLineString()
    019:    {
    020:       text = "";
    021:       justification = CENTER;
    022:       size = NORMAL;
    023:       underlined = false;
    024:    }
    025:    /**
    026:       Sets the value of the text property.
    027:       @param newValue the text of the multiline string
    028:    */
    029:    public void setText(String newValue) { text = newValue; }
    030:    /**
    031:       Gets the value of the text property.
    032:       @return the text of the multiline string
    033:    */
    034:    public String getText() { return text; }
    035:    /**
    036:       Sets the value of the justification property.
    037:       @param newValue the justification, one of LEFT, CENTER,
    038:       RIGHT
    039:    */
    040:    public void setJustification(int newValue) { justification = newValue; }
    041:    /**
    042:       Gets the value of the justification property.
    043:       @return the justification, one of LEFT, CENTER,
    044:       RIGHT
    045:    */
    046:    public int getJustification() { return justification; }
    047:    /**
    048:       Gets the value of the underlined property.
    049:       @return true if the text is underlined
    050:    */
    051:    public boolean isUnderlined() { return underlined; }
    052:    /**
    053:       Sets the value of the underlined property.
    054:       @param newValue true to underline the text
    055:    */
    056:    public void setUnderlined(boolean newValue) { underlined = newValue; }
    057:    /**
    058:       Sets the value of the size property.
    059:       @param newValue the size, one of SMALL, NORMAL, LARGE
    060:    */
    061:    public void setSize(int newValue) { size = newValue; }
    062:    /**
    063:       Gets the value of the size property.
    064:       @return the size, one of SMALL, NORMAL, LARGE
    065:    */
    066:    public int getSize() { return size; }
    067:
    068:    public String toString()
    069:    {
    070:       return text.replace('\n', '|');
    071:    }
    072:
    073:    /**
    074:       Gets the bounding rectangle for this multiline string.
    075:       @param g2 the graphics context
    076:       @return the bounding rectangle (with top left corner (0,0))
    077:    */
    078:    public Rectangle2D getBounds(Graphics2D g2)
    079:    {
    080:       double width = 0;
    081:       double height = 0;
    082:       Font oldFont = g2.getFont();
    083:       Font font = oldFont;
    084:       if (size == LARGE)
    085:       {
    086:          float size = font.getSize() * 1.25F;
    087:          font = font.deriveFont(size);
    088:       }
    089:       else if (size == SMALL)
    090:       {
    091:          float size = font.getSize() / 1.25F;
    092:          font = font.deriveFont(size);
    093:       }
    094:       g2.setFont(font);
    095:       FontRenderContext frc = g2.getFontRenderContext();
    096:       StringTokenizer tokenizer = new StringTokenizer(text, "\n");
    097:       while (tokenizer.hasMoreTokens())
    098:       {
    099:          String t = tokenizer.nextToken();
    100:          Rectangle2D b = font.getStringBounds(t, frc);
    101:          width = Math.max(width, b.getWidth() + 2 * GAP);
    102:          height += b.getHeight();
    103:       }
    104:       g2.setFont(oldFont);
    105:       return new Rectangle2D.Double(0, 0, width, height);
    106:    }
    107:
    108:    /**
    109:       Draws this multiline string inside a given rectangle
    110:       @param g2 the graphics context
    111:       @param r the rectangle into which to place this multiline string
    112:    */
    113:    public void draw(Graphics2D g2, Rectangle2D r)
    114:    {
    115:       Font oldFont = g2.getFont();
    116:       Font font = oldFont;
    117:       if (size == LARGE)
    118:       {
    119:          float size = font.getSize() * 1.25F;
    120:          font = font.deriveFont(size);
    121:       }
    122:       else if (size == SMALL)
    123:       {
    124:          float size = font.getSize() / 1.25F;
    125:          font = font.deriveFont(size);
    126:       }
    127:       g2.setFont(font);
    128:
    129:       FontRenderContext frc = g2.getFontRenderContext();
    130:       Rectangle2D bounds = getBounds(g2);
    131:       StringTokenizer tokenizer = new StringTokenizer(text, "\n");
    132:       double xleft = r.getX();
    133:       double ytop = r.getY() + (r.getHeight() - bounds.getHeight()) / 2;
    134:       while (tokenizer.hasMoreTokens())
    135:       {
    136:          String t = tokenizer.nextToken();
    137:          Rectangle2D b = font.getStringBounds(t, frc);
    138:
    139:          double xstart;
    140:          double xslack = r.getWidth() - b.getWidth();
    141:          if (justification == CENTER)
    142:             xstart = xleft + xslack / 2;
    143:          else if (justification == RIGHT)
    144:             xstart = xleft + xslack - GAP;
    145:          else
    146:             xstart = xleft + GAP;
    147:          double ystart = ytop - b.getY();
    148:          g2.drawString(t, (float)(xstart), (float)(ystart));
    149:          if (underlined)
    150:             g2.draw(new Line2D.Double(xstart, ystart + 1, xstart + b.getWidth(), ystart + 1));
    151:          ytop += b.getHeight();
    152:       }
    153:       g2.setFont(oldFont);
    154:    }
    155:
    156:    public Object clone()
    157:    {
    158:       try
    159:       {
    160:          return super.clone();
    161:       }
    162:       catch (CloneNotSupportedException exception)
    163:       {
    164:          return null;
    165:       }
    166:    }
    167:
    168:    public static final int LEFT = 0;
    169:    public static final int CENTER = 1;
    170:    public static final int RIGHT = 2;
    171:    public static final int LARGE = 3;
    172:    public static final int NORMAL = 4;
    173:    public static final int SMALL = 5;
    174:
    175:    private static final int GAP = 3;
    176:
    177:    private String text;
    178:    private int justification;
    179:    private int size;
    180:    private boolean underlined;
    181: }