    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2005, 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-2005, 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.1 2005/10/25 20:58:34 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     * @author David Berry
    091     */
    092    public abstract class Title extends AbstractBlock
    093                                implements Block, Cloneable, Serializable {
    094
    095        /** For serialization. */
    096        private static final long serialVersionUID = -6675162505277817221L;
    097
    098        /** The default title position. */
    099        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    }