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.graphics3d.swing;
034
035import java.awt.BorderLayout;
036import java.awt.Font;
037import java.awt.FontFormatException;
038import java.awt.event.MouseEvent;
039import java.awt.event.MouseListener;
040import java.io.IOException;
041import java.io.InputStream;
042import java.util.logging.Level;
043import java.util.logging.Logger;
044
045import javax.swing.JButton;
046import javax.swing.JMenuItem;
047import javax.swing.JPanel;
048import javax.swing.JPopupMenu;
049import javax.swing.JToolBar;
050import javax.swing.JMenu;
051
052import org.jfree.chart3d.Resources;
053import org.jfree.chart3d.export.ExportFormat;
054import org.jfree.chart3d.export.ExportFormats;
055import org.jfree.chart3d.internal.Args;
056
057/**
058 * A panel for displaying 3D content, with a toolbar and popup menu to control 
059 * the view.
060 * <br><br>
061 * NOTE: This class is serializable, but the serialization format is subject 
062 * to change in future releases and should not be relied upon for persisting 
063 * instances of this class. 
064 */
065@SuppressWarnings("serial")
066public class DisplayPanel3D extends JPanel implements MouseListener {
067  
068    private static final int FONT_SIZE = 22;
069
070    private static Font FONT_AWESOME;
071    
072    /**
073     * Returns a font for "Font Awesome" (http://fontawesome.io) at the 
074     * specified size.
075     * 
076     * @param size  the point size.
077     * 
078     * @return A font. 
079     */
080    public static Font getFontAwesomeFont(int size) {
081        if (FONT_AWESOME == null) {
082            try {
083                InputStream in = DisplayPanel3D.class.getResourceAsStream(
084                        "fontawesome-webfont.ttf");
085                FONT_AWESOME = Font.createFont(Font.TRUETYPE_FONT, in);
086            } catch (FontFormatException | IOException ex) {
087                Logger.getLogger(Panel3D.class.getName()).log(Level.SEVERE, 
088                        null, ex);
089            }
090        }
091        return FONT_AWESOME.deriveFont(Font.PLAIN, size);
092    }
093
094    /** The 3D content. */
095    Panel3D content;
096    
097    /** The popup menu. */
098    private JPopupMenu popup;
099  
100    /**
101     * Creates a new display panel for the given content, with a toolbar
102     * and popup menu configured.
103     * 
104     * @param content  the content ({@code null} not permitted). 
105     */
106    public DisplayPanel3D(Panel3D content) {
107        this(content, true, true);
108    }
109    
110    /** 
111     * Creates a new display panel.
112     * 
113     * @param content  the content ({@code null} not permitted).
114     * @param toolbar  toolbar?
115     * @param popupMenu  popup menu?
116     */
117    public DisplayPanel3D(Panel3D content, boolean toolbar, boolean popupMenu) {
118        super(new BorderLayout());
119        
120        this.content = content;
121        add(this.content);
122        
123        if (toolbar) {
124            JToolBar tb = createToolBar(content);
125            add(tb, BorderLayout.EAST);
126        }
127        if (popupMenu) {
128            this.popup = createPopupMenu(ExportFormat.values());
129        }
130        this.content.addMouseListener(this);
131    }
132
133    /**
134     * Returns a reference to the content panel.
135     * 
136     * @return A reference to the content panel.
137     */
138    public Panel3D getContent() {
139        return this.content;
140    }
141  
142    /**
143     * Sets the list of export formats that will be shown in the popup menu.
144     * If you provide an empty list, there will be no export submenu in the 
145     * popup menu.
146     * 
147     * @param formats  the list of formats ({@code null} not permitted). 
148     * 
149     * @since 1.2
150     */
151    public void setExportFormats(ExportFormat... formats) {
152        // defer argument checking
153        this.popup = createPopupMenu(formats);
154    }
155    
156    /**
157     * Creates the toolbar used to control zooming etc.
158     * 
159     * @param content  the 3D content that will be updated by toolbar actions.
160     * 
161     * @return The toolbar. 
162     */
163    private JToolBar createToolBar(Panel3D content) {
164        JToolBar tb = new JToolBar(JToolBar.VERTICAL);
165        Font font = getFontAwesomeFont(FONT_SIZE);
166        JButton zoomInButton = new JButton(new ZoomInAction(this.content, 
167                true));
168        zoomInButton.setFont(font);
169        JButton zoomOutButton = new JButton(new ZoomOutAction(this.content, 
170                true));
171        zoomOutButton.setFont(font);
172        JButton zoomToFitButton = new JButton(new ZoomToFitAction(this.content, 
173                true));
174        zoomToFitButton.setFont(font);
175        JButton leftButton = new JButton(new LeftAction(content));
176        leftButton.setFont(font);
177        JButton rightButton = new JButton(new RightAction(content));
178        rightButton.setFont(font);
179        JButton upButton = new JButton(new UpAction(content));
180        upButton.setFont(font);
181        JButton downButton = new JButton(new DownAction(content));
182        downButton.setFont(font);
183        JButton rotateLeftButton = new JButton(new RollLeftAction(content));
184        rotateLeftButton.setFont(font);
185        JButton rotateRightButton = new JButton(new RollRightAction(content));
186        rotateRightButton.setFont(font);
187        tb.add(zoomInButton);
188        tb.add(zoomOutButton);
189        tb.add(zoomToFitButton);
190        tb.add(new JToolBar.Separator());
191        tb.add(leftButton);
192        tb.add(rightButton);
193        tb.add(upButton);
194        tb.add(downButton);
195        tb.add(rotateLeftButton);
196        tb.add(rotateRightButton);
197        return tb;   
198    }
199    
200    /**
201     * Creates a popup menu containing zooming items plus, if exportFormats
202     * is not empty, a submenu of export items.
203     * 
204     * @param exportFormats  an ordered list of export formats to add to the
205     *     submenu ({@code null} not permitted).
206     * 
207     * @return A popup menu.
208     */
209    private JPopupMenu createPopupMenu(ExportFormat... exportFormats) {
210        Args.nullNotPermitted(exportFormats, "exportFormats");
211        JPopupMenu popupMenu = new JPopupMenu();
212        popupMenu.add(new JMenuItem(new ZoomInAction(this.content, false)));
213        popupMenu.add(new JMenuItem(new ZoomOutAction(this.content, false)));
214        popupMenu.add(new JMenuItem(new ZoomToFitAction(this.content, false)));
215        
216        if (exportFormats.length > 0) {
217            JMenu exportSubMenu = new JMenu(Resources.localString("EXPORT_AS"));
218            for (ExportFormat f : exportFormats) {
219                if (f.equals(ExportFormat.PNG)) {
220                    JMenuItem pngItem = new JMenuItem(new ExportToPNGAction(
221                            this.content));
222                    exportSubMenu.add(pngItem);                    
223                } else if (f.equals(ExportFormat.JPEG)) {
224                    JMenuItem jpgItem = new JMenuItem(new ExportToJPEGAction(
225                            this.content));
226                    exportSubMenu.add(jpgItem);                    
227                } else if (f.equals(ExportFormat.PDF)) {
228                    if (ExportFormats.isJFreePDFAvailable()) {
229                        JMenuItem pdfItem = new JMenuItem(new ExportToPDFAction(
230                                this.content));
231                        exportSubMenu.add(pdfItem);
232                    }
233                } else if (f.equals(ExportFormat.SVG)) {
234                    if (ExportFormats.isJFreeSVGAvailable()) {
235                        JMenuItem svgItem = new JMenuItem(new ExportToSVGAction(
236                                this.content));
237                        exportSubMenu.add(svgItem);
238                    }
239                }
240            }
241            if (exportSubMenu.getItemCount() > 0) {
242                popupMenu.addSeparator();
243                popupMenu.add(exportSubMenu);           
244            }
245        }
246        return popupMenu;
247    }
248    
249    /**
250     * This method does nothing.  
251     * 
252     * @param e  the mouse event.
253     */
254    @Override
255    public void mouseClicked(MouseEvent e) {
256        // nothing to do
257    }
258
259    /**
260     * Checks if the popup is triggered in which case it is displayed.
261     * 
262     * @param e  the mouse event. 
263     */
264    @Override
265    public void mousePressed(MouseEvent e) {
266        // popup is triggered on mousePressed for Linux and Mac, but Windows
267        // is mouseReleased
268        if (e.isPopupTrigger()) {
269            if (this.popup != null) {
270                this.popup.show(this, e.getX(), e.getY());
271                e.consume();
272            }
273        }
274    }
275
276    /**
277     * Checks if the popup is triggered in which case it is displayed.
278     * 
279     * @param e  the mouse event. 
280     */
281    @Override
282    public void mouseReleased(MouseEvent e) {
283        // popup is triggered on mouseReleased for Windows, but Linux and Mac
284        // is mousePressed
285        if (e.isPopupTrigger()) {
286            if (this.popup != null) {
287                this.popup.show(this, e.getX(), e.getY());
288                e.consume();
289            }
290        }
291    }
292
293    /**
294     * This method does nothing.  
295     * 
296     * @param e  the mouse event.
297     */
298    @Override
299    public void mouseEntered(MouseEvent e) {
300        // nothing to do
301    }
302
303    /**
304     * This method does nothing.  
305     * 
306     * @param e  the mouse event.
307     */
308    @Override
309    public void mouseExited(MouseEvent e) {
310        // nothing to do
311    }
312
313}