Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, 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-2007, 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.2 2007/01/24 11:07:07 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: public abstract class Title extends AbstractBlock 91: implements Block, Cloneable, Serializable { 92: 93: /** For serialization. */ 94: private static final long serialVersionUID = -6675162505277817221L; 95: 96: /** The default title position. */ 97: public static final RectangleEdge DEFAULT_POSITION = RectangleEdge.TOP; 98: 99: /** The default horizontal alignment. */ 100: public static final HorizontalAlignment 101: DEFAULT_HORIZONTAL_ALIGNMENT = HorizontalAlignment.CENTER; 102: 103: /** The default vertical alignment. */ 104: public static final VerticalAlignment 105: DEFAULT_VERTICAL_ALIGNMENT = VerticalAlignment.CENTER; 106: 107: /** Default title padding. */ 108: public static final RectangleInsets DEFAULT_PADDING = new RectangleInsets( 109: 1, 1, 1, 1); 110: 111: /** The title position. */ 112: private RectangleEdge position; 113: 114: /** The horizontal alignment of the title content. */ 115: private HorizontalAlignment horizontalAlignment; 116: 117: /** The vertical alignment of the title content. */ 118: private VerticalAlignment verticalAlignment; 119: 120: /** Storage for registered change listeners. */ 121: private transient EventListenerList listenerList; 122: 123: /** 124: * A flag that can be used to temporarily disable the listener mechanism. 125: */ 126: private boolean notify; 127: 128: /** 129: * Creates a new title, using default attributes where necessary. 130: */ 131: protected Title() { 132: this(Title.DEFAULT_POSITION, 133: Title.DEFAULT_HORIZONTAL_ALIGNMENT, 134: Title.DEFAULT_VERTICAL_ALIGNMENT, Title.DEFAULT_PADDING); 135: } 136: 137: /** 138: * Creates a new title, using default attributes where necessary. 139: * 140: * @param position the position of the title (<code>null</code> not 141: * permitted). 142: * @param horizontalAlignment the horizontal alignment of the title 143: * (<code>null</code> not permitted). 144: * @param verticalAlignment the vertical alignment of the title 145: * (<code>null</code> not permitted). 146: */ 147: protected Title(RectangleEdge position, 148: HorizontalAlignment horizontalAlignment, 149: VerticalAlignment verticalAlignment) { 150: 151: this(position, horizontalAlignment, verticalAlignment, 152: Title.DEFAULT_PADDING); 153: 154: } 155: 156: /** 157: * Creates a new title. 158: * 159: * @param position the position of the title (<code>null</code> not 160: * permitted). 161: * @param horizontalAlignment the horizontal alignment of the title (LEFT, 162: * CENTER or RIGHT, <code>null</code> not 163: * permitted). 164: * @param verticalAlignment the vertical alignment of the title (TOP, 165: * MIDDLE or BOTTOM, <code>null</code> not 166: * permitted). 167: * @param padding the amount of space to leave around the outside of the 168: * title (<code>null</code> not permitted). 169: */ 170: protected Title(RectangleEdge position, 171: HorizontalAlignment horizontalAlignment, 172: VerticalAlignment verticalAlignment, 173: RectangleInsets padding) { 174: 175: // check arguments... 176: if (position == null) { 177: throw new IllegalArgumentException("Null 'position' argument."); 178: } 179: if (horizontalAlignment == null) { 180: throw new IllegalArgumentException( 181: "Null 'horizontalAlignment' argument."); 182: } 183: 184: if (verticalAlignment == null) { 185: throw new IllegalArgumentException( 186: "Null 'verticalAlignment' argument."); 187: } 188: if (padding == null) { 189: throw new IllegalArgumentException("Null 'spacer' argument."); 190: } 191: 192: this.position = position; 193: this.horizontalAlignment = horizontalAlignment; 194: this.verticalAlignment = verticalAlignment; 195: setPadding(padding); 196: this.listenerList = new EventListenerList(); 197: this.notify = true; 198: 199: } 200: 201: /** 202: * Returns the position of the title. 203: * 204: * @return The title position (never <code>null</code>). 205: */ 206: public RectangleEdge getPosition() { 207: return this.position; 208: } 209: 210: /** 211: * Sets the position for the title and sends a {@link TitleChangeEvent} to 212: * all registered listeners. 213: * 214: * @param position the position (<code>null</code> not permitted). 215: */ 216: public void setPosition(RectangleEdge position) { 217: if (position == null) { 218: throw new IllegalArgumentException("Null 'position' argument."); 219: } 220: if (this.position != position) { 221: this.position = position; 222: notifyListeners(new TitleChangeEvent(this)); 223: } 224: } 225: 226: /** 227: * Returns the horizontal alignment of the title. 228: * 229: * @return The horizontal alignment (never <code>null</code>). 230: */ 231: public HorizontalAlignment getHorizontalAlignment() { 232: return this.horizontalAlignment; 233: } 234: 235: /** 236: * Sets the horizontal alignment for the title and sends a 237: * {@link TitleChangeEvent} to all registered listeners. 238: * 239: * @param alignment the horizontal alignment (<code>null</code> not 240: * permitted). 241: */ 242: public void setHorizontalAlignment(HorizontalAlignment alignment) { 243: if (alignment == null) { 244: throw new IllegalArgumentException("Null 'alignment' argument."); 245: } 246: if (this.horizontalAlignment != alignment) { 247: this.horizontalAlignment = alignment; 248: notifyListeners(new TitleChangeEvent(this)); 249: } 250: } 251: 252: /** 253: * Returns the vertical alignment of the title. 254: * 255: * @return The vertical alignment (never <code>null</code>). 256: */ 257: public VerticalAlignment getVerticalAlignment() { 258: return this.verticalAlignment; 259: } 260: 261: /** 262: * Sets the vertical alignment for the title, and notifies any registered 263: * listeners of the change. 264: * 265: * @param alignment the new vertical alignment (TOP, MIDDLE or BOTTOM, 266: * <code>null</code> not permitted). 267: */ 268: public void setVerticalAlignment(VerticalAlignment alignment) { 269: if (alignment == null) { 270: throw new IllegalArgumentException("Null 'alignment' argument."); 271: } 272: if (this.verticalAlignment != alignment) { 273: this.verticalAlignment = alignment; 274: notifyListeners(new TitleChangeEvent(this)); 275: } 276: } 277: 278: /** 279: * Returns the flag that indicates whether or not the notification 280: * mechanism is enabled. 281: * 282: * @return The flag. 283: */ 284: public boolean getNotify() { 285: return this.notify; 286: } 287: 288: /** 289: * Sets the flag that indicates whether or not the notification mechanism 290: * is enabled. There are certain situations (such as cloning) where you 291: * want to turn notification off temporarily. 292: * 293: * @param flag the new value of the flag. 294: */ 295: public void setNotify(boolean flag) { 296: this.notify = flag; 297: if (flag) { 298: notifyListeners(new TitleChangeEvent(this)); 299: } 300: } 301: 302: /** 303: * Draws the title on a Java 2D graphics device (such as the screen or a 304: * printer). 305: * 306: * @param g2 the graphics device. 307: * @param area the area allocated for the title (subclasses should not 308: * draw outside this area). 309: */ 310: public abstract void draw(Graphics2D g2, Rectangle2D area); 311: 312: /** 313: * Returns a clone of the title. 314: * <P> 315: * One situation when this is useful is when editing the title properties - 316: * you can edit a clone, and then it is easier to cancel the changes if 317: * necessary. 318: * 319: * @return A clone of the title. 320: * 321: * @throws CloneNotSupportedException not thrown by this class, but it may 322: * be thrown by subclasses. 323: */ 324: public Object clone() throws CloneNotSupportedException { 325: 326: Title duplicate = (Title) super.clone(); 327: duplicate.listenerList = new EventListenerList(); 328: // RectangleInsets is immutable => same reference in clone OK 329: return duplicate; 330: } 331: 332: /** 333: * Registers an object for notification of changes to the title. 334: * 335: * @param listener the object that is being registered. 336: */ 337: public void addChangeListener(TitleChangeListener listener) { 338: this.listenerList.add(TitleChangeListener.class, listener); 339: } 340: 341: /** 342: * Unregisters an object for notification of changes to the chart title. 343: * 344: * @param listener the object that is being unregistered. 345: */ 346: public void removeChangeListener(TitleChangeListener listener) { 347: this.listenerList.remove(TitleChangeListener.class, listener); 348: } 349: 350: /** 351: * Notifies all registered listeners that the chart title has changed in 352: * some way. 353: * 354: * @param event an object that contains information about the change to 355: * the title. 356: */ 357: protected void notifyListeners(TitleChangeEvent event) { 358: if (this.notify) { 359: Object[] listeners = this.listenerList.getListenerList(); 360: for (int i = listeners.length - 2; i >= 0; i -= 2) { 361: if (listeners[i] == TitleChangeListener.class) { 362: ((TitleChangeListener) listeners[i + 1]).titleChanged( 363: event); 364: } 365: } 366: } 367: } 368: 369: /** 370: * Tests an object for equality with this title. 371: * 372: * @param obj the object (<code>null</code> not permitted). 373: * 374: * @return <code>true</code> or <code>false</code>. 375: */ 376: public boolean equals(Object obj) { 377: if (obj == this) { 378: return true; 379: } 380: if (!(obj instanceof Title)) { 381: return false; 382: } 383: if (!super.equals(obj)) { 384: return false; 385: } 386: Title that = (Title) obj; 387: if (this.position != that.position) { 388: return false; 389: } 390: if (this.horizontalAlignment != that.horizontalAlignment) { 391: return false; 392: } 393: if (this.verticalAlignment != that.verticalAlignment) { 394: return false; 395: } 396: if (this.notify != that.notify) { 397: return false; 398: } 399: return true; 400: } 401: 402: /** 403: * Returns a hashcode for the title. 404: * 405: * @return The hashcode. 406: */ 407: public int hashCode() { 408: int result = 193; 409: result = 37 * result + ObjectUtilities.hashCode(this.position); 410: result = 37 * result 411: + ObjectUtilities.hashCode(this.horizontalAlignment); 412: result = 37 * result + ObjectUtilities.hashCode(this.verticalAlignment); 413: return result; 414: } 415: 416: /** 417: * Provides serialization support. 418: * 419: * @param stream the output stream. 420: * 421: * @throws IOException if there is an I/O error. 422: */ 423: private void writeObject(ObjectOutputStream stream) throws IOException { 424: stream.defaultWriteObject(); 425: } 426: 427: /** 428: * Provides serialization support. 429: * 430: * @param stream the input stream. 431: * 432: * @throws IOException if there is an I/O error. 433: * @throws ClassNotFoundException if there is a classpath problem. 434: */ 435: private void readObject(ObjectInputStream stream) 436: throws IOException, ClassNotFoundException { 437: stream.defaultReadObject(); 438: this.listenerList = new EventListenerList(); 439: } 440: 441: }