package research.figure;
import research.*;
import research.store.StorableOutput;
import research.store.StorableInput;
import research.connector.BoxConnector;
import research.connector.ChopBoxConnector;
import research.connector.Connector;
import javax.swing.*;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.event.InputEvent;
import java.util.Vector;
import java.util.Hashtable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.text.AttributedString;
import java.text.AttributedCharacterIterator;
import research.util.ColorMap;
/**
* author: zhangwei
* Date: 2003-6-8
* Time: 14:55:30
*/
public class TextAreaFigure extends AbstractFigure
implements FigureChangeListener, TextAreaHolder {
private static final int CENTER = 0;
private static final int NORTH = 1;
private static final int SOUTH = 2;
private static final int WEST = 3;
private static final int EAST = 4;
// cache of the TextFigure's size
transient private boolean fSizeIsDirty = true;
transient private int fWidth;
transient private int fHeight;
public final String TEXT = "text";
private boolean fIsReadOnly;
private Figure fObservedFigure = null;
private OffsetLocator fLocator = null;
private transient JInerTextArea textAgent = null;
private class JInerTextArea extends JTextArea {
public int getRowHeight() {
return super.getRowHeight();
}
public int getColumnWidth() {
return super.getColumnWidth();
}
}
protected JInerTextArea getTextAgent() {
if (textAgent == null) {
Integer colums = (Integer) this.getAttribute("columns");
//temp-test
if (colums == null) {
colums = new Integer(9);
System.out.println("warning info: the 'columns' attribute of " + this + " is null");
}
//
textAgent = new JInerTextArea();
textAgent.setColumns(colums.intValue());
textAgent.setEditable(false);
textAgent.setEnabled(false);
textAgent.setDisabledTextColor(textAgent.getForeground());
textAgent.setCaretColor(textAgent.getBackground());
textAgent.setBorder(BorderFactory.createEmptyBorder());
textAgent.setOpaque(false);
textAgent.setLineWrap(true);
textAgent.setWrapStyleWord(true);
}
return textAgent;
}
/*
* Serialization support.
*/
private static final long serialVersionUID = 4599820785949456124L;
private int textFigureSerializedDataVersion = 1;
public TextAreaFigure() {
setAttribute("font", new Font("Dialog", Font.PLAIN, 14));
setAttribute("anchor", new Point(0, 0));
setAttribute("size", new Dimension(0, 0));
setAttribute("fillColor", ColorMap.color("None"));
setAttribute("textColor", Color.black);
setAttribute("frameColor", ColorMap.color("None"));
setAttribute("insets", new Insets(1, 4, 0, 4));
setAttribute("columns", new Integer(9));
setAttribute(TEXT, "");
fSizeIsDirty = true;
initConnectors();
}
protected void initConnectors() {
connector = new Connector[5];
connector[NORTH] = new BoxConnector(this, BoxConnector.NORTH);
connector[SOUTH] = new BoxConnector(this, BoxConnector.SOUTH);
connector[WEST] = new BoxConnector(this, BoxConnector.WEST);
connector[EAST] = new BoxConnector(this, BoxConnector.EAST);
connector[CENTER] = new ChopBoxConnector(this);
}
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) {
Point anchor = (Point) this.getAttribute("anchor");
anchor.x += x;
anchor.y += y;
}
public void basicDisplayBox(Point newOrigin, Point newCorner) {
Rectangle rect = new Rectangle(newOrigin);
rect.add(newCorner);
Point anchor = (Point) this.getAttribute("anchor");
Dimension size = (Dimension) this.getAttribute("size");
anchor.x = rect.x;
anchor.y = rect.y;
size.width = rect.width;
size.height = rect.height;
}
public Rectangle getDisplayBox() {
Dimension extent = textExtent();
Insets insets = (Insets) this.getAttribute("insets");
Point anchor = (Point) this.getAttribute("anchor");
Dimension size = (Dimension) this.getAttribute("size");
//temp-test
if (size == null) {
size = new Dimension(0, 0);
System.out.println("warning info: the 'size' attribute of " + this + " is null");
}
//
return new Rectangle(anchor.x, anchor.y,
Math.max(extent.width, size.width) + insets.left + insets.right,
Math.max(extent.height, size.height) + insets.top + insets.bottom);
}
public Rectangle getDisplayBox(Rectangle rect) {
if (rect == null)
rect = new Rectangle();
Dimension extent = textExtent();
Insets insets = (Insets) this.getAttribute("insets");
Point anchor = (Point) this.getAttribute("anchor");
Dimension size = (Dimension) this.getAttribute("size");
rect.setBounds(anchor.x, anchor.y,
Math.max(extent.width, size.width) + insets.left + insets.right,
Math.max(extent.height, size.height) + insets.top + insets.bottom);
return rect;
}
public Rectangle textDisplayBox() {
Dimension extent = textExtent();
Insets insets = (Insets) this.getAttribute("insets");
Point anchor = (Point) this.getAttribute("anchor");
return new Rectangle(anchor.x + insets.left, anchor.y + insets.top, extent.width, extent.height);
}
/**
* 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 (Font) this.getAttribute("font");
}
/**
* Sets the font.
*/
public void setFont(Font newFont) {
if (newFont == null)
return;
willChange();
setAttribute("font", newFont);
markDirty();
changed();
}
/**
* Updates the location whenever the figure changes itself.
*/
public void changed() {
super.changed();
updateLocation();
}
/**
* Gets the text shown by the text figure.
*/
public String getText() {
return (String) getAttribute(TEXT);
}
/**
* Sets the text shown by the text figure.
*/
public void setText(String newText) {
if (newText == null)
return;
if (!getText().equals(newText)) {
willChange();
setAttribute(TEXT, newText);
markDirty();
changed();
}
}
/**
* Tests whether the figure accepts typing.
*/
public boolean acceptsTyping() {
return !fIsReadOnly;
}
public void drawBackground(Graphics g) {
Rectangle r = getDisplayBox();
g.fillRect(r.x, r.y, r.width, r.height);
}
public void drawFrame(Graphics g) {
Rectangle r = getDisplayBox();
g.drawRect(r.x, r.y, r.width, r.height);
}
public void drawContent(Graphics g) {
Color text = getTextColor();
if (!ColorMap.isTransparent(text)) {
Color _old = g.getColor();
g.setColor(text);
drawText(g);
g.setColor(_old);
}
}
public void drawText(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
if ((g2.getTransform().getScaleX() == 1) && (g2.getTransform().getScaleY() == 1)) {
//old
/**
Rectangle rect = g.getClipBounds();
Rectangle textBox = textDisplayBox();
getTextAgent().setFont((Font) getAttribute("font"));
getTextAgent().setText(getText());
getTextAgent().setBounds(textBox);
g.setClip(textBox);
getTextAgent().getUI().getRootView(getTextAgent()).paint(g, textBox);
g.setClip(rect);
getTextAgent().getRows();
**/
//test
Rectangle rect = g.getClipBounds();
Rectangle textBox = textDisplayBox();
getTextAgent().setBounds(textBox);
getTextAgent().setForeground(g.getColor());
getTextAgent().setDisabledTextColor(g.getColor());
g.setClip(textBox);
getTextAgent().getUI().getRootView(getTextAgent()).paint(g, textBox);
g.setClip(rect);
} else {
/**
Rectangle rect = g.getClipBounds();
Rectangle textBox = textDisplayBox();
getTextAgent().setBounds(textBox);
g.setClip(textBox);
getTextAgent().getUI().getRootView(getTextAgent()).paint(g, textBox);
g.setClip(rect);
**/
Rectangle textBox = textDisplayBox();
AffineTransform oldAT = g2.getTransform();
g2.setTransform(AffineTransform.getTranslateInstance(0, 0));
FontRenderContext frc = g2.getFontRenderContext();
Point2D pen = textBox.getLocation();
int rowsHeight = getTextAgent().getRowHeight();
int lineCount = getTextAgent().getLineCount();
String text = getText();
if (text.length() == 0)
text = " ";
getTextAgent().setText(text);
for (int i = 0; i < lineCount; i++) {
String currentString = null;
try {
//��õ�i�а������ַ�
currentString = text.substring(getTextAgent().getLineStartOffset(i), getTextAgent().getLineEndOffset(i));
} catch (Exception e) {
;
}
TextLayout layout = null;
if (currentString.length() > 0) {
if (i == 0) {
layout = new TextLayout(currentString, (Font) getAttribute("font"), frc);
pen.setLocation(pen.getX(), pen.getY() + layout.getAscent());
}
Hashtable map = new Hashtable();
map.put(TextAttribute.FONT, (Font) getAttribute("font"));
AttributedString as = new AttributedString(currentString, map);
AttributedCharacterIterator aci = as.getIterator();
LineBreakMeasurer measurer = new LineBreakMeasurer(aci, frc);
float wrappingWidth = textBox.width;
while (measurer.getPosition() < currentString.length()) {
layout = measurer.nextLayout(wrappingWidth);
Shape shape = layout.getOutline(oldAT);
double deltaScale = textBox.width * oldAT.getScaleX() / shape.getBounds().width;
if (1 - deltaScale > 0) {
AffineTransform adjust = (AffineTransform) oldAT.clone();
adjust.scale(deltaScale, 1);
shape = layout.getOutline(adjust);
}
AffineTransform at = AffineTransform.getTranslateInstance(pen.getX() * oldAT.getScaleX(), pen.getY() * oldAT.getScaleY());
shape = at.createTransformedShape(shape);
g2.fill(shape);
pen.setLocation(pen.getX(), pen.getY() + rowsHeight);
}
} else {
pen.setLocation(pen.getX(), pen.getY() + rowsHeight);
}
}
g2.setTransform(oldAT);
}
}
private Dimension textExtent() {
if (!fSizeIsDirty) {
return new Dimension(fWidth, fHeight);
}
/**
Font font = (Font) this.getAttribute("font");
FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
fWidth = metrics.stringWidth(getText());
fHeight = metrics.getHeight()*5;
fSizeIsDirty = false;
return new Dimension(fWidth, fHeight);
**/
getTextAgent().setFont((Font) getAttribute("font"));
getTextAgent().setText(getText());
Dimension dim = getTextAgent().getPreferredSize();
fWidth = dim.width;
fHeight = dim.height;
return dim;
}
private void markDirty() {
fSizeIsDirty = true;
}
/**
* Gets the number of columns to be overlaid when the figure is edited.
*/
public int getColumns() {
/**
int length = getText().length();
int columns = 20;
if (length != 0) {
columns = getText().length() + 3;
}
return columns;
**/
return getTextAgent().getColumns();
}
public void setColumns(int cols) {
int oldCols = getTextAgent().getColumns();
int oldRows = getTextAgent().getRows();
if (oldCols == cols)
return;
willChange();
setAttribute("columns", new Integer(cols));
if (cols > oldCols) {
getTextAgent().setColumns(cols);
getTextAgent().invalidate();
} else {
textAgent = new JInerTextArea();
textAgent.setColumns(cols);
textAgent.setRows(oldRows);
textAgent.setEditable(false);
textAgent.setEnabled(false);
textAgent.setDisabledTextColor(textAgent.getForeground());
textAgent.setCaretColor(textAgent.getBackground());
textAgent.setBorder(BorderFactory.createEmptyBorder());
textAgent.setOpaque(false);
textAgent.setLineWrap(true);
textAgent.setWrapStyleWord(true);
textAgent.setText(getText());
textAgent.invalidate();
}
markDirty();
changed();
}
public Dimension getPreferredSize() {
return getTextAgent().getPreferredSize();
}
public int getColumnsWidth() {
return getTextAgent().getColumnWidth();
}
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 NullHandle(this, RelativeLocator.southWest()));
handles.addElement(new AreaWithHandle(this));
//handles.addElement(new AreaFontSizeHandle(this, RelativeLocator.southWest()));
return handles;
}
public Connector connectorAt(int x, int y) {
Rectangle rect = getDisplayBox();
if (!rect.contains(x, y)) return null;
int index = findLocation(x, y);
if (index < 0) return null;
Connector[] array = getConnectors();
return array[index];
}
private int findLocation(int x, int y) {
Rectangle rect = getDisplayBox();
if (x < rect.x + rect.width / 3) {
if (y < rect.y + rect.height / 3) {
return -1;
} else if (y < rect.y + rect.height - rect.height / 3) {
return WEST;
} else {
return -1;
}
} else if (x < rect.x + rect.width - rect.width / 3) {
if (y < rect.y + rect.height / 3) {
return NORTH;
} else if (y < rect.y + rect.height - rect.height / 3) {
return CENTER;
} else {
return SOUTH;
}
} else {
if (y < rect.y + rect.height / 3) {
return -1;
} else if (y < rect.y + rect.height - rect.height / 3) {
return EAST;
} else {
return -1;
}
}
}
public void write(StorableOutput dw) {
super.write(dw);
dw.writeBoolean(fIsReadOnly);
dw.writeStorable(fObservedFigure);
dw.writeStorable(fLocator);
}
public void read(StorableInput dr) throws IOException {
super.read(dr);
markDirty();
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);
Point anchor = (Point) this.getAttribute("anchor");
p.x -= size().width / 2 + anchor.x;
p.y -= size().height / 2 + anchor.y;
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;
}
}
class AreaWithHandle extends LocatorHandle {
protected transient int columnWidth;
protected transient int columns;
protected transient int width;
public AreaWithHandle(TextAreaFigure owner) {
super(owner, RelativeLocator.east());
}
public void invokeStart(int x, int y, DrawingView view) {
TextAreaFigure owner = (TextAreaFigure) this.owner();
columnWidth = owner.getColumnsWidth();
columns = owner.getColumns();
width = owner.getPreferredSize().width;
owner.willChange();
owner.changed();
}
public void invokeStep(InputEvent inputEvent, int x, int y, int anchorX, int anchorY, DrawingView view) {
TextAreaFigure owner = (TextAreaFigure) this.owner();
int deltaX = x - anchorX;
int deltaColumns = deltaX / columnWidth;
int newColumns = columns + deltaColumns;
if (newColumns <= 0) newColumns = 1;
if (newColumns < 9) return;
owner.willChange();
Dimension before = owner.getPreferredSize();
int oldColumns = owner.getColumns();
owner.setColumns(newColumns);
Dimension after = owner.getPreferredSize();
if (before.width == after.width) {
owner.setColumns(oldColumns);
}
owner.setAttribute("size", after);
owner.changed();
}
public void invokeEnd(int x, int y, int anchorX, int anchorY, DrawingView view) {
columnWidth = 0;
columns = 0;
width = 0;
TextAreaFigure owner = (TextAreaFigure) this.owner();
owner.willChange();
owner.changed();
}
}