001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * --------------- 028 * ImageTitle.java 029 * --------------- 030 * (C) Copyright 2000-present, by David Berry and Contributors; 031 * 032 * Original Author: David Berry; 033 * Contributor(s): David Gilbert; 034 * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); 035 * 036 */ 037 038package org.jfree.chart.title; 039 040import java.awt.Graphics2D; 041import java.awt.Image; 042import java.awt.geom.Rectangle2D; 043import java.util.Objects; 044 045import org.jfree.chart.block.RectangleConstraint; 046import org.jfree.chart.event.TitleChangeEvent; 047import org.jfree.chart.ui.HorizontalAlignment; 048import org.jfree.chart.ui.RectangleEdge; 049import org.jfree.chart.ui.RectangleInsets; 050import org.jfree.chart.ui.Size2D; 051import org.jfree.chart.ui.VerticalAlignment; 052 053/** 054 * A chart title that displays an image. This is useful, for example, if you 055 * have an image of your corporate logo and want to use as a footnote or part 056 * of a title in a chart you create. 057 * <P> 058 * ImageTitle needs an image passed to it in the constructor. For ImageTitle 059 * to work, you must have already loaded this image from its source (disk or 060 * URL). It is recommended you use something like 061 * Toolkit.getDefaultToolkit().getImage() to get the image. Then, use 062 * MediaTracker or some other message to make sure the image is fully loaded 063 * from disk. 064 * <P> 065 * SPECIAL NOTE: this class fails to serialize, so if you are 066 * relying on your charts to be serializable, please avoid using this class. 067 */ 068public class ImageTitle extends Title { 069 070 /** The title image. */ 071 private Image image; 072 073 /** 074 * Creates a new image title. 075 * 076 * @param image the image ({@code null} not permitted). 077 */ 078 public ImageTitle(Image image) { 079 this(image, image.getHeight(null), image.getWidth(null), 080 Title.DEFAULT_POSITION, Title.DEFAULT_HORIZONTAL_ALIGNMENT, 081 Title.DEFAULT_VERTICAL_ALIGNMENT, Title.DEFAULT_PADDING); 082 } 083 084 /** 085 * Creates a new image title. 086 * 087 * @param image the image ({@code null} not permitted). 088 * @param position the title position. 089 * @param horizontalAlignment the horizontal alignment. 090 * @param verticalAlignment the vertical alignment. 091 */ 092 public ImageTitle(Image image, RectangleEdge position, 093 HorizontalAlignment horizontalAlignment, 094 VerticalAlignment verticalAlignment) { 095 096 this(image, image.getHeight(null), image.getWidth(null), 097 position, horizontalAlignment, verticalAlignment, 098 Title.DEFAULT_PADDING); 099 } 100 101 /** 102 * Creates a new image title with the given image scaled to the given 103 * width and height in the given location. 104 * 105 * @param image the image ({@code null} not permitted). 106 * @param height the height used to draw the image. 107 * @param width the width used to draw the image. 108 * @param position the title position. 109 * @param horizontalAlignment the horizontal alignment. 110 * @param verticalAlignment the vertical alignment. 111 * @param padding the amount of space to leave around the outside of the 112 * title. 113 */ 114 public ImageTitle(Image image, int height, int width, 115 RectangleEdge position, 116 HorizontalAlignment horizontalAlignment, 117 VerticalAlignment verticalAlignment, 118 RectangleInsets padding) { 119 120 super(position, horizontalAlignment, verticalAlignment, padding); 121 if (image == null) { 122 throw new NullPointerException("Null 'image' argument."); 123 } 124 this.image = image; 125 setHeight(height); 126 setWidth(width); 127 } 128 129 /** 130 * Returns the image for the title. 131 * 132 * @return The image for the title (never {@code null}). 133 */ 134 public Image getImage() { 135 return this.image; 136 } 137 138 /** 139 * Sets the image for the title and notifies registered listeners that the 140 * title has been modified. 141 * 142 * @param image the new image ({@code null} not permitted). 143 */ 144 public void setImage(Image image) { 145 if (image == null) { 146 throw new NullPointerException("Null 'image' argument."); 147 } 148 this.image = image; 149 notifyListeners(new TitleChangeEvent(this)); 150 } 151 152 /** 153 * Arranges the contents of the block, within the given constraints, and 154 * returns the block size. 155 * 156 * @param g2 the graphics device. 157 * @param constraint the constraint ({@code null} not permitted). 158 * 159 * @return The block size (in Java2D units, never {@code null}). 160 */ 161 @Override 162 public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { 163 Size2D s = new Size2D(this.image.getWidth(null), 164 this.image.getHeight(null)); 165 return new Size2D(calculateTotalWidth(s.getWidth()), 166 calculateTotalHeight(s.getHeight())); 167 } 168 169 /** 170 * Draws the title on a Java 2D graphics device (such as the screen or a 171 * printer). 172 * 173 * @param g2 the graphics device. 174 * @param area the area allocated for the title. 175 */ 176 @Override 177 public void draw(Graphics2D g2, Rectangle2D area) { 178 RectangleEdge position = getPosition(); 179 if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) { 180 drawHorizontal(g2, area); 181 } 182 else if (position == RectangleEdge.LEFT 183 || position == RectangleEdge.RIGHT) { 184 drawVertical(g2, area); 185 } 186 else { 187 throw new RuntimeException("Invalid title position."); 188 } 189 } 190 191 /** 192 * Draws the title on a Java 2D graphics device (such as the screen or a 193 * printer). 194 * 195 * @param g2 the graphics device. 196 * @param chartArea the area within which the title (and plot) should be 197 * drawn. 198 * 199 * @return The size of the area used by the title. 200 */ 201 protected Size2D drawHorizontal(Graphics2D g2, Rectangle2D chartArea) { 202 double startY; 203 double topSpace; 204 double bottomSpace; 205 double leftSpace; 206 double rightSpace; 207 208 double w = getWidth(); 209 double h = getHeight(); 210 RectangleInsets padding = getPadding(); 211 topSpace = padding.calculateTopOutset(h); 212 bottomSpace = padding.calculateBottomOutset(h); 213 leftSpace = padding.calculateLeftOutset(w); 214 rightSpace = padding.calculateRightOutset(w); 215 216 if (getPosition() == RectangleEdge.TOP) { 217 startY = chartArea.getY() + topSpace; 218 } 219 else { 220 startY = chartArea.getY() + chartArea.getHeight() - bottomSpace - h; 221 } 222 223 // what is our alignment? 224 HorizontalAlignment horizontalAlignment = getHorizontalAlignment(); 225 double startX = 0.0; 226 if (horizontalAlignment == HorizontalAlignment.CENTER) { 227 startX = chartArea.getX() + leftSpace + chartArea.getWidth() / 2.0 228 - w / 2.0; 229 } 230 else if (horizontalAlignment == HorizontalAlignment.LEFT) { 231 startX = chartArea.getX() + leftSpace; 232 } 233 else if (horizontalAlignment == HorizontalAlignment.RIGHT) { 234 startX = chartArea.getX() + chartArea.getWidth() - rightSpace - w; 235 } 236 g2.drawImage(this.image, (int) startX, (int) startY, (int) w, (int) h, 237 null); 238 239 return new Size2D(chartArea.getWidth() + leftSpace + rightSpace, 240 h + topSpace + bottomSpace); 241 242 } 243 244 /** 245 * Draws the title on a Java 2D graphics device (such as the screen or a 246 * printer). 247 * 248 * @param g2 the graphics device. 249 * @param chartArea the area within which the title (and plot) should be 250 * drawn. 251 * 252 * @return The size of the area used by the title. 253 */ 254 protected Size2D drawVertical(Graphics2D g2, Rectangle2D chartArea) { 255 256 double startX; 257 double topSpace = 0.0; 258 double bottomSpace = 0.0; 259 double leftSpace = 0.0; 260 double rightSpace = 0.0; 261 262 double w = getWidth(); 263 double h = getHeight(); 264 265 RectangleInsets padding = getPadding(); 266 if (padding != null) { 267 topSpace = padding.calculateTopOutset(h); 268 bottomSpace = padding.calculateBottomOutset(h); 269 leftSpace = padding.calculateLeftOutset(w); 270 rightSpace = padding.calculateRightOutset(w); 271 } 272 273 if (getPosition() == RectangleEdge.LEFT) { 274 startX = chartArea.getX() + leftSpace; 275 } 276 else { 277 startX = chartArea.getMaxX() - rightSpace - w; 278 } 279 280 // what is our alignment? 281 VerticalAlignment alignment = getVerticalAlignment(); 282 double startY = 0.0; 283 if (alignment == VerticalAlignment.CENTER) { 284 startY = chartArea.getMinY() + topSpace 285 + chartArea.getHeight() / 2.0 - h / 2.0; 286 } 287 else if (alignment == VerticalAlignment.TOP) { 288 startY = chartArea.getMinY() + topSpace; 289 } 290 else if (alignment == VerticalAlignment.BOTTOM) { 291 startY = chartArea.getMaxY() - bottomSpace - h; 292 } 293 294 g2.drawImage(this.image, (int) startX, (int) startY, (int) w, (int) h, 295 null); 296 297 return new Size2D(chartArea.getWidth() + leftSpace + rightSpace, 298 h + topSpace + bottomSpace); 299 300 } 301 302 /** 303 * Draws the block within the specified area. 304 * 305 * @param g2 the graphics device. 306 * @param area the area. 307 * @param params ignored ({@code null} permitted). 308 * 309 * @return Always {@code null}. 310 */ 311 @Override 312 public Object draw(Graphics2D g2, Rectangle2D area, Object params) { 313 draw(g2, area); 314 return null; 315 } 316 317 /** 318 * Tests this {@code ImageTitle} for equality with an arbitrary 319 * object. Returns {@code true} if: 320 * <ul> 321 * <li>{@code obj} is an instance of {@code ImageTitle}; 322 * <li>{@code obj} references the same image as this 323 * {@code ImageTitle}; 324 * <li>{@code super.equals(obj)} returns {@code true}; 325 * </ul> 326 * 327 * @param obj the object ({@code null} permitted). 328 * 329 * @return A boolean. 330 */ 331 @Override 332 public boolean equals(Object obj) { 333 if (obj == this) { 334 return true; 335 } 336 if (!(obj instanceof ImageTitle)) { 337 return false; 338 } 339 ImageTitle that = (ImageTitle) obj; 340 if (!Objects.equals(this.image, that.image)) { 341 return false; 342 } 343 if (!that.canEqual(this)) { 344 return false; 345 } 346 return super.equals(obj); 347 } 348 349 /** 350 * Ensures symmetry between super/subclass implementations of equals. For 351 * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. 352 * 353 * @param other Object 354 * 355 * @return true ONLY if the parameter is THIS class type 356 */ 357 @Override 358 public boolean canEqual(Object other) { 359 // fix the "equals not symmetric" problem 360 return (other instanceof ImageTitle); 361 } 362 363 @Override 364 public int hashCode() { 365 int hash = super.hashCode(); // equals calls superclass, hashCode must also 366 hash = 17 * hash + Objects.hashCode(this.image); 367 return hash; 368 } 369}