/* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2016, by Object Refinery Limited. All rights reserved.
*
* http://www.object-refinery.com/orsoncharts/index.html
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package com.orsoncharts.table;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.orsoncharts.util.ArgChecks;
import com.orsoncharts.util.TextAnchor;
import com.orsoncharts.util.TextUtils;
/**
* A table element consisting of some text that will be drawn on one line.
* <br><br>
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
@SuppressWarnings("serial")
public class TextElement extends AbstractTableElement
implements TableElement, Serializable {
/**
* The default font.
*
* @since 1.1
*/
public static final Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN,
12);
/** The text (never {@code null}). */
private String text;
/** The font (never {@code null}). */
private Font font;
/** The color for the text (never {@code null}). */
private Color color;
/** The horizontal alignment (never {@code null}). */
private HAlign alignment;
/**
* Creates a new element that will display the specified text using the
* default font ({@link #DEFAULT_FONT}).
*
* @param text the text ({@code null} not permitted).
*/
public TextElement(String text) {
this(text, DEFAULT_FONT);
}
/**
* Creates a new instance.
*
* @param text the text ({@code null} not permitted).
* @param font the font ({@code null} not permitted).
*/
public TextElement(String text, Font font) {
super();
ArgChecks.nullNotPermitted(text, "text");
ArgChecks.nullNotPermitted(font, "font");
this.text = text;
this.font = font;
this.color = Color.BLACK;
this.alignment = HAlign.LEFT;
}
/**
* Returns the font. The default value is {@link #DEFAULT_FONT}.
*
* @return The font (never {@code null}).
*/
public Font getFont() {
return this.font;
}
/**
* Sets the font.
*
* @param font the font ({@code null} not permitted).
*/
public void setFont(Font font) {
ArgChecks.nullNotPermitted(font, "font");
this.font = font;
}
public Color getColor() {
return this.color;
}
public void setColor(Color color) {
ArgChecks.nullNotPermitted(color, "color");
this.color = color;
}
/**
* Returns the horizontal alignment that will be used when rendering the
* text. The default value is {@code LEFT}.
*
* @return The horizontal alignment (never {@code null}).
*/
public HAlign getHorizontalAlignment() {
return this.alignment;
}
/**
* Sets the horizontal alignment.
*
* @param align the alignment ({@code null} not permitted).
*/
public void setHorizontalAligment(HAlign align) {
ArgChecks.nullNotPermitted(align, "align");
this.alignment = align;
}
/**
* Returns the preferred size of the element (including insets).
*
* @param g2 the graphics target.
* @param bounds the bounds.
* @param constraints the constraints (ignored for now).
*
* @return The preferred size.
*/
@Override
public Dimension2D preferredSize(Graphics2D g2, Rectangle2D bounds,
Map<String, Object> constraints) {
g2.setFont(this.font);
Rectangle2D textBounds = TextUtils.getTextBounds(this.text,
g2.getFontMetrics(this.font));
Insets insets = getInsets();
double w = Math.min(textBounds.getWidth() + insets.left + insets.right,
bounds.getWidth());
double h = Math.min(textBounds.getHeight() + insets.top + insets.bottom,
bounds.getHeight());
return new ElementDimension(w, h);
}
/**
* Performs a layout of this table element, returning a list of bounding
* rectangles for the element and its subelements.
*
* @param g2 the graphics target.
* @param bounds the bounds.
* @param constraints the constraints (if any).
*
* @return A list containing the bounding rectangle for the text (as the
* only item in the list).
*/
@Override
public List<Rectangle2D> layoutElements(Graphics2D g2, Rectangle2D bounds,
Map<String, Object> constraints) {
g2.setFont(this.font);
Rectangle2D textBounds = TextUtils.getTextBounds(this.text,
g2.getFontMetrics(this.font));
Insets insets = getInsets();
double width = textBounds.getWidth() + insets.left + insets.right;
double x = bounds.getX();
switch (this.alignment) {
case LEFT:
x = bounds.getX();
break;
case CENTER:
x = bounds.getCenterX() - width / 2.0 - insets.left;
break;
case RIGHT:
x = bounds.getMaxX() - width - insets.right;
break;
default:
throw new IllegalStateException("HAlign: " + this.alignment);
}
double y = bounds.getY();
double w = Math.min(width, bounds.getWidth());
double h = Math.min(textBounds.getHeight() + insets.top + insets.bottom,
bounds.getHeight());
List<Rectangle2D> result = new ArrayList<Rectangle2D>(1);
result.add(new Rectangle2D.Double(x, y, w, h));
return result;
}
/**
* Receives a visitor.
*
* @param visitor the visitor ({@code null} not permitted).
*
* @since 1.2
*/
@Override
public void receive(TableElementVisitor visitor) {
visitor.visit(this);
}
/**
* Draws the element within the specified bounds.
*
* @param g2 the graphics target.
* @param bounds the bounds.
*/
@Override
public void draw(Graphics2D g2, Rectangle2D bounds) {
draw(g2, bounds, null);
}
/**
* Draws the element within the specified bounds. If the
* {@code recordBounds} flag is set, this element and each of its
* children will have their {@code BOUNDS_2D} property updated with
* the current bounds.
*
* @param g2 the graphics target ({@code null} not permitted).
* @param bounds the bounds ({@code null} not permitted).
* @param onDrawHandler an object that will receive notification before
* and after the element is drawn ({@code null} permitted).
*/
@Override
public void draw(Graphics2D g2, Rectangle2D bounds,
TableElementOnDraw onDrawHandler) {
if (onDrawHandler != null) {
onDrawHandler.beforeDraw(this, g2, bounds);
}
List<Rectangle2D> layout = layoutElements(g2, bounds, null);
Rectangle2D textBounds = layout.get(0);
if (getBackground() != null) {
getBackground().fill(g2, textBounds);
}
g2.setPaint(this.color);
g2.setFont(this.font);
Insets insets = getInsets();
TextUtils.drawAlignedString(this.text, g2,
(float) (textBounds.getX() + insets.left),
(float) (textBounds.getY() + insets.top), TextAnchor.TOP_LEFT);
if (onDrawHandler != null) {
onDrawHandler.afterDraw(this, g2, bounds);
}
}
/**
* Tests this element for equality with an arbitrary object.
*
* @param obj the object ({@code null} permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof TextElement)) {
return false;
}
TextElement that = (TextElement) obj;
if (!this.text.equals(that.text)) {
return false;
}
if (!this.font.equals(that.font)) {
return false;
}
if (!this.color.equals(that.color)) {
return false;
}
if (this.alignment != that.alignment) {
return false;
}
return super.equals(obj);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("TextElement[text=").append(this.text).append("]");
return sb.toString();
}
}