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