001/* An extended simple graph view for Ptolemy models
002
003 Copyright (c) 1998-2016 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.vergil.basic;
029
030import java.awt.BorderLayout;
031import java.awt.Component;
032import java.awt.Toolkit;
033import java.awt.event.ActionEvent;
034import java.awt.event.KeyEvent;
035import java.awt.event.KeyListener;
036
037import javax.swing.AbstractAction;
038import javax.swing.Action;
039import javax.swing.ActionMap;
040import javax.swing.InputMap;
041import javax.swing.JDialog;
042import javax.swing.KeyStroke;
043
044import diva.gui.GUIUtilities;
045import ptolemy.actor.gui.Tableau;
046import ptolemy.gui.UndeferredGraphicalMessageHandler;
047import ptolemy.kernel.CompositeEntity;
048import ptolemy.moml.LibraryAttribute;
049
050///////////////////////////////////////////////////////////////////
051//// ExtendedGraphFrame
052
053/**
054 An graph view for ptolemy models extended with the capability
055 to display the model in full-screen mode.
056
057 @author  Edward A. Lee
058 @version $Id$
059 @since Ptolemy II 2.0
060 @Pt.ProposedRating Red (neuendor)
061 @Pt.AcceptedRating Red (johnr)
062 */
063@SuppressWarnings("serial")
064public abstract class ExtendedGraphFrame extends BasicGraphFrame {
065    /** Construct a frame associated with the specified Ptolemy II model.
066     *  After constructing this, it is necessary
067     *  to call setVisible(true) to make the frame appear.
068     *  This is typically done by calling show() on the controlling tableau.
069     *  This constructor results in a graph frame that obtains its library
070     *  either from the model (if it has one) or the default library defined
071     *  in the configuration.
072     *  @see Tableau#show()
073     *  @param entity The model to put in this frame.
074     *  @param tableau The tableau responsible for this frame.
075     */
076    public ExtendedGraphFrame(CompositeEntity entity, Tableau tableau) {
077        this(entity, tableau, null);
078    }
079
080    /** Construct a frame associated with the specified Ptolemy II model.
081     *  After constructing this, it is necessary
082     *  to call setVisible(true) to make the frame appear.
083     *  This is typically done by calling show() on the controlling tableau.
084     *  This constructor results in a graph frame that obtains its library
085     *  either from the model (if it has one), or the <i>defaultLibrary</i>
086     *  argument (if it is non-null), or the default library defined
087     *  in the configuration.
088     *  @see Tableau#show()
089     *  @param entity The model to put in this frame.
090     *  @param tableau The tableau responsible for this frame.
091     *  @param defaultLibrary An attribute specifying the default library
092     *   to use if the model does not have a library.
093     */
094    public ExtendedGraphFrame(CompositeEntity entity, Tableau tableau,
095            LibraryAttribute defaultLibrary) {
096        super(entity, tableau, defaultLibrary);
097        _initExtendedGraphFrame();
098    }
099
100    ///////////////////////////////////////////////////////////////////
101    ////                         public methods                    ////
102
103    /** Cancel full screen mode. Note that this should be called
104     *  in the swing event thread.
105     */
106    public void cancelFullScreen() {
107        if (_screen == null) {
108            // Already canceled.
109            return;
110        }
111
112        _screen.dispose();
113        _screen = null;
114
115        // Only include the palettePane and panner if there is an actor library.
116        // The ptinyViewer configuration uses this.
117        if ((CompositeEntity) getConfiguration()
118                .getEntity("actor library") != null) {
119            // Put the component back into the original window.
120            _splitPane.setRightComponent(_getRightComponent());
121
122            // Restore association with the graph panner.
123            _graphPanner.setCanvas(getJGraph());
124        } else {
125            getContentPane().add(_getRightComponent());
126        }
127        pack();
128        show();
129        UndeferredGraphicalMessageHandler.setContext(_previousDefaultContext);
130        toFront();
131        _getRightComponent().requestFocus();
132    }
133
134    /** Go to full screen.
135     */
136    public void fullScreen() {
137        if (_screen != null) {
138            // Already in full screen mode.
139            _screen.toFront();
140            return;
141        }
142
143        // NOTE: Do not make the original graph frame the owner,
144        // because we are going to hide it, and if it is the owner,
145        // then the new frame will be hidden also.
146        _screen = new JDialog();
147        _screen.getContentPane().setLayout(new BorderLayout());
148
149        // Set to full-screen size.
150        Toolkit toolkit = _screen.getToolkit();
151        int width = toolkit.getScreenSize().width;
152        int height = toolkit.getScreenSize().height;
153        _screen.setSize(width, height);
154
155        _screen.setUndecorated(true);
156        _screen.getContentPane().add(getJGraph(), BorderLayout.CENTER);
157
158        // NOTE: Have to avoid the following, which forces the
159        // dialog to resize the preferred size of _jgraph, which
160        // nullifies the call to setSize() above.
161        // _screen.pack();
162        _screen.setVisible(true);
163
164        // Make the new screen the default context for modal messages.
165        _previousDefaultContext = UndeferredGraphicalMessageHandler
166                .getContext();
167        UndeferredGraphicalMessageHandler.setContext(_screen);
168
169        // NOTE: As usual with swing, what the UI does is pretty
170        // random, and doesn't correlate much with the documentation.
171        // The following two lines do not work if _screen is a
172        // JWindow instead of a JDialog.  There is no apparent
173        // reason for this, but this is why we use JDialog.
174        // Unfortunately, apparently the JDialog does not appear
175        // in the Windows task bar.
176        _screen.toFront();
177        getJGraph().requestFocus();
178
179        _screen.setResizable(false);
180
181        // Bind escape key to remove full-screen mode.
182        ActionMap actionMap = getJGraph().getActionMap();
183
184        // Use the action as both a key and the action.
185        actionMap.put(_fullScreenAction, _fullScreenAction);
186
187        InputMap inputMap = getJGraph().getInputMap();
188        inputMap.put(KeyStroke.getKeyStroke("ESCAPE"), _fullScreenAction);
189
190        // The ptinyViewer configuration will have a null _graphPanner.
191        if (_graphPanner != null) {
192            // Remove association with the graph panner.
193            _graphPanner.setCanvas(null);
194        }
195
196        setVisible(false);
197    }
198
199    /** Dispose of this frame.
200     *     Override this dispose() method to unattach any listeners that may keep
201     *  this model from getting garbage collected.  This method invokes the
202     *  dispose() method of the superclass,
203     *  {@link ptolemy.vergil.basic.BasicGraphFrame}.
204     */
205    @Override
206    public void dispose() {
207        if (_debugClosing) {
208            System.out.println(
209                    "ExtendedGraphFrame.dispose() : " + this.getName());
210        }
211
212        _fullScreenAction = null;
213        super.dispose();
214    }
215
216    ///////////////////////////////////////////////////////////////////
217    ////                         protected methods                 ////
218
219    /**
220     * Initialize this class.
221     * In this base class, a button for the full screen action
222     * is added to the toolbar.
223     */
224    protected void _initExtendedGraphFrame() {
225        GUIUtilities.addToolBarButton(_toolbar, _fullScreenAction);
226    }
227
228    /** Create the menus that are used by this frame.
229     */
230    @Override
231    protected void _addMenus() {
232        super._addMenus();
233
234        _viewMenu.addSeparator();
235        GUIUtilities.addHotKey(_getRightComponent(), _fullScreenAction);
236        GUIUtilities.addMenuItem(_viewMenu, _fullScreenAction);
237    }
238
239    /** Invoke the close() method of the superclass and optionally
240     *  print a debugging message.
241     *  If {@link ptolemy.actor.gui.Tableau#_debugClosing} is
242     *  true, then a message is printed to standard out.
243     *  This method is used for debugging memory leaks.
244     *  @return True if the close completes, and false otherwise.
245     */
246    @Override
247    protected boolean _close() {
248        if (_debugClosing) {
249            System.out
250                    .println("ExtendedGraphFrame._close() : " + this.getName());
251        }
252
253        return super._close();
254    }
255
256    ///////////////////////////////////////////////////////////////////
257    ////                         private variables                 ////
258
259    /** Action for displaying in full-screen mode. */
260    private Action _fullScreenAction = new FullScreenAction("Full Screen");
261
262    /** Default context for dialogs before going to full-screen mode. */
263    private Component _previousDefaultContext;
264
265    /** If we are in full-screen mode, this will be non-null. */
266    private JDialog _screen;
267
268    ///////////////////////////////////////////////////////////////////
269    ////                         inner classes                     ////
270
271    ///////////////////////////////////////////////////////////////////
272    //// FullScreenAction
273    /** An action to display in full-screen mode. */
274    public class FullScreenAction extends AbstractAction
275            implements KeyListener {
276        /** Construct a full screen action.
277         *  @param description A string that describes the action.  Spaces are
278         *  permitted, each word is usually capitalized.
279         */
280        public FullScreenAction(String description) {
281            super(description);
282
283            // Load the image by using the absolute path to the gif.
284            // Using a relative location should work, but it does not.
285            // Use the resource locator of the class.
286            // For more information, see
287            // jdk1.3/docs/guide/resources/resources.html
288            GUIUtilities.addIcons(this,
289                    new String[][] {
290                            { "/ptolemy/vergil/basic/img/fullscreen.gif",
291                                    GUIUtilities.LARGE_ICON },
292                            { "/ptolemy/vergil/basic/img/fullscreen_o.gif",
293                                    GUIUtilities.ROLLOVER_ICON },
294                            { "/ptolemy/vergil/basic/img/fullscreen_ov.gif",
295                                    GUIUtilities.ROLLOVER_SELECTED_ICON },
296                            { "/ptolemy/vergil/basic/img/fullscreen_on.gif",
297                                    GUIUtilities.SELECTED_ICON } });
298
299            putValue("tooltip", description);
300
301            putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_S));
302        }
303
304        /** If we are in full-screen mode, then revert; otherwise, go
305         *  to full-screen mode.
306         *  @param e The action event, ignored by this method.
307         */
308        @Override
309        public void actionPerformed(ActionEvent e) {
310            if (_screen == null) {
311                fullScreen();
312            } else {
313                cancelFullScreen();
314            }
315        }
316
317        /** React to a key press by removing full-screen mode.
318         *  @param e The key event, ignored by this method.
319         */
320        @Override
321        public void keyPressed(KeyEvent e) {
322            if (_screen != null) {
323                cancelFullScreen();
324            }
325        }
326
327        /** React to a key press by removing full-screen mode.
328         *  @param e The key event, ignored by this method.
329         */
330        @Override
331        public void keyReleased(KeyEvent e) {
332            if (_screen != null) {
333                cancelFullScreen();
334            }
335        }
336
337        /** React to a key press by removing full-screen mode.
338         *  @param e The key event, ignored by this method.
339         */
340        @Override
341        public void keyTyped(KeyEvent e) {
342            if (_screen != null) {
343                cancelFullScreen();
344            }
345        }
346    }
347}