    001/* ===========================================================
    002 * Orson Charts : a 3D chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C)opyright 2013-2016, by Object Refinery Limited.  All rights reserved.
    006 *
    007 * http://www.object-refinery.com/orsoncharts/index.html
    008 *
    009 * This program is free software: you can redistribute it and/or modify
    010 * it under the terms of the GNU General Public License as published by
    011 * the Free Software Foundation, either version 3 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This program is distributed in the hope that it will be useful,
    015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
    016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    017 * GNU General Public License for more details.
    018 *
    019 * You should have received a copy of the GNU General Public License
    020 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    021 *
    022 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    023 * Other names may be trademarks of their respective owners.]
    024 *
    025 * If you do not wish to be bound by the terms of the GPL, an alternative
    026 * commercial license can be purchased.  For details, please see visit the
    027 * Orson Charts home page:
    028 *
    029 * http://www.object-refinery.com/orsoncharts/index.html
    030 *
    031 */
    032
    033package com.orsoncharts.renderer;
    034
    035import java.awt.Color;
    036import java.awt.Font;
    037import java.io.Serializable;
    038import javax.swing.event.EventListenerList;
    039
    040import com.orsoncharts.ChartElementVisitor;
    041import com.orsoncharts.label.ItemLabelPositioning;
    042import com.orsoncharts.util.ArgChecks;
    043
    044/**
    045 * A base class for 3D renderers.
    046 */
    047public abstract class AbstractRenderer3D implements Renderer3D, Serializable {
    048
    049    /** The font used to draw item labels. */
    050    private Font itemLabelFont;
    051
    052    /** The foreground color for item labels. */
    053    private Color itemLabelColor;
    054
    055    /** The background color for item labels. */
    056    private Color itemLabelBackgroundColor;
    057
    058    /** The item label positioning. */
    059    private ItemLabelPositioning itemLabelPositioning;
    060
    061    /** Storage for registered change listeners. */
    062    private transient EventListenerList listenerList;
    063
    064    /**
    065     * A flag that controls whether or not the renderer will notify listeners
    066     * of changes (defaults to {@code true}, but sometimes it is useful
    067     * to disable this).
    068     */
    069    private boolean notify;
    070
    071    /**
    072     * Default constructor.
    073     */
    074    protected AbstractRenderer3D() {
    075        this.itemLabelFont = new Font(Font.SERIF, Font.PLAIN, 12);
    076        this.itemLabelColor = Color.WHITE;
    077        this.itemLabelBackgroundColor = new Color(100, 100, 100, 100); //Renderer3D.TRANSPARENT_COLOR;
    078        this.itemLabelPositioning = ItemLabelPositioning.CENTRAL;
    079        this.listenerList = new EventListenerList();
    080        this.notify = true;
    081    }
    082
    083    /**
    084     * Returns the font used to display item labels, if there are any.
    085     * The default value is {@code Font(Font.SERIF, Font.PLAIN, 8)}.
    086     *
    087     * @return The font (never {@code null}).
    088     *
    089     * @since 1.3
    090     */
    091    public Font getItemLabelFont() {
    092        return itemLabelFont;
    093    }
    094
    095    /**
    096     * Sets the font used to display item labels and sends a change event
    097     * to all registered listeners.
    098     *
    099     * @param itemLabelFont  the font ({@code null} not permitted).
    100     *
    101     * @since 1.3
    102     */
    103    public void setItemLabelFont(Font itemLabelFont) {
    104        ArgChecks.nullNotPermitted(itemLabelFont, "itemLabelFont");
    105        this.itemLabelFont = itemLabelFont;
    106        fireChangeEvent(true);
    107    }
    108
    109    /**
    110     * Returns the foreground color used to display item labels.  The default
    111     * value is {@code Color.BLACK}.
    112     *
    113     * @return The foreground color (never {@code null}).
    114     *
    115     * @since 1.3
    116     */
    117    public Color getItemLabelColor() {
    118        return itemLabelColor;
    119    }
    120
    121    /**
    122     * Sets the foreground color used to display item labels and sends a
    123     * change event to all registered listeners.
    124     *
    125     * @param itemLabelColor  the new color ({@code null} not permitted).
    126     *
    127     * @since 1.3
    128     */
    129    public void setItemLabelColor(Color itemLabelColor) {
    130        ArgChecks.nullNotPermitted(itemLabelColor, "itemLabelColor");
    131        this.itemLabelColor = itemLabelColor;
    132        fireChangeEvent(true);
    133    }
    134
    135    /**
    136     * Returns the background color for item labels.
    137     *
    138     * @return The background color (never {@code null}).
    139     *
    140     * @since 1.3
    141     */
    142    public Color getItemLabelBackgroundColor() {
    143        return itemLabelBackgroundColor;
    144    }
    145
    146    /**
    147     * Sets the background color and sends a change event to all registered
    148     * listeners.
    149     *
    150     * @param color  the new color ({@code null} not permitted).
    151     *
    152     * @since 1.3
    153     */
    154    public void setItemLabelBackgroundColor(Color color) {
    155        ArgChecks.nullNotPermitted(color, "color");
    156        this.itemLabelBackgroundColor = color ;
    157        fireChangeEvent(true);
    158    }
    159
    160    /**
    161     * Returns the item label positioning.  The default value is
    162     * {@link ItemLabelPositioning#CENTRAL}.
    163     *
    164     * @return The item label positioning (never {@code null}).
    165     *
    166     * @since 1.3
    167     */
    168    public ItemLabelPositioning getItemLabelPositioning() {
    169        return itemLabelPositioning;
    170    }
    171
    172    /**
    173     * Sets the item label positioning and sends a change event to all
    174     * registered listeners.
    175     *
    176     * @param positioning  the new positioning ({@code null} not
    177     *     permitted).
    178     *
    179     * @since 1.3
    180     */
    181    public void setItemLabelPositioning(ItemLabelPositioning positioning) {
    182        ArgChecks.nullNotPermitted(positioning, "positioning");
    183        this.itemLabelPositioning = positioning;
    184        fireChangeEvent(true);
    185    }
    186
    187    /**
    188     * Returns a flag that controls whether or not change events are sent to
    189     * registered listeners.
    190     *
    191     * @return A boolean.
    192     *
    193     * @see #setNotify(boolean)
    194     */
    195    public boolean isNotify() {
    196        return this.notify;
    197    }
    198
    199    /**
    200     * Sets a flag that controls whether or not listeners receive
    201     * {@link Renderer3DChangeEvent} notifications.
    202     *
    203     * @param notify  a boolean.
    204     *
    205     * @see #isNotify()
    206     */
    207    public void setNotify(boolean notify) {
    208        this.notify = notify;
    209        // if the flag is being set to true, there may be queued up changes...
    210        if (notify) {
    211            fireChangeEvent(true);
    212        }
    213    }
    214
    215    /**
    216     * Receives a {@link ChartElementVisitor}.  This is part of a general
    217     * purpose mechanism for traversing the chart structure and performing
    218     * operations on the elements in the structure.  You won't normally call
    219     * this method directly.
    220     *
    221     * @param visitor  the visitor ({@code null} not permitted).
    222     *
    223     * @since 1.2
    224     */
    225    @Override
    226    public void receive(ChartElementVisitor visitor) {
    227        visitor.visit(this);
    228    }
    229
    230    /**
    231     * Registers an object for notification of changes to the renderer.
    232     *
    233     * @param listener  the object to be registered.
    234     *
    235     * @see #removeChangeListener(Renderer3DChangeListener)
    236     */
    237    @Override
    238    public void addChangeListener(Renderer3DChangeListener listener) {
    239        this.listenerList.add(Renderer3DChangeListener.class, listener);
    240    }
    241
    242    /**
    243     * Unregisters an object for notification of changes to the renderer.
    244     *
    245     * @param listener  the object to be unregistered.
    246     *
    247     * @see #addChangeListener(Renderer3DChangeListener)
    248     */
    249    @Override
    250    public void removeChangeListener(Renderer3DChangeListener listener) {
    251        this.listenerList.remove(Renderer3DChangeListener.class, listener);
    252    }
    253
    254    /**
    255     * Notifies all registered listeners that the plot has been modified.
    256     *
    257     * @param event  information about the change event.
    258     */
    259    public void notifyListeners(Renderer3DChangeEvent event) {
    260        // if the 'notify' flag has been switched to false, we don't notify
    261        // the listeners
    262        if (!this.notify) {
    263            return;
    264        }
    265        Object[] listeners = this.listenerList.getListenerList();
    266        for (int i = listeners.length - 2; i >= 0; i -= 2) {
    267            if (listeners[i] == Renderer3DChangeListener.class) {
    268                ((Renderer3DChangeListener) listeners[i + 1]).rendererChanged(
    269                        event);
    270            }
    271        }
    272    }
    273
    274    /**
    275     * Sends a {@link Renderer3DChangeEvent} to all registered listeners.
    276     *
    277     * @param requiresWorldUpdate  a flag indicating whether or not the change
    278     *     requires the 3D world to be updated.
    279     */
    280    protected void fireChangeEvent(boolean requiresWorldUpdate) {
    281        notifyListeners(new Renderer3DChangeEvent(this, requiresWorldUpdate));
    282    }
    283
    284    /**
    285     * Tests this renderer for equality with an arbitrary object.  The
    286     * change listeners are NOT considered in the test, but the
    287     * {@code notify} flag is taken into account.
    288     *
    289     * @param obj  the object ({@code null} permitted).
    290     *
    291     * @return A boolean.
    292     */
    293    @Override
    294    public boolean equals(Object obj) {
    295        if (obj == this) {
    296            return true;
    297        }
    298        if (!(obj instanceof AbstractRenderer3D)) {
    299            return false;
    300        }
    301        AbstractRenderer3D that = (AbstractRenderer3D) obj;
    302        if (this.notify != that.notify) {
    303            return false;
    304        }
    305        if (!this.itemLabelFont.equals(that.itemLabelFont)) {
    306            return false;
    307        }
    308        if (!this.itemLabelColor.equals(that.itemLabelColor)) {
    309            return false;
    310        }
    311        if (!this.itemLabelBackgroundColor.equals(
    312                that.itemLabelBackgroundColor)) {
    313            return false;
    314        }
    315        if (this.itemLabelPositioning != that.itemLabelPositioning) {
    316            return false;
    317        }
    318        return true;
    319    }
    320}