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: }