001/* Top-level window containing a Ptolemy II model.
002
003 Copyright (c) 1998-2014 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// FIXME: To do:
028//  - Fix printing.
029//  - Handle file changes (warn when discarding modified models).
030package ptolemy.actor.gui;
031
032import java.awt.BorderLayout;
033import java.awt.Color;
034
035import javax.swing.JOptionPane;
036
037import ptolemy.actor.CompositeActor;
038import ptolemy.actor.ExecutionListener;
039import ptolemy.actor.Manager;
040import ptolemy.kernel.util.NamedObj;
041import ptolemy.moml.Documentation;
042
043///////////////////////////////////////////////////////////////////
044//// ModelFrame
045
046/**
047 ModelFrame is a top-level window containing a Ptolemy II model control panel.
048 It contains a ModelPane, and has a menu bar and a status bar for
049 message reporting.  This class is intended to be used with
050 composite actors, which are executable models.
051
052 @see ModelPane
053 @author Edward A. Lee
054 @version $Id$
055 @since Ptolemy II 0.4
056 @Pt.ProposedRating Green (eal)
057 @Pt.AcceptedRating Yellow (janneck)
058 */
059@SuppressWarnings("serial")
060public class ModelFrame extends PtolemyFrame implements ExecutionListener {
061    // FIXME: Possibly this class could also handle atomic actors.
062    // This might be useful for code generation.
063
064    /** Construct a frame to control the specified Ptolemy II model.
065     *  After constructing this, it is necessary to call
066     *  setVisible(true) to make the frame appear.  This creates an
067     *  instance of ModelPane and puts it in a top-level window.  This
068     *  is typically done by calling show() on the controlling
069     *  tableau.
070     *  @see ModelPane
071     *  @see Tableau#show()
072     *  @see ModelPane
073     *  @param model The model to put in this frame, or null if none.
074     */
075    public ModelFrame(CompositeActor model) {
076        this(model, null);
077    }
078
079    /** Construct a frame to control the specified Ptolemy II model.
080     *  After constructing this, it is necessary to call
081     *  setVisible(true) to make the frame appear.  This creates an
082     *  instance of ModelPane and puts it in a top-level window.  This
083     *  is typically done by calling show() on the controlling
084     *  tableau.
085     *  @see ModelPane
086     *  @see Tableau#show()
087     *  @param model The model to put in this frame, or null if none.
088     *  @param tableau The tableau responsible for this frame, or null if none.
089     */
090    public ModelFrame(CompositeActor model, Tableau tableau) {
091        this(model, tableau, new ModelPane(model));
092    }
093
094    /** Construct a frame to control the specified Ptolemy II model.
095     *  After constructing this, it is necessary to call
096     *  setVisible(true) to make the frame appear.  This creates an
097     *  instance of ModelPane and puts it in a top-level window.
098     *  @see ModelPane
099     *  @see Tableau#show()
100     *  @param model The model to put in this frame, or null if none.
101     *  @param tableau The tableau responsible for this frame, or null if none.
102     *  @param pane The model pane to place inside the frame.
103     */
104    public ModelFrame(CompositeActor model, Tableau tableau, ModelPane pane) {
105        super(model, tableau);
106
107        _pane = pane;
108        getContentPane().add(_pane, BorderLayout.CENTER);
109
110        Manager manager = model.getManager();
111
112        if (manager != null) {
113            manager.addExecutionListener(this);
114        }
115
116        // Make the go button the default.
117        _pane.setDefaultButton();
118
119        // FIXME: Need to do something with the progress bar in the status bar.
120    }
121
122    ///////////////////////////////////////////////////////////////////
123    ////                         public methods                    ////
124
125    /** Report that an execution error has occurred.  This method
126     *  is called by the specified manager.
127     *  @param manager The manager calling this method.
128     *  @param throwable The throwable being reported.
129     */
130    @Override
131    public void executionError(Manager manager, Throwable throwable) {
132        report(throwable);
133    }
134
135    /** Report that execution of the model has finished.
136     *  @param manager The manager calling this method.
137     */
138    @Override
139    public void executionFinished(Manager manager) {
140        // Display the amount of time and memory used.
141        // See http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5571
142        // There is similar code in ptolemy/vergil/basic/RunnableGraphController.java
143        String statusMessage = manager.getStatusMessage();
144        if (!statusMessage.isEmpty()) {
145            statusMessage = ": " + statusMessage;
146        } else {
147            statusMessage = ".";
148        }
149        report("execution finished" + statusMessage);
150    }
151
152    /** Report that a manager state has changed.
153     *  This method is called by the specified manager.
154     *  @param manager The manager calling this method.
155     */
156    @Override
157    public void managerStateChanged(Manager manager) {
158        Manager.State newState = manager.getState();
159
160        if (newState != _previousState) {
161            // There is similar code in ptolemy/vergil/basic/RunnableGraphController.java
162            String statusMessage = manager.getStatusMessage();
163            if (statusMessage.equals(_previousStatusMessage)) {
164                _previousStatusMessage = statusMessage;
165                statusMessage = "";
166            } else {
167                _previousStatusMessage = statusMessage;
168            }
169
170            if (!statusMessage.isEmpty()) {
171                statusMessage = ": " + statusMessage;
172            } else {
173                statusMessage = ".";
174            }
175            report(manager.getState().getDescription() + statusMessage);
176
177            _previousState = newState;
178        }
179    }
180
181    /** Return the container into which to place placeable objects.
182     *  These are objects in a Ptolemy II model that have their own
183     *  user interface, such as plotters.
184     *  @return A container for graphical displays.
185     */
186    public ModelPane modelPane() {
187        return _pane;
188    }
189
190    /** Set background color.  This overrides the base class to set the
191     *  background of the contained ModelPane.
192     *  @param background The background color.
193     */
194    @Override
195    public void setBackground(Color background) {
196        super.setBackground(background);
197        getContentPane().setBackground(background);
198
199        // This seems to be called in a base class constructor, before
200        // this variable has been set. Hence the test against null.
201        if (_pane != null) {
202            _pane.setBackground(background);
203        }
204    }
205
206    /** Set the associated model. This overrides the base class to add
207     *  this object as an execution listener to the manager, if there is
208     *  one.
209     *  @param model The associated model.
210     */
211    public void setModel(CompositeActor model) {
212        super.setModel(model);
213
214        if (model != null) {
215            if (_pane != null) {
216                _pane.setModel(model);
217            }
218
219            Manager manager = model.getManager();
220
221            if (manager != null) {
222                manager.addExecutionListener(this);
223            }
224        }
225    }
226
227    ///////////////////////////////////////////////////////////////////
228    ////                         protected methods                 ////
229
230    /** Clear the current contents.  First, check to see whether
231     *  the contents have been modified, and if so, then prompt the user
232     *  to save them.  A return value of false
233     *  indicates that the user has canceled the action.
234     *  Then, if the user does not cancel, replace the model with a
235     *  new, blank composite actor.
236     *  @return False if the user cancels the clear.
237     */
238    @Override
239    protected boolean _clear() {
240        if (super._clear()) {
241            setModel(new CompositeActor());
242            return true;
243        } else {
244            return false;
245        }
246    }
247
248    /** Close the window.  Override the base class to remove the
249     *  execution listener from the manager, and to notify the contained
250     *  ModelPane.
251     *  @return False if the user cancels on a save query.
252     */
253    @Override
254    protected boolean _close() {
255        boolean result = super._close();
256
257        // FIXME: Shouldn't the following only be done if the
258        // above returns true.
259        NamedObj model = getModel();
260
261        if (model instanceof CompositeActor) {
262            Manager manager = ((CompositeActor) model).getManager();
263
264            if (manager != null) {
265                manager.removeExecutionListener(this);
266            }
267        }
268
269        if (_pane != null) {
270            _pane.stopRun();
271
272            // The second argument is supposed to be a button name, but there
273            // is no button that would have triggered this.
274            _pane.windowClosed(this, "");
275        }
276
277        return result;
278    }
279
280    /** Display more detailed information than given by _about().
281     *  This class displays information contained by Documentation
282     *  attributes in the associated model.
283     *  @see Documentation
284     */
285    @Override
286    protected void _help() {
287        String message = "Ptolemy II model.";
288        NamedObj model = getModel();
289
290        if (model != null) {
291            String tip = Documentation.consolidate(model);
292
293            if (tip != null) {
294                message = "Ptolemy II model:\n" + tip;
295            }
296        }
297
298        JOptionPane.showMessageDialog(this, message, "About " + getTitle(),
299                JOptionPane.INFORMATION_MESSAGE);
300    }
301
302    ///////////////////////////////////////////////////////////////////
303    ////                         private variables                 ////
304    // The pane in which the model data is displayed.
305    private ModelPane _pane;
306
307    /** The previous state of the manager, to avoid reporting it if it hasn't
308     * changed.
309     */
310    private Manager.State _previousState;
311
312    /** The Manager status message from the previous state.
313     */
314    private String _previousStatusMessage = "";
315}