    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
    091                                implements Block, 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
    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    }