001/* ===========================================================
002 * Orson Charts : a 3D chart library for the Java(tm) platform
003 * ===========================================================
004 * 
005 * (C)opyright 2013-2022, by David Gilbert.  All rights reserved.
006 * 
007 * https://github.com/jfree/orson-charts
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 org.jfree.chart3d;
034
035import java.awt.event.ComponentEvent;
036import java.awt.event.ComponentListener;
037import java.awt.event.MouseEvent;
038import java.io.IOException;
039import java.io.ObjectInputStream;
040import java.util.EventListener;
041import javax.swing.event.EventListenerList;
042
043import org.jfree.chart3d.data.ItemKey;
044import org.jfree.chart3d.graphics3d.Object3D;
045import org.jfree.chart3d.graphics3d.RenderedElement;
046import org.jfree.chart3d.graphics3d.RenderingInfo;
047import org.jfree.chart3d.graphics3d.swing.DisplayPanel3D;
048import org.jfree.chart3d.graphics3d.swing.Panel3D;
049import org.jfree.chart3d.interaction.Chart3DMouseEvent;
050import org.jfree.chart3d.interaction.Chart3DMouseListener;
051import org.jfree.chart3d.internal.Args;
052
053/**
054 * A panel designed to display a {@link Chart3D} in a Swing-based desktop
055 * application.  The panel registers with the chart to receive change 
056 * notifications, and when these are received the chart is automatically 
057 * repainted.
058 * <br><br>
059 * This panel will display the chart, but does not include additional features
060 * such as the view toolbar and popup menu (these are provided by the 
061 * {@link DisplayPanel3D} class).
062 * <br><br>
063 * NOTE: This class is serializable, but the serialization format is subject 
064 * to change in future releases and should not be relied upon for persisting 
065 * instances of this class.
066 */
067@SuppressWarnings("serial")
068public class Chart3DPanel extends Panel3D implements Chart3DChangeListener, 
069        ComponentListener {
070
071    /**
072     * The chart being rendered.
073     */
074    private final Chart3D chart;
075    
076    /** Auto-fit the chart on resize? */
077    private final boolean autoFitOnPanelResize;
078    
079    /** Storage for registered (chart) mouse listeners. */
080    private transient EventListenerList chartMouseListeners;
081
082    /**
083     * Creates a new chart panel to display the specified chart.
084     *
085     * @param chart the chart.
086     */
087    public Chart3DPanel(Chart3D chart) {
088        super(chart);
089        this.chartMouseListeners = new EventListenerList();
090        this.chart = chart;
091        this.chart.addChangeListener(this);
092        addComponentListener(this);
093        this.autoFitOnPanelResize = false;
094        registerForTooltips();
095    }
096
097    /**
098     * Returns the chart being displayed in this panel.
099     * 
100     * @return The chart (never {@code null}).
101     * 
102     * @since 1.3
103     */
104    public Chart3D getChart() {
105        return this.chart;
106    }
107    
108    /**
109     * Receives notification when the chart has been modified, and responds
110     * by completely repainting the panel and chart.
111     * 
112     * @param event  the event. 
113     */
114    @Override
115    public void chartChanged(Chart3DChangeEvent event) {
116        repaint();
117    }
118
119    @Override
120    public void componentResized(ComponentEvent e) {
121        if (this.autoFitOnPanelResize) {
122            zoomToFit();
123        }
124    }
125
126    @Override
127    public void componentMoved(ComponentEvent e) {
128        // do nothing
129    }
130
131    @Override
132    public void componentShown(ComponentEvent e) {
133        // do nothing
134    }
135
136    @Override
137    public void componentHidden(ComponentEvent e) {
138        // do nothing
139    }
140
141    @Override
142    public String getToolTipText(MouseEvent e) {
143        RenderingInfo info = getRenderingInfo();
144        if (info == null) {
145            return null;
146        }
147        Object3D object = info.fetchObjectAt(e.getX(), e.getY());
148        if (object != null) {
149            ItemKey key = (ItemKey) object.getProperty(Object3D.ITEM_KEY);
150            if (key != null) {
151                return chart.getPlot().generateToolTipText(key);
152            }
153        }
154        return null;
155    }
156
157    /**
158     * Receives a mouse event and passes it on to registered 
159     * {@link Chart3DMouseListener}s along with the underlying rendered
160     * element if any.
161     * 
162     * @param e  the mouse event.
163     */
164    @Override
165    public void mouseClicked(MouseEvent e) {
166        Object[] listeners = this.chartMouseListeners.getListeners(
167                Chart3DMouseListener.class);
168        if (listeners.length == 0) {
169            return;
170        }
171        RenderedElement element = null;
172        RenderingInfo info = getRenderingInfo();
173        if (info != null) {
174            element = info.findElementAt(e.getX(), e.getY());
175        }
176        Chart3DMouseEvent chartEvent = new Chart3DMouseEvent(this.chart, e,
177                element);
178        for (int i = listeners.length - 1; i >= 0; i -= 1) {
179            ((Chart3DMouseListener) listeners[i]).chartMouseClicked(chartEvent);
180        }
181        super.mouseClicked(e);
182    }
183
184    /**
185     * Receives a mouse event and passes it on to registered 
186     * {@link Chart3DMouseListener}s along with the underlying rendered
187     * element if any.
188     * 
189     * @param e  the mouse event.
190     */
191    @Override
192    public void mouseMoved(MouseEvent e) {
193        Object[] listeners = this.chartMouseListeners.getListeners(
194                Chart3DMouseListener.class);
195        if (listeners.length == 0) {
196            return;
197        }
198        RenderedElement element = null;
199        RenderingInfo info = getRenderingInfo();
200        if (info != null) {
201            element = info.findElementAt(e.getX(), e.getY());
202        }
203        Chart3DMouseEvent chartEvent = new Chart3DMouseEvent(this.chart, e,
204                element);
205        for (int i = listeners.length - 1; i >= 0; i -= 1) {
206            ((Chart3DMouseListener) listeners[i]).chartMouseMoved(chartEvent);
207        }
208        super.mouseMoved(e);
209    }
210
211    /**
212     * Adds a listener to the list of objects listening for chart mouse events.
213     *
214     * @param listener  the listener ({@code null} not permitted).
215     * 
216     * @since 1.3
217     */
218    public void addChartMouseListener(Chart3DMouseListener listener) {
219        Args.nullNotPermitted(listener, "listener");
220        this.chartMouseListeners.add(Chart3DMouseListener.class, listener);
221    }
222
223    /**
224     * Removes a listener from the list of objects listening for chart mouse
225     * events.
226     *
227     * @param listener  the listener ({@code null} not permitted).
228     * 
229     * @since 1.3
230     */
231    public void removeChartMouseListener(Chart3DMouseListener listener) {
232        Args.nullNotPermitted(listener, "listener");
233        this.chartMouseListeners.remove(Chart3DMouseListener.class, listener);
234    }
235
236    /**
237     * Returns an array of the listeners of the given type registered with the
238     * panel.
239     *
240     * @param listenerType  the listener type.
241     *
242     * @return An array of listeners.
243     */
244    @Override
245    public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
246        if (listenerType == Chart3DMouseListener.class) {
247            // fetch listeners from local storage
248            return this.chartMouseListeners.getListeners(listenerType);
249        }
250        else {
251            return super.getListeners(listenerType);
252        }
253    }
254    
255    /**
256     * Provides serialization support.
257     *
258     * @param stream  the input stream.
259     *
260     * @throws IOException  if there is an I/O error.
261     * @throws ClassNotFoundException  if there is a classpath problem.
262     */
263    private void readObject(ObjectInputStream stream)
264        throws IOException, ClassNotFoundException {
265        stream.defaultReadObject();
266        // we create a new but empty chartMouseListeners list
267        this.chartMouseListeners = new EventListenerList();
268        // register as a listener with sub-components...
269        if (this.chart != null) {
270            this.chart.addChangeListener(this);
271        }
272    }
273
274}