001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2005, by Object Refinery Limited 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 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * ---------- 028 * Title.java 029 * ---------- 030 * (C) Copyright 2000-2005, by David Berry and Contributors. 031 * 032 * Original Author: David Berry; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Nicolas Brodu; 035 * 036 * $Id: Title.java,v 1.10.2.1 2005/10/25 20:58:34 mungady Exp $ 037 * 038 * Changes (from 21-Aug-2001) 039 * -------------------------- 040 * 21-Aug-2001 : Added standard header (DG); 041 * 18-Sep-2001 : Updated header (DG); 042 * 14-Nov-2001 : Package com.jrefinery.common.ui.* changed to 043 * com.jrefinery.ui.* (DG); 044 * 07-Feb-2002 : Changed blank space around title from Insets --> Spacer, to 045 * allow for relative or absolute spacing (DG); 046 * 25-Jun-2002 : Removed unnecessary imports (DG); 047 * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG); 048 * 14-Oct-2002 : Changed the event listener storage structure (DG); 049 * 11-Sep-2003 : Took care of listeners while cloning (NB); 050 * 22-Sep-2003 : Spacer cannot be null. Added nullpointer checks for this (TM); 051 * 08-Jan-2003 : Renamed AbstractTitle --> Title and moved to separate 052 * package (DG); 053 * 26-Oct-2004 : Refactored to implement Block interface, and removed redundant 054 * constants (DG); 055 * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0 056 * release (DG); 057 * 02-Feb-2005 : Changed Spacer --> RectangleInsets for padding (DG); 058 * 03-May-2005 : Fixed problem in equals() method (DG); 059 * 060 */ 061 062 package org.jfree.chart.title; 063 064 import java.awt.Graphics2D; 065 import java.awt.geom.Rectangle2D; 066 import java.io.IOException; 067 import java.io.ObjectInputStream; 068 import java.io.ObjectOutputStream; 069 import java.io.Serializable; 070 071 import javax.swing.event.EventListenerList; 072 073 import org.jfree.chart.block.AbstractBlock; 074 import org.jfree.chart.block.Block; 075 import org.jfree.chart.event.TitleChangeEvent; 076 import org.jfree.chart.event.TitleChangeListener; 077 import org.jfree.ui.HorizontalAlignment; 078 import org.jfree.ui.RectangleEdge; 079 import org.jfree.ui.RectangleInsets; 080 import org.jfree.ui.VerticalAlignment; 081 import org.jfree.util.ObjectUtilities; 082 083 /** 084 * The base class for all chart titles. A chart can have multiple titles, 085 * appearing at the top, bottom, left or right of the chart. 086 * <P> 087 * Concrete implementations of this class will render text and images, and 088 * hence do the actual work of drawing titles. 089 * 090 * @author David Berry 091 */ 092 public abstract class Title extends AbstractBlock 093 implements Block, Cloneable, Serializable { 094 095 /** For serialization. */ 096 private static final long serialVersionUID = -6675162505277817221L; 097 098 /** The default title position. */ 099 public static final RectangleEdge DEFAULT_POSITION = RectangleEdge.TOP; 100 101 /** The default horizontal alignment. */ 102 public static final HorizontalAlignment 103 DEFAULT_HORIZONTAL_ALIGNMENT = HorizontalAlignment.CENTER; 104 105 /** The default vertical alignment. */ 106 public static final VerticalAlignment 107 DEFAULT_VERTICAL_ALIGNMENT = VerticalAlignment.CENTER; 108 109 /** Default title padding. */ 110 public static final RectangleInsets DEFAULT_PADDING = new RectangleInsets( 111 1, 1, 1, 1 112 ); 113 114 /** The title position. */ 115 private RectangleEdge position; 116 117 /** The horizontal alignment of the title content. */ 118 private HorizontalAlignment horizontalAlignment; 119 120 /** The vertical alignment of the title content. */ 121 private VerticalAlignment verticalAlignment; 122 123 /** Storage for registered change listeners. */ 124 private transient EventListenerList listenerList; 125 126 /** 127 * A flag that can be used to temporarily disable the listener mechanism. 128 */ 129 private boolean notify; 130 131 /** 132 * Creates a new title, using default attributes where necessary. 133 */ 134 protected Title() { 135 this( 136 Title.DEFAULT_POSITION, 137 Title.DEFAULT_HORIZONTAL_ALIGNMENT, 138 Title.DEFAULT_VERTICAL_ALIGNMENT, 139 Title.DEFAULT_PADDING 140 ); 141 } 142 143 /** 144 * Creates a new title, using default attributes where necessary. 145 * 146 * @param position the position of the title (<code>null</code> not 147 * permitted). 148 * @param horizontalAlignment the horizontal alignment of the title 149 * (<code>null</code> not permitted). 150 * @param verticalAlignment the vertical alignment of the title 151 * (<code>null</code> not permitted). 152 */ 153 protected Title(RectangleEdge position, 154 HorizontalAlignment horizontalAlignment, 155 VerticalAlignment verticalAlignment) { 156 157 this( 158 position, horizontalAlignment, verticalAlignment, 159 Title.DEFAULT_PADDING 160 ); 161 162 } 163 164 /** 165 * Creates a new title. 166 * 167 * @param position the position of the title (<code>null</code> not 168 * permitted). 169 * @param horizontalAlignment the horizontal alignment of the title (LEFT, 170 * CENTER or RIGHT, <code>null</code> not 171 * permitted). 172 * @param verticalAlignment the vertical alignment of the title (TOP, 173 * MIDDLE or BOTTOM, <code>null</code> not 174 * permitted). 175 * @param padding the amount of space to leave around the outside of the 176 * title (<code>null</code> not permitted). 177 */ 178 protected Title(RectangleEdge position, 179 HorizontalAlignment horizontalAlignment, 180 VerticalAlignment verticalAlignment, 181 RectangleInsets padding) { 182 183 // check arguments... 184 if (position == null) { 185 throw new IllegalArgumentException("Null 'position' argument."); 186 } 187 if (horizontalAlignment == null) { 188 throw new IllegalArgumentException( 189 "Null 'horizontalAlignment' argument." 190 ); 191 } 192 193 if (verticalAlignment == null) { 194 throw new IllegalArgumentException( 195 "Null 'verticalAlignment' argument." 196 ); 197 } 198 if (padding == null) { 199 throw new IllegalArgumentException("Null 'spacer' argument."); 200 } 201 202 this.position = position; 203 this.horizontalAlignment = horizontalAlignment; 204 this.verticalAlignment = verticalAlignment; 205 setPadding(padding); 206 this.listenerList = new EventListenerList(); 207 this.notify = true; 208 209 } 210 211 /** 212 * Returns the position of the title. 213 * 214 * @return The title position (never <code>null</code>). 215 */ 216 public RectangleEdge getPosition() { 217 return this.position; 218 } 219 220 /** 221 * Sets the position for the title and sends a {@link TitleChangeEvent} to 222 * all registered listeners. 223 * 224 * @param position the position (<code>null</code> not permitted). 225 */ 226 public void setPosition(RectangleEdge position) { 227 if (position == null) { 228 throw new IllegalArgumentException("Null 'position' argument."); 229 } 230 if (this.position != position) { 231 this.position = position; 232 notifyListeners(new TitleChangeEvent(this)); 233 } 234 } 235 236 /** 237 * Returns the horizontal alignment of the title. 238 * 239 * @return The horizontal alignment (never <code>null</code>). 240 */ 241 public HorizontalAlignment getHorizontalAlignment() { 242 return this.horizontalAlignment; 243 } 244 245 /** 246 * Sets the horizontal alignment for the title and sends a 247 * {@link TitleChangeEvent} to all registered listeners. 248 * 249 * @param alignment the horizontal alignment (<code>null</code> not 250 * permitted). 251 */ 252 public void setHorizontalAlignment(HorizontalAlignment alignment) { 253 if (alignment == null) { 254 throw new IllegalArgumentException("Null 'alignment' argument."); 255 } 256 if (this.horizontalAlignment != alignment) { 257 this.horizontalAlignment = alignment; 258 notifyListeners(new TitleChangeEvent(this)); 259 } 260 } 261 262 /** 263 * Returns the vertical alignment of the title. 264 * 265 * @return The vertical alignment (never <code>null</code>). 266 */ 267 public VerticalAlignment getVerticalAlignment() { 268 return this.verticalAlignment; 269 } 270 271 /** 272 * Sets the vertical alignment for the title, and notifies any registered 273 * listeners of the change. 274 * 275 * @param alignment the new vertical alignment (TOP, MIDDLE or BOTTOM, 276 * <code>null</code> not permitted). 277 */ 278 public void setVerticalAlignment(VerticalAlignment alignment) { 279 if (alignment == null) { 280 throw new IllegalArgumentException("Null 'alignment' argument."); 281 } 282 if (this.verticalAlignment != alignment) { 283 this.verticalAlignment = alignment; 284 notifyListeners(new TitleChangeEvent(this)); 285 } 286 } 287 288 /** 289 * Returns the flag that indicates whether or not the notification 290 * mechanism is enabled. 291 * 292 * @return The flag. 293 */ 294 public boolean getNotify() { 295 return this.notify; 296 } 297 298 /** 299 * Sets the flag that indicates whether or not the notification mechanism 300 * is enabled. There are certain situations (such as cloning) where you 301 * want to turn notification off temporarily. 302 * 303 * @param flag the new value of the flag. 304 */ 305 public void setNotify(boolean flag) { 306 this.notify = flag; 307 if (flag) { 308 notifyListeners(new TitleChangeEvent(this)); 309 } 310 } 311 312 /** 313 * Draws the title on a Java 2D graphics device (such as the screen or a 314 * printer). 315 * 316 * @param g2 the graphics device. 317 * @param area the area allocated for the title (subclasses should not 318 * draw outside this area). 319 */ 320 public abstract void draw(Graphics2D g2, Rectangle2D area); 321 322 /** 323 * Returns a clone of the title. 324 * <P> 325 * One situation when this is useful is when editing the title properties - 326 * you can edit a clone, and then it is easier to cancel the changes if 327 * necessary. 328 * 329 * @return A clone of the title. 330 * 331 * @throws CloneNotSupportedException not thrown by this class, but it may 332 * be thrown by subclasses. 333 */ 334 public Object clone() throws CloneNotSupportedException { 335 336 Title duplicate = (Title) super.clone(); 337 duplicate.listenerList = new EventListenerList(); 338 // RectangleInsets is immutable => same reference in clone OK 339 return duplicate; 340 } 341 342 /** 343 * Registers an object for notification of changes to the title. 344 * 345 * @param listener the object that is being registered. 346 */ 347 public void addChangeListener(TitleChangeListener listener) { 348 this.listenerList.add(TitleChangeListener.class, listener); 349 } 350 351 /** 352 * Unregisters an object for notification of changes to the chart title. 353 * 354 * @param listener the object that is being unregistered. 355 */ 356 public void removeChangeListener(TitleChangeListener listener) { 357 this.listenerList.remove(TitleChangeListener.class, listener); 358 } 359 360 /** 361 * Notifies all registered listeners that the chart title has changed in 362 * some way. 363 * 364 * @param event an object that contains information about the change to 365 * the title. 366 */ 367 protected void notifyListeners(TitleChangeEvent event) { 368 if (this.notify) { 369 Object[] listeners = this.listenerList.getListenerList(); 370 for (int i = listeners.length - 2; i >= 0; i -= 2) { 371 if (listeners[i] == TitleChangeListener.class) { 372 ((TitleChangeListener) listeners[i + 1]).titleChanged( 373 event 374 ); 375 } 376 } 377 } 378 } 379 380 /** 381 * Tests an object for equality with this title. 382 * 383 * @param obj the object (<code>null</code> not permitted). 384 * 385 * @return <code>true</code> or <code>false</code>. 386 */ 387 public boolean equals(Object obj) { 388 if (obj == this) { 389 return true; 390 } 391 if (!(obj instanceof Title)) { 392 return false; 393 } 394 if (!super.equals(obj)) { 395 return false; 396 } 397 Title that = (Title) obj; 398 if (this.position != that.position) { 399 return false; 400 } 401 if (this.horizontalAlignment != that.horizontalAlignment) { 402 return false; 403 } 404 if (this.verticalAlignment != that.verticalAlignment) { 405 return false; 406 } 407 if (this.notify != that.notify) { 408 return false; 409 } 410 return true; 411 } 412 413 /** 414 * Returns a hashcode for the title. 415 * 416 * @return The hashcode. 417 */ 418 public int hashCode() { 419 int result = 193; 420 result = 37 * result + ObjectUtilities.hashCode(this.position); 421 result = 37 * result 422 + ObjectUtilities.hashCode(this.horizontalAlignment); 423 result = 37 * result + ObjectUtilities.hashCode(this.verticalAlignment); 424 return result; 425 } 426 427 /** 428 * Provides serialization support. 429 * 430 * @param stream the output stream. 431 * 432 * @throws IOException if there is an I/O error. 433 */ 434 private void writeObject(ObjectOutputStream stream) throws IOException { 435 stream.defaultWriteObject(); 436 } 437 438 /** 439 * Provides serialization support. 440 * 441 * @param stream the input stream. 442 * 443 * @throws IOException if there is an I/O error. 444 * @throws ClassNotFoundException if there is a classpath problem. 445 */ 446 private void readObject(ObjectInputStream stream) 447 throws IOException, ClassNotFoundException { 448 stream.defaultReadObject(); 449 this.listenerList = new EventListenerList(); 450 } 451 452 }