001/* A graph editor frame 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.actor;
029
030import java.awt.Color;
031import java.awt.Frame;
032import java.awt.event.ActionEvent;
033import java.awt.event.ActionListener;
034import java.awt.event.KeyEvent;
035import java.awt.geom.Rectangle2D;
036import java.io.File;
037import java.util.Iterator;
038import java.util.List;
039
040import javax.swing.AbstractAction;
041import javax.swing.Action;
042import javax.swing.JFileChooser;
043import javax.swing.JMenu;
044import javax.swing.JMenuItem;
045import javax.swing.KeyStroke;
046
047import diva.graph.GraphController;
048import diva.graph.GraphPane;
049import diva.gui.GUIUtilities;
050import ptolemy.actor.Actor;
051import ptolemy.actor.CompositeActor;
052import ptolemy.actor.Director;
053import ptolemy.actor.Manager;
054import ptolemy.actor.gui.Configuration;
055import ptolemy.actor.gui.DebugListenerTableau;
056import ptolemy.actor.gui.Effigy;
057import ptolemy.actor.gui.EffigyFactory;
058import ptolemy.actor.gui.ModelDirectory;
059import ptolemy.actor.gui.PtolemyEffigy;
060import ptolemy.actor.gui.Tableau;
061import ptolemy.actor.gui.TextEffigy;
062import ptolemy.actor.gui.UserActorLibrary;
063import ptolemy.gui.ComponentDialog;
064import ptolemy.gui.JFileChooserBugFix;
065import ptolemy.gui.PtFileChooser;
066import ptolemy.gui.Query;
067import ptolemy.kernel.CompositeEntity;
068import ptolemy.kernel.Entity;
069import ptolemy.kernel.util.KernelException;
070import ptolemy.kernel.util.KernelRuntimeException;
071import ptolemy.kernel.util.NamedObj;
072import ptolemy.moml.LibraryAttribute;
073import ptolemy.moml.MoMLChangeRequest;
074import ptolemy.util.CancelException;
075import ptolemy.util.MessageHandler;
076import ptolemy.vergil.basic.AbstractBasicGraphModel;
077import ptolemy.vergil.basic.BasicGraphPane;
078import ptolemy.vergil.basic.ExtendedGraphFrame;
079
080///////////////////////////////////////////////////////////////////
081//// ActorGraphFrame
082
083/**
084 * This is a graph editor frame for ptolemy models. Given a composite entity and
085 * an instance of ActorGraphTableau, it creates an editor and populates the
086 * menus and toolbar. This overrides the base class to associate with the editor
087 * an instance of ActorEditorGraphController.
088 *
089 * @see ActorEditorGraphController
090 * @author Steve Neuendorffer, Contributor: Edward A. Lee, KIELER Layout: Christian Motika <cmot@informatik.uni-kiel.de>
091 * @version $Id$
092 * @since Ptolemy II 2.0
093 * @Pt.ProposedRating Red (neuendor)
094 * @Pt.AcceptedRating Red (johnr)
095 */
096@SuppressWarnings("serial")
097public class ActorGraphFrame extends ExtendedGraphFrame
098/*implements ActionListener*/ {
099    /**
100     * Construct a frame associated with the specified Ptolemy II model. After
101     * constructing this, it is necessary to call setVisible(true) to make the
102     * frame appear. This is typically done by calling show() on the controlling
103     * tableau. This constructor results in a graph frame that obtains its
104     * library either from the model (if it has one) or the default library
105     * defined in the configuration.
106     *
107     * @see Tableau#show()
108     * @param entity The model to put in this frame.
109     * @param tableau The tableau responsible for this frame.
110     */
111    public ActorGraphFrame(CompositeEntity entity, Tableau tableau) {
112        this(entity, tableau, null);
113    }
114
115    /**
116     * Construct a frame associated with the specified Ptolemy II model. After
117     * constructing this, it is necessary to call setVisible(true) to make the
118     * frame appear. This is typically done by calling show() on the controlling
119     * tableau. This constructor results in a graph frame that obtains its
120     * library either from the model (if it has one), or the
121     * <i>defaultLibrary</i> argument (if it is non-null), or the default
122     * library defined in the configuration.
123     *
124     * @see Tableau#show()
125     * @param entity The model to put in this frame.
126     * @param tableau The tableau responsible for this frame.
127     * @param defaultLibrary An attribute specifying the default library to use
128     *            if the model does not have a library.
129     */
130    public ActorGraphFrame(CompositeEntity entity, Tableau tableau,
131            LibraryAttribute defaultLibrary) {
132        super(entity, tableau, defaultLibrary);
133        _initActorGraphFrame();
134    }
135
136    //    /**
137    //     * React to the actions specific to this actor graph frame.
138    //     *
139    //     * @param e The action event.
140    //     */
141    //    public void actionPerformed(ActionEvent e) {
142    //        JMenuItem target = (JMenuItem) e.getSource();
143    //        String actionCommand = target.getActionCommand();
144    //        if (actionCommand.equals(_IMPORT_DESIGN_PATTERN_LABEL)) {
145    //            importDesignPattern();
146    //        } else if (actionCommand.equals(_EXPORT_DESIGN_PATTERN_LABEL)) {
147    //            exportDesignPattern();
148    //        } else if (actionCommand.equals(_IMPORT_LIBRARY_LABEL)) {
149    //                _importLibraryAction.actionPerformed(e);
150    //        } else if (actionCommand.equals(_EXPORT_LIBRARY_LABEL)) {
151    //                _saveInLibraryAction.actionPerformed(e);
152    //        }
153    //    }
154
155    /** Dispose of this frame.
156     *     Override this dispose() method to unattach any listeners that may keep
157     *  this model from getting garbage collected.  This method invokes the
158     *  dispose() method of the superclass,
159     *  {@link ptolemy.vergil.basic.ExtendedGraphFrame}.
160     */
161    @Override
162    public void dispose() {
163        if (_debugClosing) {
164            System.out.println("ActorGraphFrame.dispose() : " + this.getName());
165        }
166
167        if (_rightComponent != null) {
168            // A bug with Graph -> Save In Library resulted in creating a
169            // Composite that would fail to open because dispose would throw
170            // a NPE because _rightComponent was null.
171            KeyStroke[] keyStroke = _rightComponent.getRegisteredKeyStrokes();
172            int count = keyStroke.length;
173            for (int i = 0; i < count; i++) {
174                KeyStroke ks = keyStroke[i];
175                _rightComponent.unregisterKeyboardAction(ks);
176            }
177        }
178        _exportDesignPatternAction = null;
179        _saveInLibraryAction = null;
180        _importDesignPatternAction = null;
181        _importLibraryAction = null;
182        _instantiateAttributeAction = null;
183        _instantiateEntityAction = null;
184        _instantiatePortAction = null;
185        _createHierarchyAction = null;
186        _debugMenuListener = null;
187
188        // These might not be necessary, but they probably can't hurt.
189        if (_controller != null) {
190            _controller.setFrame(null);
191            _controller.setConfiguration(null);
192            _controller = null;
193        }
194
195        super.dispose();
196    }
197
198    /** Import a library by first opening a file chooser dialog and
199     *  then importing the specified library.  See {@link
200     *  ptolemy.actor.gui.UserActorLibrary#openLibrary(Configuration,
201     *  File)} for information on the file format.  This method opens
202     *  up a new blank graph viewer so that the new library is
203     *  visible.
204     *  @param lastDirectory The last directory opened, usually the
205     *  value of getDirectory().
206     *  @param frame The frame of the owner of the file chooser.
207     *  @param configuration  The Ptolemy configuration.
208     *  @return the last directory opened.
209     */
210    public static File importLibrary(File lastDirectory, Frame frame,
211            Configuration configuration) {
212        // This method is static so that other frames such as OntologySolverGraphFrame
213        // can use it.
214        JFileChooserBugFix jFileChooserBugFix = new JFileChooserBugFix();
215        Color background = null;
216        PtFileChooser ptFileChooser = null;
217        try {
218            background = jFileChooserBugFix.saveBackground();
219            ptFileChooser = new PtFileChooser(frame,
220                    "Select a library to import", JFileChooser.OPEN_DIALOG);
221
222            ptFileChooser.setCurrentDirectory(lastDirectory);
223
224            int result = ptFileChooser.showDialog(frame, "Open");
225
226            if (result == JFileChooser.APPROVE_OPTION) {
227                try {
228                    File file = ptFileChooser.getSelectedFile()
229                            .getCanonicalFile();
230                    //PtolemyEffigy effigy = (PtolemyEffigy) getTableau()
231                    //    .getContainer();
232                    //Configuration configuration = (Configuration) effigy
233                    //    .toplevel();
234                    UserActorLibrary.openLibrary(configuration, file);
235                    lastDirectory = ptFileChooser.getCurrentDirectory();
236                } catch (Throwable throwable) {
237                    MessageHandler.error("Library import failed.", throwable);
238                }
239            }
240        } finally {
241            jFileChooserBugFix.restoreBackground(background);
242        }
243        try {
244            // FIXME: A bug prevents the left hand actor tree from updating.
245            // vergil.tree.VisibleTreeModel has valueForPathChanged()
246            // defined as an empty method, which could be the cause.
247
248            // So, we get the effigyFactory from the configuration, find
249            // the ActorGraphTableau Factory and create a primary Tableau.
250
251            // FIXME: It might be possible to just instantiate an ActorGraphTableau Factory.
252
253            // Code similar to TableauFrame._addMenus()
254            //final Cnfiguration configuration = getConfiguration();
255            EffigyFactory effigyFactory = (EffigyFactory) configuration
256                    .getEntity("effigyFactory");
257            List factoryList = effigyFactory.entityList(EffigyFactory.class);
258            Iterator factories = factoryList.iterator();
259            Effigy effigy = null;
260
261            // Loop through the factories until createEffigy() returns a non-null
262            // Effigy.  See EffigyFactory.createEffigy().
263
264            while (factories.hasNext() && effigy == null) {
265                final EffigyFactory factory = (EffigyFactory) factories.next();
266                if (factory instanceof ptolemy.actor.gui.PtolemyEffigy.Factory) {
267                    final ModelDirectory directory = configuration
268                            .getDirectory();
269                    effigy = factory.createEffigy(directory);
270                    configuration.createPrimaryTableau(effigy);
271                }
272            }
273        } catch (Throwable throwable) {
274            MessageHandler.error("Failed to open model.", throwable);
275        }
276        return lastDirectory;
277    }
278
279    ///////////////////////////////////////////////////////////////////
280    ////                         protected methods                 ////
281
282    /**
283     * Initialize this class. In this base class, the help file is set, and
284     * various actions are instantiated.
285     */
286    protected void _initActorGraphFrame() {
287
288        // Override the default help file.
289        helpFile = "ptolemy/configs/doc/vergilGraphEditorHelp.htm";
290
291        _createHierarchyAction = new CreateHierarchyAction();
292        // Only include the various actions if there is an actor library
293        // The ptinyViewer configuration uses this.
294        if (getConfiguration().getEntity("actor library") != null) {
295            //_saveInLibraryAction = new SaveInLibraryAction();
296            //_importLibraryAction = new ImportLibraryAction();
297            _instantiateAttributeAction = new InstantiateAttributeAction(this,
298                    "ptolemy.vergil.kernel.attributes.EllipseAttribute");
299            _instantiateEntityAction = new InstantiateEntityAction(this,
300                    "ptolemy.actor.lib.Ramp");
301            _instantiatePortAction = new InstantiatePortAction();
302        }
303
304    }
305
306    /**
307     * Create the menus that are used by this frame. It is essential that
308     * _createGraphPane() be called before this.
309     */
310    @Override
311    protected void _addMenus() {
312        super._addMenus();
313
314        _graphMenu.addSeparator();
315        _addLayoutMenu(_graphMenu);
316        _addReloadAccessorsMenu(_graphMenu);
317
318        // Only include the various actions if there is an actor library
319        // The ptinyViewer configuration uses this.
320        if (getConfiguration().getEntity("actor library") != null) {
321            GUIUtilities.addHotKey(_getRightComponent(), _saveInLibraryAction);
322            GUIUtilities.addHotKey(_getRightComponent(), _importLibraryAction);
323            GUIUtilities.addMenuItem(_graphMenu, _instantiateAttributeAction);
324            GUIUtilities.addHotKey(_getRightComponent(),
325                    _instantiateAttributeAction);
326            GUIUtilities.addMenuItem(_graphMenu, _instantiateEntityAction);
327            GUIUtilities.addMenuItem(_graphMenu, _instantiatePortAction);
328            GUIUtilities.addHotKey(_getRightComponent(),
329                    _instantiateEntityAction);
330            _graphMenu.addSeparator();
331            diva.gui.GUIUtilities.addHotKey(_getRightComponent(),
332                    _createHierarchyAction);
333            diva.gui.GUIUtilities.addMenuItem(_graphMenu,
334                    _createHierarchyAction);
335        }
336        // Add any commands to graph menu and toolbar that the controller
337        // wants in the graph menu and toolbar.
338        _graphMenu.addSeparator();
339        _controller.addToMenuAndToolbar(_graphMenu, _toolbar);
340
341        // Add debug menu.
342        // Generate .smv file newly added by Patrick
343        JMenuItem[] debugMenuItems = {
344                new JMenuItem("Listen to Director", KeyEvent.VK_L),
345                new JMenuItem("Animate Execution", KeyEvent.VK_A),
346                new JMenuItem("Stop Animating", KeyEvent.VK_S), };
347
348        // NOTE: This has to be initialized here rather than
349        // statically because this method is called by the constructor
350        // of the base class, and static initializers have not yet
351        // been run.
352        _debugMenu = new JMenu("Debug");
353        _debugMenu.setMnemonic(KeyEvent.VK_D);
354
355        _debugMenuListener = new DebugMenuListener();
356
357        // Set the action command and listener for each menu item.
358        for (JMenuItem debugMenuItem : debugMenuItems) {
359            debugMenuItem.setActionCommand(debugMenuItem.getText());
360            debugMenuItem.addActionListener(_debugMenuListener);
361            _debugMenu.add(debugMenuItem);
362        }
363
364        _menubar.add(_debugMenu);
365    }
366
367    /**
368     * If the ptolemy model associated with this frame is a top-level composite
369     * actor, use its manager to stop it. Remove the listeners that this frame
370     * registered with the ptolemy model. Also remove the listeners our graph
371     * model has created.
372     *
373     * @return True if the close completes, and false otherwise.
374     */
375    @Override
376    protected boolean _close() {
377        if (_debugClosing) {
378            System.out.println("ActorGraphFrame._close() : " + this.getName());
379        }
380
381        NamedObj ptModel = getModel();
382
383        if (ptModel instanceof CompositeActor
384                && ptModel.getContainer() == null) {
385            CompositeActor ptActorModel = (CompositeActor) ptModel;
386            Manager manager = ptActorModel.getManager();
387
388            if (manager != null) {
389                manager.stop();
390            }
391        }
392
393        return super._close();
394    }
395
396    /**
397     * Create the items in the File menu. A null element in the array represents
398     * a separator in the menu.
399     *
400     * @return The items in the File menu.
401     */
402    @Override
403    protected JMenuItem[] _createFileMenuItems() {
404        // NOTE: Cannot use getConfiguration() because the configuration is
405        // not set when this method is called when instantiating Top. Hence, we assume that there
406        // is only one configuration, or that if there are multiple configurations
407        // in this execution, that the first one will determine whether PDF
408        // export is provided.
409        Configuration configuration = Configuration.configurations().get(0);
410        JMenuItem[] fileMenuItems = super._createFileMenuItems();
411        //int i = 0;
412        for (JMenuItem item : fileMenuItems) {
413            //i++;
414            // Only include the various actions if there is an actor library
415            // The ptinyViewer configuration uses this.
416            if (configuration != null
417                    && configuration.getEntity("actor library") != null
418                    && item != null) {
419                if (item.getActionCommand().equals("Import")) {
420
421                    /////////////////////////////////////////////////
422                    // IMPORTANT: Do not add any import actions to
423                    // this class that require 3rd-party libraries.
424                    // Instead, edit the defaultFullConfiguration.xml
425                    // and update _importActionClassNames.  See
426                    // ptolemy.vergil.basic.import.accessor.ImportAccessorAction
427
428                    // The reason to *not* add imports that use third-party libraries is because
429                    // various configurations such as Ptiny, Kepler and BCVTB use this class and
430                    // we don't want to clutter Ptiny with third-party libraries.
431                    /////////////////////////////////////////////////
432
433                    _importDesignPatternAction = new ImportDesignPatternAction();
434                    JMenuItem importItem = new JMenuItem(
435                            _importDesignPatternAction);
436                    item.add(importItem);
437                    _importLibraryAction = new ImportLibraryAction();
438                    JMenuItem importLibraryItem = new JMenuItem(
439                            _importLibraryAction);
440                    item.add(importLibraryItem);
441
442                } else if (item.getActionCommand().equals("Export")) {
443
444                    /////////////////////////////////////////////////
445                    // IMPORTANT: Do not add any export actions to this class
446                    // that require 3rd-party libraries.
447                    // Instead, edit defaultConfiguration.xml.
448
449                    // The reason to *not* add exports that use third-party libraries is because
450                    // various configurations such as Ptiny, Kepler and BCVTB use this class and
451                    // we don't want to clutter Ptiny with third-party libraries.
452                    /////////////////////////////////////////////////
453
454                    _exportDesignPatternAction = new ExportDesignPatternAction();
455                    JMenuItem exportItem = new JMenuItem(
456                            _exportDesignPatternAction);
457                    item.add(exportItem);
458                    _saveInLibraryAction = new SaveInLibraryAction();
459                    JMenuItem exportLibraryItem = new JMenuItem(
460                            _saveInLibraryAction);
461                    item.add(exportLibraryItem);
462                }
463                // The code above inserts menus items in the the File->Export and
464                // File -> Import menu items.
465                // Here's how to insert a menu item into the File main menu.
466                //                JMenuItem[] newItems = new JMenuItem[fileMenuItems.length + 4];
467                //                System.arraycopy(fileMenuItems, 0, newItems, 0, i);
468                //                newItems[i + 1] = importItem;
469                //                importItem.addActionListener(this);
470                //                newItems[i + 2] = exportItem;
471                //                exportItem.addActionListener(this);
472                //                System.arraycopy(fileMenuItems, i, newItems, i + 4,
473                //                        fileMenuItems.length - i);
474                //                return newItems;
475            }
476        }
477        return fileMenuItems;
478    }
479
480    /**
481     * Create a new graph pane. Note that this method is called in constructor
482     * of the base class, so it must be careful to not reference local variables
483     * that may not have yet been created.
484     *
485     * @param entity The object to be displayed in the pane.
486     * @return The pane that is created.
487     */
488    @Override
489    protected GraphPane _createGraphPane(NamedObj entity) {
490        _controller = new ActorEditorGraphController();
491        _controller.setConfiguration(getConfiguration());
492        _controller.setFrame(this);
493
494        // The cast is safe because the constructor only accepts
495        // CompositeEntity.
496        final ActorGraphModel graphModel = new ActorGraphModel(entity);
497        return new BasicGraphPane(_controller, graphModel, entity);
498    }
499
500    ///////////////////////////////////////////////////////////////////
501    ///                    protected variables                     ////
502
503    /** The graph controller. This is created in _createGraphPane(). */
504    protected ActorEditorGraphController _controller;
505
506    /** Debug menu for this frame. */
507    protected JMenu _debugMenu;
508
509    /** The action for creating a level of hierarchy. */
510    protected Action _createHierarchyAction;
511
512    /** The action for exporting a design pattern. */
513    protected Action _exportDesignPatternAction;
514
515    /** The action for saving the current model in a library. */
516    protected Action _saveInLibraryAction;
517
518    /** The action for importing a design pattern. */
519    protected Action _importDesignPatternAction;
520
521    /** The action for importing a library of components. */
522    protected Action _importLibraryAction;
523
524    /** The action for instantiating an attribute. */
525    protected Action _instantiateAttributeAction;
526
527    /** The action for instantiating an entity. */
528    protected Action _instantiateEntityAction;
529
530    /** The action for instantiating a port. */
531    protected Action _instantiatePortAction;
532
533    /** Listener for debug menu commands. */
534    protected DebugMenuListener _debugMenuListener;
535
536    ///////////////////////////////////////////////////////////////////
537    ////                      public inner classes                 ////
538
539    // NOTE: The following class is very similar to the inner class
540    // in FSMGraphFrame. Is there some way to merge these?
541    // There seem to be enough differences that this may be hard.
542
543    /** Listener for debug menu commands. */
544    public class DebugMenuListener implements ActionListener {
545        /** React to a menu command. */
546        @Override
547        public void actionPerformed(ActionEvent e) {
548            JMenuItem target = (JMenuItem) e.getSource();
549            String actionCommand = target.getActionCommand();
550
551            try {
552                if (actionCommand.equals("Listen to Director")) {
553                    NamedObj model = getModel();
554                    boolean success = false;
555
556                    if (model instanceof Actor) {
557                        Director director = ((Actor) model).getDirector();
558
559                        if (director != null) {
560                            Effigy effigy = (Effigy) getTableau()
561                                    .getContainer();
562
563                            // Create a new text effigy inside this one.
564                            Effigy textEffigy = new TextEffigy(effigy,
565                                    effigy.uniqueName("debug listener"));
566                            DebugListenerTableau tableau = new DebugListenerTableau(
567                                    textEffigy,
568                                    textEffigy.uniqueName("debugListener"));
569                            tableau.setDebuggable(director);
570                            success = true;
571                        }
572                    }
573
574                    if (!success) {
575                        MessageHandler.error("No director to listen to!");
576                    }
577                } else if (actionCommand.equals("Animate Execution")) {
578                    // To support animation, add a listener to the
579                    // first director found above in the hierarchy.
580                    // NOTE: This doesn't properly support all
581                    // hierarchy. Insides of transparent composite
582                    // actors do not get animated if they are classes
583                    // rather than instances.
584                    NamedObj model = getModel();
585
586                    if (model instanceof Actor) {
587                        // Dialog to ask for a delay time.
588                        Query query = new Query();
589                        query.addLine("delay", "Time (in ms) to hold highlight",
590                                Long.toString(_lastDelayTime));
591
592                        ComponentDialog dialog = new ComponentDialog(
593                                ActorGraphFrame.this, "Delay for Animation",
594                                query);
595
596                        if (dialog.buttonPressed().equals("OK")) {
597                            try {
598                                _lastDelayTime = Long.parseLong(
599                                        query.getStringValue("delay"));
600                                _controller.setAnimationDelay(_lastDelayTime);
601
602                                Director director = ((Actor) model)
603                                        .getDirector();
604
605                                while (director == null
606                                        && model instanceof Actor) {
607                                    model = model.getContainer();
608
609                                    if (model instanceof Actor) {
610                                        director = ((Actor) model)
611                                                .getDirector();
612                                    }
613                                }
614
615                                if (director != null
616                                        && _listeningTo != director) {
617                                    if (_listeningTo != null) {
618                                        _listeningTo.removeDebugListener(
619                                                _controller);
620                                    }
621
622                                    director.addDebugListener(_controller);
623                                    _listeningTo = director;
624                                } else {
625                                    MessageHandler
626                                            .error("Cannot find the director. "
627                                                    + "Possibly this is because this "
628                                                    + "is a class, not an instance.");
629                                }
630
631                            } catch (NumberFormatException ex) {
632                                MessageHandler.error(
633                                        "Invalid time, which is required "
634                                                + "to be an integer",
635                                        ex);
636                            }
637                        }
638                    } else {
639                        MessageHandler.error(
640                                "Model is not an actor. Cannot animate.");
641                    }
642                } else if (actionCommand.equals("Stop Animating")) {
643                    if (_listeningTo != null) {
644                        _listeningTo.removeDebugListener(_controller);
645                        _controller.clearAnimation();
646                        _listeningTo = null;
647                    }
648                }
649            } catch (KernelException ex) {
650                try {
651                    MessageHandler
652                            .warning("Failed to create debug listener: " + ex);
653                } catch (CancelException exception) {
654                }
655            }
656        }
657
658        /** The delay time specified that last time animation was set. */
659        private long _lastDelayTime = 0;
660
661        private Director _listeningTo;
662    }
663
664    ///////////////////////////////////////////////////////////////////
665    ////                  public static inner classes              ////
666
667    ///////////////////////////////////////////////////////////////////
668    //// InstantiateAttributeAction
669
670    /** An action to instantiate an attribute given a class name. */
671    public static class InstantiateAttributeAction extends AbstractAction {
672
673        /** Create a new action to instantiate an attribute.
674         *  @param graphFrame The graph frame that will contain this attribute.
675         *  @param initialLastAttributeClassName The initial value of the class
676         *   name string in the instantiate attribute dialog.
677         */
678        public InstantiateAttributeAction(ExtendedGraphFrame graphFrame,
679                String initialLastAttributeClassName) {
680            super("Instantiate Attribute");
681            _graphFrame = graphFrame;
682            _lastAttributeClassName = initialLastAttributeClassName;
683            putValue("tooltip", "Instantiate an attribute by class name");
684            putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_A));
685        }
686
687        /**
688         * Instantiate a class by first opening a dialog to get a class name and
689         * then issuing a change request.
690         *
691         * @param e The event that initiates the action.
692         */
693        @Override
694        public void actionPerformed(ActionEvent e) {
695            Query query = new Query();
696            query.setTextWidth(60);
697            query.addLine("class", "Class name", _lastAttributeClassName);
698
699            ComponentDialog dialog = new ComponentDialog(_graphFrame,
700                    "Instantiate Attribute", query);
701
702            if (dialog.buttonPressed().equals("OK")) {
703                // Get the associated Ptolemy model.
704                GraphController controller = _graphFrame.getJGraph()
705                        .getGraphPane().getGraphController();
706                AbstractBasicGraphModel model = (AbstractBasicGraphModel) controller
707                        .getGraphModel();
708                NamedObj context = model.getPtolemyModel();
709
710                _lastAttributeClassName = query.getStringValue("class");
711
712                // Find the root for the instance name.
713                String rootName = _lastAttributeClassName;
714                int period = rootName.lastIndexOf(".");
715
716                if (period >= 0 && rootName.length() > period + 1) {
717                    rootName = rootName.substring(period + 1);
718                }
719
720                // Use the center of the screen as a location.
721                Rectangle2D bounds = _graphFrame.getVisibleCanvasRectangle();
722                double x = bounds.getWidth() / 2.0;
723                double y = bounds.getHeight() / 2.0;
724
725                // Use the "auto" namespace group so that name collisions
726                // are automatically avoided by appending a suffix to the name.
727                String moml = "<group name=\"auto\"><property name=\""
728                        + rootName + "\" class=\"" + _lastAttributeClassName
729                        + "\"><property name=\"_location\" "
730                        + "class=\"ptolemy.kernel.util.Location\" value=\"" + x
731                        + ", " + y + "\"></property></property></group>";
732                MoMLChangeRequest request = new MoMLChangeRequest(this, context,
733                        moml);
734                context.requestChange(request);
735            }
736        }
737
738        /** The graph frame that contains this action. */
739        private ExtendedGraphFrame _graphFrame;
740
741        /** The most recent class name for instantiating an attribute. */
742        private String _lastAttributeClassName;
743    }
744
745    ///////////////////////////////////////////////////////////////////
746    //// InstantiateEntityAction
747
748    /** An action to instantiate an entity given a class name. */
749    public static class InstantiateEntityAction extends AbstractAction {
750        /** Create a new action to instantiate an entity.
751         *  @param graphFrame The graph frame that will contain this attribute.
752         *  @param initialLastEntityClassName The initial value of the class
753         *   name string in the instantiate entity dialog.
754         */
755        public InstantiateEntityAction(ExtendedGraphFrame graphFrame,
756                String initialLastEntityClassName) {
757            super("Instantiate Entity");
758            _graphFrame = graphFrame;
759            _lastEntityClassName = initialLastEntityClassName;
760            putValue("tooltip", "Instantiate an entity by class name");
761            putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_E));
762        }
763
764        /**
765         * Instantiate a class by first opening a dialog to get a class name and
766         * then issuing a change request.
767         *
768         * @param e The event that initiates the action.
769         */
770        @Override
771        public void actionPerformed(ActionEvent e) {
772            Query query = new Query();
773            query.setTextWidth(60);
774            query.addLine("class", "Class name", _lastEntityClassName);
775            query.addLine("location", "Location (URL)", _lastLocation);
776
777            ComponentDialog dialog = new ComponentDialog(_graphFrame,
778                    "Instantiate Entity", query);
779
780            if (dialog.buttonPressed().equals("OK")) {
781                // Get the associated Ptolemy model.
782                GraphController controller = _graphFrame.getJGraph()
783                        .getGraphPane().getGraphController();
784                AbstractBasicGraphModel model = (AbstractBasicGraphModel) controller
785                        .getGraphModel();
786                NamedObj context = model.getPtolemyModel();
787
788                _lastEntityClassName = query.getStringValue("class");
789                _lastLocation = query.getStringValue("location");
790
791                // Find the root for the instance name.
792                String rootName = _lastEntityClassName;
793                int period = rootName.lastIndexOf(".");
794
795                if (period >= 0 && rootName.length() > period + 1) {
796                    rootName = rootName.substring(period + 1);
797                }
798
799                // Use the center of the screen as a location.
800                Rectangle2D bounds = _graphFrame.getVisibleCanvasRectangle();
801                double x = bounds.getWidth() / 2.0;
802                double y = bounds.getHeight() / 2.0;
803
804                // If a location is given, construct MoML to
805                // specify a "source".
806                String source = "";
807
808                if (!_lastLocation.trim().equals("")) {
809                    source = " source=\"" + _lastLocation.trim() + "\"";
810                }
811
812                // Use the "auto" namespace group so that name collisions
813                // are automatically avoided by appending a suffix to the name.
814                String moml = "<group name=\"auto\"><entity name=\"" + rootName
815                        + "\" class=\"" + _lastEntityClassName + "\"" + source
816                        + "><property name=\"_location\" "
817                        + "class=\"ptolemy.kernel.util.Location\" value=\"" + x
818                        + ", " + y + "\"></property></entity></group>";
819                MoMLChangeRequest request = new MoMLChangeRequest(this, context,
820                        moml);
821                context.requestChange(request);
822            }
823        }
824
825        /** The graph frame that contains this action. */
826        private ExtendedGraphFrame _graphFrame;
827
828        /** The most recent class name for instantiating an entity. */
829        private String _lastEntityClassName;
830
831        /** The most recent location for instantiating a class. */
832        private String _lastLocation = "";
833    }
834
835    ///////////////////////////////////////////////////////////////////
836    ////                  private inner classes                     ////
837
838    ///////////////////////////////////////////////////////////////////
839    //// CreateHierarchy
840
841    /**
842     * Action to create a typed composite actor that contains the the selected
843     * actors.
844     */
845    private class CreateHierarchyAction extends AbstractAction {
846        /**
847         * Create a new action to introduce a level of hierarchy.
848         */
849        public CreateHierarchyAction() {
850            super("Create Hierarchy");
851            putValue("tooltip", "Create a TypedCompositeActor that contains the"
852                    + " selected actors.");
853
854            // putValue(diva.gui.GUIUtilities.ACCELERATOR_KEY,
855            // KeyStroke.getKeyStroke(KeyEvent.VK_H,
856            // Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
857            // putValue(diva.gui.GUIUtilities.MNEMONIC_KEY,
858            // Integer.valueOf(KeyEvent.VK_H));
859        }
860
861        @Override
862        public void actionPerformed(ActionEvent e) {
863            createHierarchy();
864        }
865    }
866
867    ///////////////////////////////////////////////////////////////////
868    //// ExportDesignPatternAction
869
870    /** An action to export a design pattern.
871     */
872    private class ExportDesignPatternAction extends AbstractAction {
873        /** Create a new action to export a design pattern. */
874        public ExportDesignPatternAction() {
875            super(_EXPORT_DESIGN_PATTERN_LABEL);
876            putValue("tooltip", "Export a design pattern into the Palette");
877            putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_D));
878        }
879
880        /**
881         * Export a design pattern by first opening a file chooser dialog and then
882         * exporting the specified library.
883         */
884        @Override
885        public void actionPerformed(ActionEvent e) {
886            exportDesignPattern();
887        }
888    }
889
890    ///////////////////////////////////////////////////////////////////
891    //// ImportDesignPatternAction
892
893    /** An action to import a design pattern.
894     */
895    private class ImportDesignPatternAction extends AbstractAction {
896        /** Create a new action to import a design pattern. */
897        public ImportDesignPatternAction() {
898            super(_IMPORT_DESIGN_PATTERN_LABEL);
899            putValue("tooltip", "Import a design pattern into the Palette");
900            putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_D));
901        }
902
903        /**
904         * Import a design pattern by first opening a file chooser dialog and then
905         * importing the specified design pattern.
906         */
907        @Override
908        public void actionPerformed(ActionEvent e) {
909            importDesignPattern();
910        }
911    }
912
913    ///////////////////////////////////////////////////////////////////
914    //// ImportLibraryAction
915
916    /** An action to import a library of components.
917     */
918    private class ImportLibraryAction extends AbstractAction {
919        /** Create a new action to import a library of components. */
920        public ImportLibraryAction() {
921            super("Import Library");
922            putValue("tooltip", "Import a library into the Palette");
923            putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_M));
924        }
925
926        /**
927         * Import a library by first opening a file chooser dialog and then
928         * importing the specified library.
929         * See {@link ptolemy.actor.gui.UserActorLibrary#openLibrary(Configuration, File)}
930         * for information on the file format.
931         */
932        @Override
933        public void actionPerformed(ActionEvent e) {
934            setLastDirectory(ActorGraphFrame.importLibrary(getLastDirectory(),
935                    ActorGraphFrame.this, getConfiguration()));
936        }
937    }
938
939    ///////////////////////////////////////////////////////////////////
940    //// InstantiatePortAction
941
942    /** An action to instantiate a port given a class name. */
943    private class InstantiatePortAction extends AbstractAction {
944        /** Create a new action to instantiate a port. */
945        public InstantiatePortAction() {
946            super("Instantiate Port");
947            putValue("tooltip", "Instantiate a port by class name");
948            putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
949        }
950
951        /**
952         * Instantiate a class by first opening a dialog to get a class name and
953         * then issuing a change request.
954         */
955        @Override
956        public void actionPerformed(ActionEvent e) {
957            Query query = new Query();
958            query.setTextWidth(60);
959            query.addLine("class", "Class name", _lastPortClassName);
960            query.addLine("location", "Location (URL)", _lastLocation);
961
962            ComponentDialog dialog = new ComponentDialog(ActorGraphFrame.this,
963                    "Instantiate Port", query);
964
965            if (dialog.buttonPressed().equals("OK")) {
966                // Get the associated Ptolemy model.
967                GraphController controller = getJGraph().getGraphPane()
968                        .getGraphController();
969                AbstractBasicGraphModel model = (AbstractBasicGraphModel) controller
970                        .getGraphModel();
971                NamedObj context = model.getPtolemyModel();
972
973                _lastPortClassName = query.getStringValue("class");
974                _lastLocation = query.getStringValue("location");
975
976                // Find the root for the instance name.
977                String rootName = _lastPortClassName;
978                int period = rootName.lastIndexOf(".");
979
980                if (period >= 0 && rootName.length() > period + 1) {
981                    rootName = rootName.substring(period + 1);
982                }
983
984                // Use the center of the screen as a location.
985                Rectangle2D bounds = getVisibleCanvasRectangle();
986                double x = bounds.getWidth() / 2.0;
987                double y = bounds.getHeight() / 2.0;
988
989                // If a location is given, construct MoML to
990                // specify a "source".
991                String source = "";
992
993                if (!_lastLocation.trim().equals("")) {
994                    source = " source=\"" + _lastLocation.trim() + "\"";
995                }
996
997                // Use the "auto" namespace group so that name collisions
998                // are automatically avoided by appending a suffix to the name.
999                String moml = "<group name=\"auto\"><port name=\"" + rootName
1000                        + "\" class=\"" + _lastPortClassName + "\"" + source
1001                        + "><property name=\"_location\" "
1002                        + "class=\"ptolemy.kernel.util.Location\" value=\"" + x
1003                        + ", " + y + "\"></property></port></group>";
1004
1005                MoMLChangeRequest request = new MoMLChangeRequest(this, context,
1006                        moml);
1007                context.requestChange(request);
1008            }
1009        }
1010
1011        /** The most recent location for instantiating a class. */
1012        private String _lastLocation = "";
1013
1014        /** The most recent class name for instantiating a port. */
1015        private String _lastPortClassName = "ptolemy.actor.TypedIOPort";
1016    }
1017
1018    ///////////////////////////////////////////////////////////////////
1019    //// SaveInLibraryAction
1020
1021    /** An action to save the current model in a library. */
1022    private class SaveInLibraryAction extends AbstractAction {
1023        /** Create a new action to save a model in a library. */
1024        public SaveInLibraryAction() {
1025            super("Save In Library");
1026            putValue("tooltip", "Save as a Component in Library");
1027            putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_S));
1028        }
1029
1030        /**
1031         * Create a new instance of the current model in the actor library of
1032         * the configuration.
1033         */
1034        @Override
1035        public void actionPerformed(ActionEvent e) {
1036            PtolemyEffigy effigy = (PtolemyEffigy) getTableau().getContainer();
1037            NamedObj object = effigy.getModel();
1038
1039            if (object == null) {
1040                return;
1041            }
1042
1043            if (!(object instanceof Entity)) {
1044                throw new KernelRuntimeException("Could not save in "
1045                        + "library, '" + object + "' is not an Entity");
1046            }
1047
1048            Entity entity = (Entity) object;
1049            Configuration configuration = (Configuration) effigy.toplevel();
1050            try {
1051                UserActorLibrary.saveComponentInLibrary(configuration, entity);
1052            } catch (Exception ex) {
1053                // We catch exceptions here because this method used to
1054                // not throw Exceptions, and we don't want to break
1055                // compatibility.
1056                MessageHandler
1057                        .error("Failed to save \"" + entity.getName() + "\".");
1058            }
1059        }
1060    }
1061
1062    ///////////////////////////////////////////////////////////////////
1063    ///                    private variables                       ////
1064
1065    private final static String _EXPORT_DESIGN_PATTERN_LABEL = "Export Design Pattern";
1066    private final static String _IMPORT_DESIGN_PATTERN_LABEL = "Import Design Pattern";
1067
1068}