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}