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