Source for org.jfree.chart.title.Title

   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: }