TextFigure.java
/*
* @(#)TextFigure.java
*
* Project: JHotdraw - a GUI framework for technical drawings
* http://www.jhotdraw.org
* http://jhotdraw.sourceforge.net
* Copyright: © by the original author(s) and all contributors
* License: Lesser GNU Public License (LGPL)
* http://www.opensource.org/licenses/lgpl-license.html
*/
package CH.ifa.draw.figures;
import java.util.*;
import java.awt.*;
import java.io.*;
import CH.ifa.draw.framework.*;
import CH.ifa.draw.standard.*;
import CH.ifa.draw.util.*;
/**
* A text figure.
*
* @see TextTool
*
* @version <$CURRENT_VERSION$>
*/
public class TextFigure
extends AttributeFigure
implements FigureChangeListener, TextHolder {
private int fOriginX;
private int fOriginY;
// cache of the TextFigure's size
transient private boolean fSizeIsDirty = true;
transient private int fWidth;
transient private int fHeight;
private String fText;
private Font fFont;
private boolean fIsReadOnly;
private Figure fObservedFigure = null;
private OffsetLocator fLocator = null;
private static String fgCurrentFontName = "Helvetica";
private static int fgCurrentFontSize = 12;
private static int fgCurrentFontStyle = Font.PLAIN;
/*
* Serialization support.
*/
private static final long serialVersionUID = 4599820785949456124L;
private int textFigureSerializedDataVersion = 1;
public TextFigure() {
fOriginX = 0;
fOriginY = 0;
fFont = createCurrentFont();
setAttribute("FillColor", ColorMap.color("None"));
fText = new String("");
fSizeIsDirty = true;
}
public void moveBy(int x, int y) {
willChange();
basicMoveBy(x, y);
if (fLocator != null) {
fLocator.moveBy(x, y);
}
changed();
}
protected void basicMoveBy(int x, int y) {
fOriginX += x;
fOriginY += y;
}
public void basicDisplayBox(Point newOrigin, Point newCorner) {
fOriginX = newOrigin.x;
fOriginY = newOrigin.y;
}
public Rectangle displayBox() {
Dimension extent = textExtent();
return new Rectangle(fOriginX, fOriginY, extent.width, extent.height);
}
public Rectangle textDisplayBox() {
return displayBox();
}
/**
* Tests whether this figure is read only.
*/
public boolean readOnly() {
return fIsReadOnly;
}
/**
* Sets the read only status of the text figure.
*/
public void setReadOnly(boolean isReadOnly) {
fIsReadOnly = isReadOnly;
}
/**
* Gets the font.
*/
public Font getFont() {
return fFont;
}
/**
* Sets the font.
*/
public void setFont(Font newFont) {
willChange();
fFont = newFont;
markDirty();
changed();
}
/**
* Updates the location whenever the figure changes itself.
*/
public void changed() {
super.changed();
updateLocation();
}
/**
* A text figure understands the "FontSize", "FontStyle", and "FontName"
* attributes.
*/
public Object getAttribute(String name) {
Font font = getFont();
if (name.equals("FontSize")) {
return new Integer(font.getSize());
}
if (name.equals("FontStyle")) {
return new Integer(font.getStyle());
}
if (name.equals("FontName")) {
return font.getName();
}
return super.getAttribute(name);
}
/**
* A text figure understands the "FontSize", "FontStyle", and "FontName"
* attributes.
*/
public void setAttribute(String name, Object value) {
Font font = getFont();
if (name.equals("FontSize")) {
Integer s = (Integer)value;
setFont(new Font(font.getName(), font.getStyle(), s.intValue()) );
}
else if (name.equals("FontStyle")) {
Integer s = (Integer)value;
int style = font.getStyle();
if (s.intValue() == Font.PLAIN) {
style = font.PLAIN;
}
else {
style = style ^ s.intValue();
}
setFont(new Font(font.getName(), style, font.getSize()) );
}
else if (name.equals("FontName")) {
String n = (String)value;
setFont(new Font(n, font.getStyle(), font.getSize()) );
}
else {
super.setAttribute(name, value);
}
}
/**
* Gets the text shown by the text figure.
*/
public String getText() {
return fText;
}
/**
* Sets the text shown by the text figure.
*/
public void setText(String newText) {
if (!newText.equals(fText)) {
willChange();
fText = new String(newText);
markDirty();
changed();
}
}
/**
* Tests whether the figure accepts typing.
*/
public boolean acceptsTyping() {
return !fIsReadOnly;
}
public void drawBackground(Graphics g) {
Rectangle r = displayBox();
g.fillRect(r.x, r.y, r.width, r.height);
}
public void drawFrame(Graphics g) {
g.setFont(fFont);
g.setColor((Color) getAttribute("TextColor"));
FontMetrics metrics = g.getFontMetrics(fFont);
g.drawString(fText, fOriginX, fOriginY + metrics.getAscent());
}
private Dimension textExtent() {
if (!fSizeIsDirty) {
return new Dimension(fWidth, fHeight);
}
FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(fFont);
fWidth = metrics.stringWidth(fText);
fHeight = metrics.getHeight();
fSizeIsDirty = false;
return new Dimension(metrics.stringWidth(fText), metrics.getHeight());
}
private void markDirty() {
fSizeIsDirty = true;
}
/**
* Gets the number of columns to be overlaid when the figure is edited.
*/
public int overlayColumns() {
int length = getText().length();
int columns = 20;
if (length != 0) {
columns = getText().length()+ 3;
}
return columns;
}
public Vector handles() {
Vector handles = new Vector();
handles.addElement(new NullHandle(this, RelativeLocator.northWest()));
handles.addElement(new NullHandle(this, RelativeLocator.northEast()));
handles.addElement(new NullHandle(this, RelativeLocator.southEast()));
handles.addElement(new FontSizeHandle(this, RelativeLocator.southWest()));
return handles;
}
public void write(StorableOutput dw) {
super.write(dw);
dw.writeInt(fOriginX);
dw.writeInt(fOriginY);
dw.writeString(fText);
dw.writeString(fFont.getName());
dw.writeInt(fFont.getStyle());
dw.writeInt(fFont.getSize());
dw.writeBoolean(fIsReadOnly);
dw.writeStorable(fObservedFigure);
dw.writeStorable(fLocator);
}
public void read(StorableInput dr) throws IOException {
super.read(dr);
markDirty();
fOriginX = dr.readInt();
fOriginY = dr.readInt();
fText = dr.readString();
fFont = new Font(dr.readString(), dr.readInt(), dr.readInt());
fIsReadOnly = dr.readBoolean();
fObservedFigure = (Figure)dr.readStorable();
if (fObservedFigure != null) {
fObservedFigure.addFigureChangeListener(this);
}
fLocator = (OffsetLocator)dr.readStorable();
}
private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
s.defaultReadObject();
if (fObservedFigure != null) {
fObservedFigure.addFigureChangeListener(this);
}
markDirty();
}
public void connect(Figure figure) {
if (fObservedFigure != null) {
fObservedFigure.removeFigureChangeListener(this);
}
fObservedFigure = figure;
fLocator = new OffsetLocator(figure.connectedTextLocator(this));
fObservedFigure.addFigureChangeListener(this);
updateLocation();
}
public void figureChanged(FigureChangeEvent e) {
updateLocation();
}
public void figureRemoved(FigureChangeEvent e) {
if (listener() != null) {
listener().figureRequestRemove(new FigureChangeEvent(this));
}
}
public void figureRequestRemove(FigureChangeEvent e) {}
public void figureInvalidated(FigureChangeEvent e) {}
public void figureRequestUpdate(FigureChangeEvent e) {}
/**
* Updates the location relative to the connected figure.
* The TextFigure is centered around the located point.
*/
protected void updateLocation() {
if (fLocator != null) {
Point p = fLocator.locate(fObservedFigure);
p.x -= size().width/2 + fOriginX;
p.y -= size().height/2 + fOriginY;
if (p.x != 0 || p.y != 0) {
willChange();
basicMoveBy(p.x, p.y);
changed();
}
}
}
public void release() {
super.release();
disconnect(fObservedFigure);
fObservedFigure = null;
}
/**
* Disconnects a text holder from a connect figure.
*/
public void disconnect(Figure disconnectFigure) {
if (disconnectFigure != null) {
disconnectFigure.removeFigureChangeListener(this);
}
fLocator = null;
}
/**
* Creates the current font to be used for new text figures.
*/
static public Font createCurrentFont() {
return new Font(fgCurrentFontName, fgCurrentFontStyle, fgCurrentFontSize);
}
/**
* Sets the current font name
*/
static public void setCurrentFontName(String name) {
fgCurrentFontName = name;
}
/**
* Sets the current font size.
*/
static public void setCurrentFontSize(int size) {
fgCurrentFontSize = size;
}
/**
* Sets the current font style.
*/
static public void setCurrentFontStyle(int style) {
fgCurrentFontStyle = style;
}
}