Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * ---------- 28: * Title.java 29: * ---------- 30: * (C) Copyright 2000-2005, by David Berry and Contributors. 31: * 32: * Original Author: David Berry; 33: * Contributor(s): David Gilbert (for Object Refinery Limited); 34: * Nicolas Brodu; 35: * 36: * $Id: Title.java,v 1.10.2.1 2005/10/25 20:58:34 mungady Exp $ 37: * 38: * Changes (from 21-Aug-2001) 39: * -------------------------- 40: * 21-Aug-2001 : Added standard header (DG); 41: * 18-Sep-2001 : Updated header (DG); 42: * 14-Nov-2001 : Package com.jrefinery.common.ui.* changed to 43: * com.jrefinery.ui.* (DG); 44: * 07-Feb-2002 : Changed blank space around title from Insets --> Spacer, to 45: * allow for relative or absolute spacing (DG); 46: * 25-Jun-2002 : Removed unnecessary imports (DG); 47: * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG); 48: * 14-Oct-2002 : Changed the event listener storage structure (DG); 49: * 11-Sep-2003 : Took care of listeners while cloning (NB); 50: * 22-Sep-2003 : Spacer cannot be null. Added nullpointer checks for this (TM); 51: * 08-Jan-2003 : Renamed AbstractTitle --> Title and moved to separate 52: * package (DG); 53: * 26-Oct-2004 : Refactored to implement Block interface, and removed redundant 54: * constants (DG); 55: * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0 56: * release (DG); 57: * 02-Feb-2005 : Changed Spacer --> RectangleInsets for padding (DG); 58: * 03-May-2005 : Fixed problem in equals() method (DG); 59: * 60: */ 61: 62: package org.jfree.chart.title; 63: 64: import java.awt.Graphics2D; 65: import java.awt.geom.Rectangle2D; 66: import java.io.IOException; 67: import java.io.ObjectInputStream; 68: import java.io.ObjectOutputStream; 69: import java.io.Serializable; 70: 71: import javax.swing.event.EventListenerList; 72: 73: import org.jfree.chart.block.AbstractBlock; 74: import org.jfree.chart.block.Block; 75: import org.jfree.chart.event.TitleChangeEvent; 76: import org.jfree.chart.event.TitleChangeListener; 77: import org.jfree.ui.HorizontalAlignment; 78: import org.jfree.ui.RectangleEdge; 79: import org.jfree.ui.RectangleInsets; 80: import org.jfree.ui.VerticalAlignment; 81: import org.jfree.util.ObjectUtilities; 82: 83: /** 84: * The base class for all chart titles. A chart can have multiple titles, 85: * appearing at the top, bottom, left or right of the chart. 86: * <P> 87: * Concrete implementations of this class will render text and images, and 88: * hence do the actual work of drawing titles. 89: * 90: * @author David Berry 91: */ 92: public abstract class Title extends AbstractBlock 93: implements Block, Cloneable, Serializable { 94: 95: /** For serialization. */ 96: private static final long serialVersionUID = -6675162505277817221L; 97: 98: /** The default title position. */ 99: 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: }