001/*  This actor opens a window to display the specified model and applies its inputs to the model.
002
003 @Copyright (c) 2007-2016 The Regents of the University of California.
004 All rights reserved.
005
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the
009 above copyright notice and the following two paragraphs appear in all
010 copies of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION 2
026 COPYRIGHTENDKEY
027 */
028package ptolemy.vergil.actor.lib;
029
030import java.awt.Container;
031import java.awt.Dimension;
032import java.net.URL;
033import java.util.List;
034
035import diva.graph.GraphPane;
036import diva.graph.JGraph;
037import ptolemy.actor.TypedIOPort;
038import ptolemy.actor.gui.AbstractPlaceableActor;
039import ptolemy.actor.gui.Configuration;
040import ptolemy.actor.gui.Effigy;
041import ptolemy.actor.gui.PtolemyEffigy;
042import ptolemy.actor.gui.SizeAttribute;
043import ptolemy.actor.gui.Tableau;
044import ptolemy.actor.gui.TableauFrame;
045import ptolemy.data.StringToken;
046import ptolemy.data.expr.FileParameter;
047import ptolemy.data.type.BaseType;
048import ptolemy.kernel.CompositeEntity;
049import ptolemy.kernel.util.Attribute;
050import ptolemy.kernel.util.ChangeListener;
051import ptolemy.kernel.util.ChangeRequest;
052import ptolemy.kernel.util.IllegalActionException;
053import ptolemy.kernel.util.InternalErrorException;
054import ptolemy.kernel.util.NameDuplicationException;
055import ptolemy.kernel.util.NamedObj;
056import ptolemy.kernel.util.Workspace;
057import ptolemy.moml.MoMLChangeRequest;
058import ptolemy.moml.MoMLParser;
059import ptolemy.moml.ParserAttribute;
060import ptolemy.vergil.actor.ActorEditorGraphController;
061import ptolemy.vergil.actor.ActorGraphModel;
062
063///////////////////////////////////////////////////////////////////
064//// ModelDisplay
065
066/**
067 This actor opens a window to display the specified model.
068 If inputs are provided, they are expected to be MoML strings
069 that are to be applied to the model. This can be used, for
070 example, to create animations.
071
072 @author  Edward A. Lee, Elaine Cheong
073 @version $Id$
074 @since Ptolemy II 8.0
075 @Pt.ProposedRating Yellow (eal)
076 @Pt.AcceptedRating Red (cxh)
077 */
078public class ModelDisplay extends AbstractPlaceableActor
079        implements ChangeListener {
080
081    /** Construct an actor with the specified container and name.
082     *  @param container The container.
083     *  @param name The name of this actor.
084     *  @exception IllegalActionException If the entity cannot be contained
085     *   by the proposed container.
086     *  @exception NameDuplicationException If the container already has an
087     *   actor with this name.
088     */
089    public ModelDisplay(CompositeEntity container, String name)
090            throws IllegalActionException, NameDuplicationException {
091        super(container, name);
092        modelURL = new FileParameter(this, "modelURL");
093        input = new TypedIOPort(this, "input", true, false);
094        input.setTypeEquals(BaseType.STRING);
095    }
096
097    ///////////////////////////////////////////////////////////////////
098    ////                   ports and parameters                    ////
099
100    /** The file or URL from which to read the starting point model.
101     *  The model is read when this parameter is set or changed.
102     */
103    public FileParameter modelURL;
104
105    /** The input port through which to provide MoML to modify the model.
106     *  This has type string.
107     */
108    public TypedIOPort input;
109
110    ///////////////////////////////////////////////////////////////////
111    ////                         public methods                    ////
112
113    /** React to a change in an attribute.  If the attribute is
114     *  modelURL, then read the specified URL and parse it to create
115     *  the entity to display.
116     *  @param attribute The attribute that changed.
117     *  @exception IllegalActionException If the change is not acceptable
118     *   to this container (not thrown in this base class).
119     */
120    @Override
121    public void attributeChanged(Attribute attribute)
122            throws IllegalActionException {
123        if (attribute == modelURL) {
124            URL url = modelURL.asURL();
125            if (url != null) {
126                MoMLParser parser = new MoMLParser();
127                try {
128                    _entity = parser.parse(null, url);
129                    ParserAttribute parserAttribute = new ParserAttribute(
130                            _entity, "_parser");
131                    parserAttribute.setParser(parser);
132                } catch (Exception ex) {
133                    throw new IllegalActionException(this, ex,
134                            "Failed to read model from: " + url);
135                }
136            } else {
137                // No URL given, so we should create a blank entity.
138                _entity = _createBlankEntity();
139            }
140            // Make sure there is no display of the old entity.
141            place(null);
142        } else {
143            super.attributeChanged(attribute);
144        }
145    }
146
147    /** Do nothing.
148     *  @param change The change that succeeded.
149     */
150    @Override
151    public void changeExecuted(ChangeRequest change) {
152    }
153
154    /** Stop executing the model.
155     *  @param change The change.
156     *  @param exception The exception.
157     */
158    @Override
159    public void changeFailed(ChangeRequest change, Exception exception) {
160        stop();
161    }
162
163    /** Read the input, if there is any, and issue a change
164     *  request to apply the MoML in the input to the displayed model.
165     *  @exception IllegalActionException If there is an error reading
166     *   the input.
167     */
168    @Override
169    public void fire() throws IllegalActionException {
170        super.fire();
171        if (input.isOutsideConnected() && input.hasToken(0)) {
172            String moml = ((StringToken) input.get(0)).stringValue();
173            MoMLChangeRequest request = new MoMLChangeRequest(this, _entity,
174                    moml);
175            request.addChangeListener(this);
176            _entity.requestChange(request);
177        }
178    }
179
180    /** Clone the actor into the specified workspace.
181     *  @param workspace The workspace for the new object.
182     *  @return A new actor.
183     *  @exception CloneNotSupportedException If a derived class has an
184     *   attribute that cannot be cloned.
185     */
186    @Override
187    public Object clone(Workspace workspace) throws CloneNotSupportedException {
188        ModelDisplay newObject = (ModelDisplay) super.clone(workspace);
189        newObject._entity = _createBlankEntity();
190        return newObject;
191    }
192
193    /** If the model is not yet displayed, then display it in its
194     *  own window.
195     *  @exception IllegalActionException If there is an constructing
196     *   the display.
197     */
198    @Override
199    public void initialize() throws IllegalActionException {
200        super.initialize();
201
202        // If we have no entity at this point, then create a simple
203        // top-level entity into which we can put attributes.
204        if (_entity == null) {
205            _entity = _createBlankEntity();
206        }
207
208        // If there is no graph display yet, then create a
209        // standalone window in which to display the model.
210        if (_graph == null) {
211            Effigy containerEffigy = Configuration.findEffigy(toplevel());
212            try {
213                _effigy = new PtolemyEffigy(containerEffigy,
214                        "ModelDisplay Effigy");
215                _effigy.setModel(_entity);
216                _tableau = new Tableau(_effigy, "tableau");
217            } catch (NameDuplicationException e) {
218                throw new IllegalActionException(this, e,
219                        "Failed to create tableau.");
220            }
221            _frame = new TableauFrame(_tableau);
222            setFrame(_frame);
223            _tableau.setFrame(_frame);
224            place(_frame.getContentPane());
225            _frame.pack();
226        }
227        if (_frame != null) {
228            ((TableauFrame) _frame).show();
229            _frame.toFront();
230        }
231    }
232
233    /** Place the display in the specified container.
234     *  @param container The container, or null to remove it from any
235     *   existing container.
236     */
237    @Override
238    public void place(Container container) {
239        if (container == null) {
240            if (_frame != null) {
241                _frame.dispose();
242            }
243            _frame = null;
244            if (_tableau != null) {
245                try {
246                    _tableau.setContainer(null);
247                    _effigy.setContainer(null);
248                } catch (Exception e) {
249                    throw new InternalErrorException(e);
250                }
251            }
252            _tableau = null;
253            _graph = null;
254        } else {
255            ActorEditorGraphController controller = new ActorEditorGraphController();
256            // _entity might be null, in which case we have to make an empty model.
257            if (_entity == null) {
258                _entity = _createBlankEntity();
259            }
260
261            ActorGraphModel graphModel = new ActorGraphModel(_entity);
262            GraphPane graphPane = new GraphPane(controller, graphModel);
263            _graph = new JGraph(graphPane);
264
265            // If the model has a recorded size, use it.
266            List size = _entity.attributeList(SizeAttribute.class);
267            if (size.size() > 0) {
268                ((SizeAttribute) size.get(0)).setSize(_graph);
269            } else {
270                _graph.setMinimumSize(new Dimension(200, 200));
271                _graph.setMaximumSize(new Dimension(200, 200));
272                _graph.setPreferredSize(new Dimension(200, 200));
273                _graph.setSize(200, 200);
274            }
275            // _graph.setBackground(Color.white);
276
277            container.add(_graph);
278        }
279    }
280
281    ///////////////////////////////////////////////////////////////////
282    ////                         private methods                   ////
283
284    /** Create a blank entity associated with this display.
285     */
286    private static NamedObj _createBlankEntity() {
287        String moml = "<entity name=\"top\" class=\"ptolemy.kernel.CompositeEntity\"/>";
288        NamedObj entity = null;
289        MoMLParser parser = new MoMLParser();
290        try {
291            entity = parser.parse(null, moml);
292            ParserAttribute parserAttribute = new ParserAttribute(entity,
293                    "_parser");
294            parserAttribute.setParser(parser);
295        } catch (Exception ex) {
296            throw new InternalErrorException(ex);
297        }
298        return entity;
299    }
300
301    ///////////////////////////////////////////////////////////////////
302    ////                         private variables                 ////
303
304    /** The effigy representing the model. */
305    private PtolemyEffigy _effigy;
306
307    /** The top-level entity read from the file or URL. */
308    private NamedObj _entity;
309
310    /** The graph display pane. */
311    private JGraph _graph;
312
313    /** The tableau, if the model is displayed in its own window. */
314    private Tableau _tableau;
315}