/*******************************************************************************
* Copyright (c) 2006-2012
* Software Technology Group, Dresden University of Technology
* DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Software Technology Group - TU Dresden, Germany;
* DevBoost GmbH - Berlin, Germany
* - initial API and implementation
******************************************************************************/
/*
* @(#)TextFigure.java 1.0.2 2007-05-02
*
* Copyright (c) 1996-2007 by the original authors of JHotDraw
* and all its contributors.
* All rights reserved.
*
* The copyright of this software is owned by the authors and
* contributors of the JHotDraw project ("the copyright holders").
* You may not use, copy or modify this software, except in
* accordance with the license agreement you entered into with
* the copyright holders. For details see accompanying license terms.
*/
package org.jhotdraw.draw;
import org.jhotdraw.util.*;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.util.*;
import java.io.*;
import static org.jhotdraw.draw.AttributeKeys.*;
import org.jhotdraw.geom.*;
import org.jhotdraw.xml.DOMInput;
import org.jhotdraw.xml.DOMOutput;
/**
* A text figure.
* <p>
* A DrawingEditor should provide the TextCreationTool to create a TextFigure.
*
* @see TextCreationTool
*
* @author Werner Randelshofer
* @version 2.0.2 2007-05-02 Made all instance variables protected instead of
* private.
* <br>2.0.1 2006-02-27 Draw UNDERLINE_LOW_ONE_PIXEL instead of UNDERLINE_ON.
* <br>2.0 2006-01-14 Changed to support double precison coordinates.
* <br>1.0 2003-12-01 Derived from JHotDraw 5.4b1.
*/
public class TextFigure extends AbstractAttributedDecoratedFigure
implements TextHolderFigure {
protected Point2D.Double origin = new Point2D.Double();
protected boolean editable = true;
// cache of the TextFigure's layout
transient protected TextLayout textLayout;
/** Creates a new instance. */
public TextFigure() {
this(ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels").
getString("TextFigure.defaultText"));
}
public TextFigure(String text) {
setText(text);
}
// DRAWING
protected void drawStroke(java.awt.Graphics2D g) {
}
protected void drawFill(java.awt.Graphics2D g) {
}
protected void drawText(java.awt.Graphics2D g) {
if (getText() != null || isEditable()) {
TextLayout layout = getTextLayout();
layout.draw(g, (float) origin.x, (float) (origin.y + layout.getAscent()));
}
}
// SHAPE AND BOUNDS
public void transform(AffineTransform tx) {
tx.transform(origin, origin);
}
public void setBounds(Point2D.Double anchor, Point2D.Double lead) {
origin = new Point2D.Double(anchor.x, anchor.y);
}
public boolean figureContains(Point2D.Double p) {
if (getBounds().contains(p)) {
return true;
}
return false;
}
protected TextLayout getTextLayout() {
if (textLayout == null) {
String text = getText();
if (text == null || text.length() == 0) {
text = " ";
}
FontRenderContext frc = getFontRenderContext();
HashMap<TextAttribute, Object> textAttributes = new HashMap<TextAttribute, Object>();
textAttributes.put(TextAttribute.FONT, getFont());
if (FONT_UNDERLINE.get(this)) {
textAttributes.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_LOW_ONE_PIXEL);
}
textLayout = new TextLayout(text, textAttributes, frc);
}
return textLayout;
}
public Rectangle2D.Double getBounds() {
TextLayout layout = getTextLayout();
Rectangle2D.Double r = new Rectangle2D.Double(origin.x, origin.y, layout.getAdvance(),
layout.getAscent() + layout.getDescent());
return r;
}
public Dimension2DDouble getPreferredSize() {
Rectangle2D.Double b = getBounds();
return new Dimension2DDouble(b.width, b.height);
}
public double getBaseline() {
TextLayout layout = getTextLayout();
return origin.y + layout.getAscent() - getBounds().y;
}
/**
* Gets the drawing area without taking the decorator into account.
*/
protected Rectangle2D.Double getFigureDrawingArea() {
if (getText() == null) {
return getBounds();
} else {
TextLayout layout = getTextLayout();
Rectangle2D.Double r = new Rectangle2D.Double(
origin.x, origin.y, layout.getAdvance(), layout.getAscent());
Rectangle2D lBounds = layout.getBounds();
if (!lBounds.isEmpty() && !Double.isNaN(lBounds.getX())) {
r.add(new Rectangle2D.Double(
lBounds.getX() + origin.x,
(lBounds.getY() + origin.y + layout.getAscent()),
lBounds.getWidth(),
lBounds.getHeight()));
}
// grow by two pixels to take antialiasing into account
Geom.grow(r, 2d, 2d);
return r;
}
}
public void restoreTransformTo(Object geometry) {
Point2D.Double p = (Point2D.Double) geometry;
origin.x = p.x;
origin.y = p.y;
}
public Object getTransformRestoreData() {
return origin.clone();
}
// ATTRIBUTES
/**
* Gets the text shown by the text figure.
*/
public String getText() {
return TEXT.get(this);
}
/**
* Sets the text shown by the text figure.
* This is a convenience method for calling willChange,
* AttribuTEXT.basicSet, changed.
*/
public void setText(String newText) {
TEXT.set(this, newText);
}
public int getTextColumns() {
//return (getText() == null) ? 4 : Math.max(getText().length(), 4);
return 4;
}
/**
* Gets the number of characters used to expand tabs.
*/
public int getTabSize() {
return 8;
}
public TextHolderFigure getLabelFor() {
return this;
}
public Insets2D.Double getInsets() {
return new Insets2D.Double();
}
public Font getFont() {
return AttributeKeys.getFont(this);
}
public Color getTextColor() {
return TEXT_COLOR.get(this);
}
public Color getFillColor() {
return FILL_COLOR.get(this);
}
public void setFontSize(float size) {
FONT_SIZE.set(this, new Double(size));
}
public float getFontSize() {
return FONT_SIZE.get(this).floatValue();
}
// EDITING
public boolean isEditable() {
return editable;
}
public void setEditable(boolean b) {
this.editable = b;
}
@Override
public Collection<Handle> createHandles(int detailLevel) {
LinkedList<Handle> handles = new LinkedList<Handle>();
switch (detailLevel) {
case -1:
handles.add(new BoundsOutlineHandle(this, false, true));
break;
case 0:
handles.add(new BoundsOutlineHandle(this));
handles.add(new MoveHandle(this, RelativeLocator.northWest()));
handles.add(new MoveHandle(this, RelativeLocator.northEast()));
handles.add(new MoveHandle(this, RelativeLocator.southWest()));
handles.add(new MoveHandle(this, RelativeLocator.southEast()));
handles.add(new FontSizeHandle(this));
break;
}
return handles;
}
/**
* Returns a specialized tool for the given coordinate.
* <p>Returns null, if no specialized tool is available.
*/
@Override
public Tool getTool(Point2D.Double p) {
if (isEditable() && contains(p)) {
TextEditingTool t = new TextEditingTool(this);
return t;
}
return null;
}
// CONNECTING
// COMPOSITE FIGURES
// CLONING
// EVENT HANDLING
@Override
public void invalidate() {
super.invalidate();
textLayout = null;
}
@Override
protected void validate() {
super.validate();
textLayout = null;
}
@Override
public void read(DOMInput in) throws IOException {
setBounds(
new Point2D.Double(in.getAttribute("x", 0d), in.getAttribute("y", 0d)),
new Point2D.Double(0, 0));
readAttributes(in);
readDecorator(in);
invalidate();
}
@Override
public void write(DOMOutput out) throws IOException {
Rectangle2D.Double b = getBounds();
out.addAttribute("x", b.x);
out.addAttribute("y", b.y);
writeAttributes(out);
writeDecorator(out);
}
@Override
public TextFigure clone() {
TextFigure that = (TextFigure) super.clone();
that.origin = (Point2D.Double) this.origin.clone();
that.textLayout = null;
return that;
}
public boolean isTextOverflow() {
return false;
}
}