001/* =========================================================== 002 * Orson Charts : a 3D chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C)opyright 2013-2016, by Object Refinery Limited. All rights reserved. 006 * 007 * http://www.object-refinery.com/orsoncharts/index.html 008 * 009 * This program is free software: you can redistribute it and/or modify 010 * it under the terms of the GNU General Public License as published by 011 * the Free Software Foundation, either version 3 of the License, or 012 * (at your option) any later version. 013 * 014 * This program is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public License 020 * along with this program. If not, see <http://www.gnu.org/licenses/>. 021 * 022 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 023 * Other names may be trademarks of their respective owners.] 024 * 025 * If you do not wish to be bound by the terms of the GPL, an alternative 026 * commercial license can be purchased. For details, please see visit the 027 * Orson Charts home page: 028 * 029 * http://www.object-refinery.com/orsoncharts/index.html 030 * 031 */ 032 033package com.orsoncharts.table; 034 035import java.awt.Color; 036import java.awt.Font; 037import java.awt.Graphics2D; 038import java.awt.Insets; 039import java.awt.geom.Dimension2D; 040import java.awt.geom.Rectangle2D; 041import java.io.Serializable; 042import java.util.ArrayList; 043import java.util.List; 044import java.util.Map; 045 046import com.orsoncharts.util.ArgChecks; 047import com.orsoncharts.util.TextAnchor; 048import com.orsoncharts.util.TextUtils; 049 050/** 051 * A table element consisting of some text that will be drawn on one line. 052 * <br><br> 053 * NOTE: This class is serializable, but the serialization format is subject 054 * to change in future releases and should not be relied upon for persisting 055 * instances of this class. 056 */ 057@SuppressWarnings("serial") 058public class TextElement extends AbstractTableElement 059 implements TableElement, Serializable { 060 061 /** 062 * The default font. 063 * 064 * @since 1.1 065 */ 066 public static final Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 067 12); 068 069 /** The text (never {@code null}). */ 070 private String text; 071 072 /** The font (never {@code null}). */ 073 private Font font; 074 075 /** The color for the text (never {@code null}). */ 076 private Color color; 077 078 /** The horizontal alignment (never {@code null}). */ 079 private HAlign alignment; 080 081 /** 082 * Creates a new element that will display the specified text using the 083 * default font ({@link #DEFAULT_FONT}). 084 * 085 * @param text the text ({@code null} not permitted). 086 */ 087 public TextElement(String text) { 088 this(text, DEFAULT_FONT); 089 } 090 091 /** 092 * Creates a new instance. 093 * 094 * @param text the text ({@code null} not permitted). 095 * @param font the font ({@code null} not permitted). 096 */ 097 public TextElement(String text, Font font) { 098 super(); 099 ArgChecks.nullNotPermitted(text, "text"); 100 ArgChecks.nullNotPermitted(font, "font"); 101 this.text = text; 102 this.font = font; 103 this.color = Color.BLACK; 104 this.alignment = HAlign.LEFT; 105 } 106 107 /** 108 * Returns the font. The default value is {@link #DEFAULT_FONT}. 109 * 110 * @return The font (never {@code null}). 111 */ 112 public Font getFont() { 113 return this.font; 114 } 115 116 /** 117 * Sets the font. 118 * 119 * @param font the font ({@code null} not permitted). 120 */ 121 public void setFont(Font font) { 122 ArgChecks.nullNotPermitted(font, "font"); 123 this.font = font; 124 } 125 126 public Color getColor() { 127 return this.color; 128 } 129 130 public void setColor(Color color) { 131 ArgChecks.nullNotPermitted(color, "color"); 132 this.color = color; 133 } 134 135 /** 136 * Returns the horizontal alignment that will be used when rendering the 137 * text. The default value is {@code LEFT}. 138 * 139 * @return The horizontal alignment (never {@code null}). 140 */ 141 public HAlign getHorizontalAlignment() { 142 return this.alignment; 143 } 144 145 /** 146 * Sets the horizontal alignment. 147 * 148 * @param align the alignment ({@code null} not permitted). 149 */ 150 public void setHorizontalAligment(HAlign align) { 151 ArgChecks.nullNotPermitted(align, "align"); 152 this.alignment = align; 153 } 154 155 /** 156 * Returns the preferred size of the element (including insets). 157 * 158 * @param g2 the graphics target. 159 * @param bounds the bounds. 160 * @param constraints the constraints (ignored for now). 161 * 162 * @return The preferred size. 163 */ 164 @Override 165 public Dimension2D preferredSize(Graphics2D g2, Rectangle2D bounds, 166 Map<String, Object> constraints) { 167 g2.setFont(this.font); 168 Rectangle2D textBounds = TextUtils.getTextBounds(this.text, 169 g2.getFontMetrics(this.font)); 170 Insets insets = getInsets(); 171 double w = Math.min(textBounds.getWidth() + insets.left + insets.right, 172 bounds.getWidth()); 173 double h = Math.min(textBounds.getHeight() + insets.top + insets.bottom, 174 bounds.getHeight()); 175 return new ElementDimension(w, h); 176 } 177 178 /** 179 * Performs a layout of this table element, returning a list of bounding 180 * rectangles for the element and its subelements. 181 * 182 * @param g2 the graphics target. 183 * @param bounds the bounds. 184 * @param constraints the constraints (if any). 185 * 186 * @return A list containing the bounding rectangle for the text (as the 187 * only item in the list). 188 */ 189 @Override 190 public List<Rectangle2D> layoutElements(Graphics2D g2, Rectangle2D bounds, 191 Map<String, Object> constraints) { 192 g2.setFont(this.font); 193 Rectangle2D textBounds = TextUtils.getTextBounds(this.text, 194 g2.getFontMetrics(this.font)); 195 Insets insets = getInsets(); 196 double width = textBounds.getWidth() + insets.left + insets.right; 197 double x = bounds.getX(); 198 switch (this.alignment) { 199 case LEFT: 200 x = bounds.getX(); 201 break; 202 case CENTER: 203 x = bounds.getCenterX() - width / 2.0 - insets.left; 204 break; 205 case RIGHT: 206 x = bounds.getMaxX() - width - insets.right; 207 break; 208 default: 209 throw new IllegalStateException("HAlign: " + this.alignment); 210 } 211 double y = bounds.getY(); 212 double w = Math.min(width, bounds.getWidth()); 213 double h = Math.min(textBounds.getHeight() + insets.top + insets.bottom, 214 bounds.getHeight()); 215 List<Rectangle2D> result = new ArrayList<Rectangle2D>(1); 216 result.add(new Rectangle2D.Double(x, y, w, h)); 217 return result; 218 } 219 220 /** 221 * Receives a visitor. 222 * 223 * @param visitor the visitor ({@code null} not permitted). 224 * 225 * @since 1.2 226 */ 227 @Override 228 public void receive(TableElementVisitor visitor) { 229 visitor.visit(this); 230 } 231 232 /** 233 * Draws the element within the specified bounds. 234 * 235 * @param g2 the graphics target. 236 * @param bounds the bounds. 237 */ 238 @Override 239 public void draw(Graphics2D g2, Rectangle2D bounds) { 240 draw(g2, bounds, null); 241 } 242 243 /** 244 * Draws the element within the specified bounds. If the 245 * {@code recordBounds} flag is set, this element and each of its 246 * children will have their {@code BOUNDS_2D} property updated with 247 * the current bounds. 248 * 249 * @param g2 the graphics target ({@code null} not permitted). 250 * @param bounds the bounds ({@code null} not permitted). 251 * @param onDrawHandler an object that will receive notification before 252 * and after the element is drawn ({@code null} permitted). 253 */ 254 @Override 255 public void draw(Graphics2D g2, Rectangle2D bounds, 256 TableElementOnDraw onDrawHandler) { 257 if (onDrawHandler != null) { 258 onDrawHandler.beforeDraw(this, g2, bounds); 259 } 260 List<Rectangle2D> layout = layoutElements(g2, bounds, null); 261 Rectangle2D textBounds = layout.get(0); 262 if (getBackground() != null) { 263 getBackground().fill(g2, textBounds); 264 } 265 g2.setPaint(this.color); 266 g2.setFont(this.font); 267 Insets insets = getInsets(); 268 TextUtils.drawAlignedString(this.text, g2, 269 (float) (textBounds.getX() + insets.left), 270 (float) (textBounds.getY() + insets.top), TextAnchor.TOP_LEFT); 271 if (onDrawHandler != null) { 272 onDrawHandler.afterDraw(this, g2, bounds); 273 } 274 } 275 276 /** 277 * Tests this element for equality with an arbitrary object. 278 * 279 * @param obj the object ({@code null} permitted). 280 * 281 * @return A boolean. 282 */ 283 @Override 284 public boolean equals(Object obj) { 285 if (obj == this) { 286 return true; 287 } 288 if (!(obj instanceof TextElement)) { 289 return false; 290 } 291 TextElement that = (TextElement) obj; 292 if (!this.text.equals(that.text)) { 293 return false; 294 } 295 if (!this.font.equals(that.font)) { 296 return false; 297 } 298 if (!this.color.equals(that.color)) { 299 return false; 300 } 301 if (this.alignment != that.alignment) { 302 return false; 303 } 304 return super.equals(obj); 305 } 306 307 @Override 308 public String toString() { 309 StringBuilder sb = new StringBuilder(); 310 sb.append("TextElement[text=").append(this.text).append("]"); 311 return sb.toString(); 312 } 313}