001/* =========================================================== 002 * Orson Charts : a 3D chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C)opyright 2013-2022, by David Gilbert. All rights reserved. 006 * 007 * https://github.com/jfree/orson-charts 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 org.jfree.chart3d.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 org.jfree.chart3d.graphics2d.TextAnchor; 047import org.jfree.chart3d.internal.Args; 048import org.jfree.chart3d.internal.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 Args.nullNotPermitted(text, "text"); 100 Args.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 Args.nullNotPermitted(font, "font"); 123 this.font = font; 124 } 125 126 /** 127 * Returns the foreground color for the text element. The default value 128 * is {@code Color#BLACK}. 129 * 130 * @return The foreground color (never {@code null}). 131 */ 132 public Color getColor() { 133 return this.color; 134 } 135 136 /** 137 * Sets the foreground color for the text element. 138 * 139 * @param color the new color ({@code null} not permitted). 140 */ 141 public void setColor(Color color) { 142 Args.nullNotPermitted(color, "color"); 143 this.color = color; 144 } 145 146 /** 147 * Returns the horizontal alignment that will be used when rendering the 148 * text. The default value is {@code LEFT}. 149 * 150 * @return The horizontal alignment (never {@code null}). 151 */ 152 public HAlign getHorizontalAlignment() { 153 return this.alignment; 154 } 155 156 /** 157 * Sets the horizontal alignment. 158 * 159 * @param align the alignment ({@code null} not permitted). 160 */ 161 public void setHorizontalAligment(HAlign align) { 162 Args.nullNotPermitted(align, "align"); 163 this.alignment = align; 164 } 165 166 /** 167 * Returns the preferred size of the element (including insets). 168 * 169 * @param g2 the graphics target. 170 * @param bounds the bounds. 171 * @param constraints the constraints (ignored for now). 172 * 173 * @return The preferred size. 174 */ 175 @Override 176 public Dimension2D preferredSize(Graphics2D g2, Rectangle2D bounds, 177 Map<String, Object> constraints) { 178 g2.setFont(this.font); 179 Rectangle2D textBounds = TextUtils.getTextBounds(this.text, 180 g2.getFontMetrics(this.font)); 181 Insets insets = getInsets(); 182 double w = Math.min(textBounds.getWidth() + insets.left + insets.right, 183 bounds.getWidth()); 184 double h = Math.min(textBounds.getHeight() + insets.top + insets.bottom, 185 bounds.getHeight()); 186 return new ElementDimension(w, h); 187 } 188 189 /** 190 * Performs a layout of this table element, returning a list of bounding 191 * rectangles for the element and its subelements. 192 * 193 * @param g2 the graphics target. 194 * @param bounds the bounds. 195 * @param constraints the constraints (if any). 196 * 197 * @return A list containing the bounding rectangle for the text (as the 198 * only item in the list). 199 */ 200 @Override 201 public List<Rectangle2D> layoutElements(Graphics2D g2, Rectangle2D bounds, 202 Map<String, Object> constraints) { 203 g2.setFont(this.font); 204 Rectangle2D textBounds = TextUtils.getTextBounds(this.text, 205 g2.getFontMetrics(this.font)); 206 Insets insets = getInsets(); 207 double width = textBounds.getWidth() + insets.left + insets.right; 208 double x = bounds.getX(); 209 switch (this.alignment) { 210 case LEFT: 211 x = bounds.getX(); 212 break; 213 case CENTER: 214 x = bounds.getCenterX() - width / 2.0 - insets.left; 215 break; 216 case RIGHT: 217 x = bounds.getMaxX() - width - insets.right; 218 break; 219 default: 220 throw new IllegalStateException("HAlign: " + this.alignment); 221 } 222 double y = bounds.getY(); 223 double w = Math.min(width, bounds.getWidth()); 224 double h = Math.min(textBounds.getHeight() + insets.top + insets.bottom, 225 bounds.getHeight()); 226 List<Rectangle2D> result = new ArrayList<>(1); 227 result.add(new Rectangle2D.Double(x, y, w, h)); 228 return result; 229 } 230 231 /** 232 * Receives a visitor. 233 * 234 * @param visitor the visitor ({@code null} not permitted). 235 * 236 * @since 1.2 237 */ 238 @Override 239 public void receive(TableElementVisitor visitor) { 240 visitor.visit(this); 241 } 242 243 /** 244 * Draws the element within the specified bounds. 245 * 246 * @param g2 the graphics target. 247 * @param bounds the bounds. 248 */ 249 @Override 250 public void draw(Graphics2D g2, Rectangle2D bounds) { 251 draw(g2, bounds, null); 252 } 253 254 /** 255 * Draws the element within the specified bounds. If the 256 * {@code recordBounds} flag is set, this element and each of its 257 * children will have their {@code BOUNDS_2D} property updated with 258 * the current bounds. 259 * 260 * @param g2 the graphics target ({@code null} not permitted). 261 * @param bounds the bounds ({@code null} not permitted). 262 * @param onDrawHandler an object that will receive notification before 263 * and after the element is drawn ({@code null} permitted). 264 */ 265 @Override 266 public void draw(Graphics2D g2, Rectangle2D bounds, 267 TableElementOnDraw onDrawHandler) { 268 if (onDrawHandler != null) { 269 onDrawHandler.beforeDraw(this, g2, bounds); 270 } 271 List<Rectangle2D> layout = layoutElements(g2, bounds, null); 272 Rectangle2D textBounds = layout.get(0); 273 if (getBackground() != null) { 274 getBackground().fill(g2, textBounds); 275 } 276 g2.setPaint(this.color); 277 g2.setFont(this.font); 278 Insets insets = getInsets(); 279 TextUtils.drawAlignedString(this.text, g2, 280 (float) (textBounds.getX() + insets.left), 281 (float) (textBounds.getY() + insets.top), TextAnchor.TOP_LEFT); 282 if (onDrawHandler != null) { 283 onDrawHandler.afterDraw(this, g2, bounds); 284 } 285 } 286 287 /** 288 * Tests this element for equality with an arbitrary object. 289 * 290 * @param obj the object ({@code null} permitted). 291 * 292 * @return A boolean. 293 */ 294 @Override 295 public boolean equals(Object obj) { 296 if (obj == this) { 297 return true; 298 } 299 if (!(obj instanceof TextElement)) { 300 return false; 301 } 302 TextElement that = (TextElement) obj; 303 if (!this.text.equals(that.text)) { 304 return false; 305 } 306 if (!this.font.equals(that.font)) { 307 return false; 308 } 309 if (!this.color.equals(that.color)) { 310 return false; 311 } 312 if (this.alignment != that.alignment) { 313 return false; 314 } 315 return super.equals(obj); 316 } 317 318 @Override 319 public String toString() { 320 return "TextElement[text=" + this.text + "]"; 321 } 322}