001/* Base class for graph controllers in Ptolemy. 002 003 Copyright (c) 1999-2018 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.vergil.basic; 029 030import java.awt.EventQueue; 031import java.awt.event.ActionEvent; 032import java.awt.geom.Point2D; 033import java.net.URL; 034import java.util.Iterator; 035import java.util.LinkedList; 036import java.util.List; 037 038import javax.swing.AbstractAction; 039import javax.swing.Action; 040import javax.swing.JMenu; 041import javax.swing.JToolBar; 042import javax.swing.SwingUtilities; 043 044import diva.canvas.Figure; 045import diva.canvas.connector.Connector; 046import diva.canvas.interactor.SelectionRenderer; 047import diva.graph.AbstractGraphController; 048import diva.graph.GraphController; 049import diva.graph.GraphModel; 050import diva.graph.GraphPane; 051import diva.graph.GraphUtilities; 052import diva.graph.JGraph; 053import diva.graph.NodeController; 054import diva.gui.toolbox.MenuCreator; 055import ptolemy.actor.gui.ColorAttribute; 056import ptolemy.actor.gui.Configuration; 057import ptolemy.actor.gui.DialogTableau; 058import ptolemy.data.expr.Parameter; 059import ptolemy.kernel.Entity; 060import ptolemy.kernel.InstantiableNamedObj; 061import ptolemy.kernel.util.Attribute; 062import ptolemy.kernel.util.ChangeRequest; 063import ptolemy.kernel.util.DebugEvent; 064import ptolemy.kernel.util.DebugListener; 065import ptolemy.kernel.util.IllegalActionException; 066import ptolemy.kernel.util.Locatable; 067import ptolemy.kernel.util.NameDuplicationException; 068import ptolemy.kernel.util.Nameable; 069import ptolemy.kernel.util.NamedObj; 070import ptolemy.kernel.util.Settable; 071import ptolemy.kernel.util.ValueListener; 072import ptolemy.util.FileUtilities; 073import ptolemy.util.MessageHandler; 074import ptolemy.util.StringUtilities; 075import ptolemy.vergil.actor.ActorGraphFrame; 076import ptolemy.vergil.toolbox.ConfigureAction; 077import ptolemy.vergil.toolbox.FigureAction; 078import ptolemy.vergil.toolbox.MenuActionFactory; 079import ptolemy.vergil.toolbox.PtolemyMenuFactory; 080import ptolemy.vergil.unit.UnitSolverDialog; 081 082/////////////////////////////////////////////////////////////////// 083//// BasicGraphController 084 085/** 086 A base class for Ptolemy II graph controllers. This extends the base 087 class with an association with a configuration. The configuration is 088 central to a Ptolemy GUI, and is used by derived classes to perform 089 various functions such as opening models or their documentation. 090 The class also provides a strategy pattern interface for a controller 091 to add commands to the menu or toolbar of the frame it is controlling. 092 093 @author Steve Neuendorffer and Edward A. Lee 094 @version $Id$ 095 @since Ptolemy II 2.0 096 @Pt.ProposedRating Red (eal) 097 @Pt.AcceptedRating Red (johnr) 098 */ 099public abstract class BasicGraphController extends AbstractGraphController 100 implements DebugListener, ValueListener { 101 /** Create a new basic controller. 102 */ 103 public BasicGraphController() { 104 super(); 105 } 106 107 /////////////////////////////////////////////////////////////////// 108 //// public methods //// 109 110 /** Request a change that clears all the error highlights. */ 111 public void clearAllErrorHighlights() { 112 ChangeRequest request = _getClearAllErrorHighlightsChangeRequest(); 113 _frame.getModel().requestChange(request); 114 } 115 116 /** Highlight the specified object and all its containers to 117 * indicate that it is the source of an error. 118 * @param culprit The culprit. 119 */ 120 public void highlightError(final Nameable culprit) { 121 if (culprit instanceof NamedObj) { 122 ChangeRequest request = new ChangeRequest(this, 123 "Error Highlighter") { 124 @Override 125 protected void _execute() throws Exception { 126 _addErrorHighlightIfNeeded(culprit); 127 NamedObj container = culprit.getContainer(); 128 while (container != null) { 129 _addErrorHighlightIfNeeded(container); 130 container = container.getContainer(); 131 } 132 } 133 }; 134 request.setPersistent(false); 135 ((NamedObj) culprit).requestChange(request); 136 } 137 } 138 139 /** Add commands to the specified menu and toolbar, as appropriate 140 * for this controller. In this base class, nothing is added. 141 * @param menu The menu to add to, or null if none. 142 * @param toolbar The toolbar to add to, or null if none. 143 */ 144 public void addToMenuAndToolbar(JMenu menu, JToolBar toolbar) { 145 _addHotKeys(getFrame().getJGraph()); 146 } 147 148 /** Clear any animation highlight that might currently be active. 149 */ 150 public void clearAnimation() { 151 // Deselect previous one. 152 if (_animated != null && _animationRenderer != null) { 153 _animationRenderer.renderDeselected(_animated); 154 } 155 } 156 157 /** React to an event. This base class does nothing. 158 * @param event The debug event. 159 */ 160 @Override 161 public void event(DebugEvent event) { 162 } 163 164 /** Get the time delay for animation. After highlighting, 165 * derived classes are expected to sleep for the specified amount 166 * of time, in milliseconds. 167 * @return The animation delay set by setAnimationDelay(). 168 * @see #setAnimationDelay(long) 169 */ 170 public long getAnimationDelay() { 171 return _animationDelay; 172 } 173 174 /** Return the configuration that has been specified by setConfiguration(), 175 * or null if none. 176 * @return The configuration. 177 * @see #setConfiguration(Configuration) 178 */ 179 public Configuration getConfiguration() { 180 return _configuration; 181 } 182 183 /** Return the configuration menu factory. 184 * 185 * @return The configuration menu factory. 186 */ 187 public MenuActionFactory getConfigureMenuFactory() { 188 return _configureMenuFactory; 189 } 190 191 /** Get the graph frame, or null if there is none. This is used by 192 * some of the controllers to mark the modified bit of the frame 193 * and to update any dependents. 194 * @return The graph frame, or null if there is none. 195 * @see #setFrame(BasicGraphFrame) 196 */ 197 public BasicGraphFrame getFrame() { 198 return _frame; 199 } 200 201 /** Return the node controller appropriate for the given object. 202 * In this base class, the method checks to see whether the object 203 * is an instance of Locatable and contains a NodeControllerFactory 204 * (which is an attribute). If it does, then it invokes that factory 205 * to create a node controller. Otherwise, it returns null. 206 * @param object The object to get a controller for. 207 * @return A custom node controller if there is one, and null otherwise. 208 */ 209 @Override 210 public NodeController getNodeController(Object object) { 211 if (object instanceof Locatable) { 212 Object semanticObject = getGraphModel().getSemanticObject(object); 213 214 // Check to see whether 215 // this is a NamedObj that contains a NodeControllerFactory. 216 // If so, that should be used. If not, use the defaults 217 // below. This allows any object in Ptolemy II to have 218 // its own controller, which means its own context menu 219 // and its own interactors. 220 if (semanticObject instanceof NamedObj) { 221 List factoryList = ((NamedObj) semanticObject) 222 .attributeList(NodeControllerFactory.class); 223 224 // FIXME: This is creating a new node controller for each instance!!! 225 // This causes problems as indicated by the NOTE in ActorInstanceController. 226 if (factoryList.size() > 0) { 227 NodeControllerFactory factory = (NodeControllerFactory) factoryList 228 .get(0); 229 NamedObjController controller = factory.create(this); 230 controller.setConfiguration(getConfiguration()); 231 _initializeInteraction(controller); 232 return controller; 233 } 234 } 235 } 236 237 return null; 238 } 239 240 /** React to a debug message. This base class does nothing. 241 * @param message The message. 242 */ 243 @Override 244 public void message(String message) { 245 } 246 247 /** Set the time delay for animation. After highlighting, 248 * derived classes are expected to sleep for the specified amount 249 * of time, in milliseconds. If this method is not called, or 250 * is called with argument 0, then no delay is introduced. 251 * @param time Time to sleep, in milliseconds. 252 * @see #getAnimationDelay() 253 254 */ 255 public void setAnimationDelay(long time) { 256 _animationDelay = time; 257 } 258 259 /** Set the configuration. This is used by some of the controllers 260 * when opening files or URLs. 261 * The configuration is checked for a "_getDocumentationActionDocPreference", 262 * which, if present, is an integer that is passed to 263 * {@link ptolemy.vergil.basic.GetDocumentationAction#GetDocumentationAction(int)}. 264 * This attribute is used to select the Kepler-specific 265 * KeplerDocumentationAttribute. 266 * @param configuration The configuration. 267 * @see #getConfiguration() 268 */ 269 public void setConfiguration(Configuration configuration) { 270 _configuration = configuration; 271 272 if (configuration != null && _getDocumentationAction == null) { 273 int docPreference = 0; 274 String parameterName = "_getDocumentationActionDocPreference"; 275 try { 276 Parameter getDocumentationActionDocPreference = (Parameter) configuration 277 .getAttribute(parameterName, Parameter.class); 278 if (getDocumentationActionDocPreference != null) { 279 // If you want KeplerDocumentationAttribute, set 280 // _getDocumentationActionDocPreference to 1. 281 docPreference = Integer 282 .parseInt(getDocumentationActionDocPreference 283 .getExpression()); 284 } 285 } catch (Exception ex) { 286 System.err.println("Warning, failed to parse " + parameterName); 287 ex.printStackTrace(); 288 } 289 _getDocumentationAction = new GetDocumentationAction(docPreference); 290 } 291 if (_getDocumentationAction != null) { 292 _getDocumentationAction.setConfiguration(configuration); 293 } 294 295 if (_configuration != null && _menuFactory != null) { 296 // NOTE: The following requires that the configuration be 297 // non-null, or it will report an error. 298 _menuFactory.addMenuItemFactory( 299 new MenuActionFactory(_openBaseClassAction)); 300 } 301 } 302 303 /////////////////////////////////////////////////////////////////// 304 //// public methods //// 305 306 /** Set the figure associated with the given semantic object, and if 307 * that semantic object is Settable, then set up a value listener 308 * so that if its value changes, then the valueChanged() method 309 * is invoked. 310 * The semantic object is normally an attribute that implements 311 * the Locatable interface, and the value indicates the location 312 * of the object. 313 * A null figure clears the association. 314 * @param semanticObject The semantic object (normally a Locatable). 315 * @param figure The figure. 316 */ 317 @Override 318 public void setFigure(Object semanticObject, Figure figure) { 319 super.setFigure(semanticObject, figure); 320 321 if (semanticObject instanceof Settable) { 322 ((Settable) semanticObject).addValueListener(this); 323 } 324 } 325 326 /** Set the graph frame. This is used by some of the controllers 327 * to mark the modified bit of the frame and to update any dependents. 328 * @param frame The graph frame, or null if there is none. 329 * @see #getFrame() 330 */ 331 public void setFrame(BasicGraphFrame frame) { 332 _frame = frame; 333 } 334 335 /** React to the fact that the specified Settable has changed. 336 * If the specified Settable implements the Locatable interface, 337 * then this method will move the figure and reroute any connections 338 * to it. This is done immediately if the caller is in the Swing 339 * event thread, but otherwise is deferred to the event thread. 340 * @param settable The object that has changed value. 341 */ 342 @Override 343 public void valueChanged(final Settable settable) { 344 if (settable instanceof Locatable && !_inValueChanged) { 345 // Have to defer this to the event thread, or repaint 346 // doesn't work properly. 347 Runnable action = new Runnable() { 348 @Override 349 public void run() { 350 Locatable location = (Locatable) settable; 351 Figure figure = getFigure(location); 352 if (figure != null) { 353 Point2D origin = figure.getOrigin(); 354 355 double originalUpperLeftX = origin.getX(); 356 double originalUpperLeftY = origin.getY(); 357 358 // NOTE: the following call may trigger an evaluation, 359 // which results in another recursive call to this method. 360 // Thus, we ignore the inside call and detect it with a 361 // private variable. 362 double[] newLocation; 363 364 try { 365 _inValueChanged = true; 366 newLocation = location.getLocation(); 367 } finally { 368 _inValueChanged = false; 369 } 370 371 double translationX = newLocation[0] 372 - originalUpperLeftX; 373 double translationY = newLocation[1] 374 - originalUpperLeftY; 375 376 if (translationX != 0.0 || translationY != 0.0) { 377 // The translate method supposedly handles the required 378 // repaint. 379 figure.translate(translationX, translationY); 380 381 // Reroute edges linked to this figure. 382 GraphModel model = getGraphModel(); 383 Object userObject = figure.getUserObject(); 384 385 if (userObject != null) { 386 Iterator inEdges = model.inEdges(userObject); 387 388 while (inEdges.hasNext()) { 389 Figure connector = getFigure( 390 inEdges.next()); 391 392 if (connector instanceof Connector) { 393 ((Connector) connector).reroute(); 394 } 395 } 396 397 Iterator outEdges = model.outEdges(userObject); 398 399 while (outEdges.hasNext()) { 400 Figure connector = getFigure( 401 outEdges.next()); 402 403 if (connector instanceof Connector) { 404 ((Connector) connector).reroute(); 405 } 406 } 407 408 if (model.isComposite(userObject)) { 409 Iterator edges = GraphUtilities 410 .partiallyContainedEdges(userObject, 411 model); 412 413 while (edges.hasNext()) { 414 Figure connector = getFigure( 415 edges.next()); 416 417 if (connector instanceof Connector) { 418 ((Connector) connector).reroute(); 419 } 420 } 421 } 422 } 423 } 424 } 425 } /* end of run() method */ 426 }; /* end of Runnable definition. */ 427 428 if (EventQueue.isDispatchThread()) { 429 action.run(); 430 } else { 431 SwingUtilities.invokeLater(action); 432 } 433 } 434 } 435 436 /////////////////////////////////////////////////////////////////// 437 //// protected methods //// 438 439 /** Add hot keys to the actions in the given JGraph. 440 * 441 * @param jgraph The JGraph to which hot keys are to be added. 442 */ 443 protected void _addHotKeys(JGraph jgraph) { 444 } 445 446 /** Create the controllers for nodes in this graph. 447 * In this base class, nothing is created. 448 * This is called by the constructor, so derived classes that 449 * override this must be careful not to reference local variables 450 * defined in the derived classes, because the derived classes 451 * will not have been fully constructed by the time this is called. 452 */ 453 protected void _createControllers() { 454 } 455 456 /** Return true if there are active highlights. 457 * @return True if the list if error highlights is not empty. 458 */ 459 protected boolean _areThereActiveErrorHighlights() { 460 return !_errorHighlights.isEmpty(); 461 } 462 463 /** 464 * Return a change request that clears all the highlights. 465 * @return a change request that clears all the highlights. 466 */ 467 protected ChangeRequest _getClearAllErrorHighlightsChangeRequest() { 468 ChangeRequest request = new ChangeRequest(this, 469 "Error Highlight Clearer", true) { 470 @Override 471 protected void _execute() throws Exception { 472 for (Attribute highlight : _errorHighlights) { 473 highlight.setContainer(null); 474 } 475 } 476 }; 477 478 // Mark the Error Highlight Clearer request as 479 // non-persistant so that we don't mark the model as being 480 // modified. ptolemy/actor/lib/jni/test/Scale/Scale.xml 481 // required this change. 482 request.setPersistent(false); 483 return request; 484 } 485 486 /** Initialize interactions for the specified controller. This 487 * method is called when a new controller is constructed. This 488 * base class does nothing, but derived classes may attach interactors 489 * to the specified controller. 490 * @param controller The controller for which to initialize interaction. 491 */ 492 protected void _initializeInteraction(NamedObjController controller) { 493 } 494 495 // NOTE: The following method name does not have a leading underscore 496 // because it is a diva method. 497 498 /** Initialize all interaction on the graph pane. This method 499 * is called by the setGraphPane() method of the superclass. 500 * This initialization cannot be done in the constructor because 501 * the controller does not yet have a reference to its pane 502 * at that time. Regrettably, the canvas is not yet associated 503 * with the GraphPane, so you can't do any initialization that 504 * involves the canvas. 505 */ 506 @Override 507 protected void initializeInteraction() { 508 // Remove the existing menu if it has already been created by an earlier 509 // call of this method, because we may invoke this method multiple times 510 // but we don't want the same items to show up multiple times. 511 // -- tfeng (07/16/2009) 512 _menuFactory = null; 513 514 GraphPane pane = getGraphPane(); 515 // Start Kepler code. 516 List configsList = Configuration.configurations(); 517 Configuration config = _configuration; 518 if (config == null) { 519 // Is this really necessary? 520 for (Iterator it = configsList.iterator(); it.hasNext();) { 521 config = (Configuration) it.next(); 522 if (config != null) { 523 break; 524 } 525 } 526 } 527 528 // If a MenuFactory has been defined in the configuration, use this 529 // one; otherwise, use the default Ptolemy one: 530 if (config != null && _contextMenuFactoryCreator == null) { 531 _contextMenuFactoryCreator = (ContextMenuFactoryCreator) config 532 .getAttribute("canvasContextMenuFactory"); 533 } 534 535 // NOTE: by passing "this" to the menu factory, we are making it 536 // handle right-click menus for the canvas (only) - not the actors or 537 // relations; these are controlled by AttributeController and 538 // RelationController, respectively - MB - 2/14/06 539 if (_contextMenuFactoryCreator != null) { 540 try { 541 _menuFactory = (PtolemyMenuFactory) _contextMenuFactoryCreator 542 .createContextMenuFactory(this); 543 // this is only done here, not for both MenuFactories, because 544 // SchematicContextMenuFactory already does this in its 545 // constructor: 546 // (Save _configureMenuFactory for use in sub classes) 547 _configureMenuFactory = new MenuActionFactory(_configureAction); 548 _menuFactory.addMenuItemFactory(_configureMenuFactory); 549 } catch (Throwable throwable) { 550 // do nothing - will default to ptii right-click menus 551 // System.out.println("Unable to use the alternative right-click menu " 552 // + "handler that was specified in the " 553 // + "configuration; defaulting to ptii handler. " 554 // + "Exception was: " + throwable); 555 } 556 } 557 // if the above has failed in any way, _menuFactory will still be null, 558 // in which case we should default to ptii context menus 559 if (_menuFactory == null) { 560 _menuFactory = new SchematicContextMenuFactory(this); 561 } 562 // End Kepler code. 563 564 _menuCreator = new MenuCreator(_menuFactory); 565 _menuCreator.setMouseFilter(new PopupMouseFilter()); 566 567 // Note that the menuCreator cannot be an interactor, because 568 // it accepts all events. 569 // NOTE: The above is a very strange comment, since 570 // it is an interactor. EAL 2/5/05. 571 pane.getBackgroundEventLayer().addInteractor(_menuCreator); 572 pane.getBackgroundEventLayer().setConsuming(false); 573 574 Action[] actions = { _getDocumentationAction, 575 new CustomizeDocumentationAction(), 576 new RemoveCustomDocumentationAction() }; 577 _menuFactory.addMenuItemFactory( 578 new MenuActionFactory(actions, "Documentation")); 579 580 if (_configuration != null) { 581 // NOTE: The following requires that the configuration be 582 // non-null, or it will report an error. 583 _menuFactory.addMenuItemFactory( 584 new MenuActionFactory(_openBaseClassAction)); 585 _menuFactory.addMenuItemFactory( 586 new MenuActionFactory(_unitSolverDialogAction)); 587 } 588 } 589 590 /////////////////////////////////////////////////////////////////// 591 //// protected variables //// 592 593 /** Currently animated state, if any. */ 594 protected Figure _animated; 595 596 /** Renderer for animation. */ 597 protected SelectionRenderer _animationRenderer; 598 599 /** The configure action. */ 600 protected static ConfigureAction _configureAction = new ConfigureAction( 601 "Configure"); 602 603 /** The submenu for configure actions. */ 604 protected static MenuActionFactory _configureMenuFactory; 605 606 /** The interactor for creating context sensitive menus on the 607 * graph itself. 608 */ 609 protected MenuCreator _menuCreator; 610 611 /** The factory belonging to the menu creator. */ 612 protected PtolemyMenuFactory _menuFactory; 613 614 /** The open base class action. */ 615 protected OpenBaseClassAction _openBaseClassAction = new OpenBaseClassAction(); 616 617 /** The UnitSolverDialog action. */ 618 protected UnitSolverDialogAction _unitSolverDialogAction = new UnitSolverDialogAction(); 619 620 /////////////////////////////////////////////////////////////////// 621 //// private methods //// 622 623 /** Add an error highlight color to the specified culprit if it is 624 * not already present. 625 * @param culprit The culprit to highlight. 626 * @exception IllegalActionException If the highlight cannot be added. 627 * @exception NameDuplicationException Should not be thrown. 628 */ 629 private void _addErrorHighlightIfNeeded(Nameable culprit) 630 throws IllegalActionException, NameDuplicationException { 631 Attribute highlightColor = ((NamedObj) culprit) 632 .getAttribute("_highlightColor"); 633 if (highlightColor == null) { 634 highlightColor = new ColorAttribute((NamedObj) culprit, 635 "_highlightColor"); 636 ((ColorAttribute) highlightColor) 637 .setExpression("{1.0, 0.0, 0.0, 1.0}"); 638 highlightColor.setPersistent(false); 639 ((ColorAttribute) highlightColor).setVisibility(Settable.EXPERT); 640 _errorHighlights.add(highlightColor); 641 } 642 } 643 644 /////////////////////////////////////////////////////////////////// 645 //// private variables //// 646 647 /** The time to sleep upon animation. */ 648 private long _animationDelay = 0L; 649 650 // The configuration. 651 private Configuration _configuration; 652 653 /** 654 * A configurable object that allows a different MenuFactory to be specified 655 * instead of the default ptII one. The MenuFactory constructs the 656 * right-click context menus 657 */ 658 private static ContextMenuFactoryCreator _contextMenuFactoryCreator; 659 660 /** List of error highlight attributes we have created. */ 661 private List<Attribute> _errorHighlights = new LinkedList<Attribute>(); 662 663 // The get documentation action. 664 private GetDocumentationAction _getDocumentationAction = new GetDocumentationAction(); 665 666 // The graph frame, if there is one. 667 private BasicGraphFrame _frame; 668 669 // Flag to prevent double rendering upon setting location. 670 private boolean _inValueChanged = false; 671 672 /////////////////////////////////////////////////////////////////// 673 //// inner classes //// 674 /////////////////////////////////////////////////////////////////// 675 //// OpenBaseClassAction 676 677 /** An action that will open the base class of a subclass or the class 678 * of an instance. 679 */ 680 @SuppressWarnings("serial") 681 public class OpenBaseClassAction extends FigureAction { 682 /** Construct a new action. 683 */ 684 public OpenBaseClassAction() { 685 super("Open Base Class"); 686 } 687 688 /////////////////////////////////////////////////////////////////// 689 //// public methods //// 690 691 /** Open the base class of a subclass or the class of an instance. 692 * @param e The event. 693 */ 694 @Override 695 public void actionPerformed(ActionEvent e) { 696 if (_configuration == null) { 697 MessageHandler.error( 698 "Cannot open base class without a configuration."); 699 return; 700 } 701 702 // Determine which entity was selected for the look inside action. 703 super.actionPerformed(e); 704 705 NamedObj target = getTarget(); 706 707 if (target == null) { 708 return; 709 } 710 711 try { 712 if (target instanceof InstantiableNamedObj) { 713 InstantiableNamedObj deferTo = (InstantiableNamedObj) ((InstantiableNamedObj) target) 714 .getParent(); 715 716 if (deferTo != null) { 717 _configuration.openModel(deferTo); 718 return; 719 } 720 } 721 722 String source = target.getSource(); 723 724 if (source != null && !source.trim().equals("")) { 725 // FIXME: Is there a more reasonable base directory 726 // to give for the second argument? 727 URL sourceURL = FileUtilities.nameToURL(source, null, 728 target.getClass().getClassLoader()); 729 _configuration.openModel(null, sourceURL, source); 730 return; 731 } 732 733 // Target does not defer and does not have a defined "source". 734 // Assume its base class is a Java class and open the source 735 // code. 736 String sourceFileName = StringUtilities 737 .objectToSourceFileName(target); 738 URL sourceURL = target.getClass().getClassLoader() 739 .getResource(sourceFileName); 740 _configuration.openModel(null, sourceURL, 741 sourceURL.toExternalForm()); 742 } catch (Exception ex) { 743 MessageHandler.error("Open base class failed.", ex); 744 } 745 } 746 } 747 748 /////////////////////////////////////////////////////////////////// 749 //// UnitSolverDialogAction 750 751 /** An action that will create a UnitSolverDialog. 752 */ 753 @SuppressWarnings("serial") 754 public class UnitSolverDialogAction extends AbstractAction { 755 /** Construct an action that will create a UnitSolverDialog. 756 */ 757 public UnitSolverDialogAction() { 758 super("UnitConstraints Solver"); 759 } 760 761 /** Construct a UnitSolverDialog. 762 * @param e The action event, ignored by this method. 763 */ 764 @Override 765 public void actionPerformed(ActionEvent e) { 766 // Only makes sense if this is an ActorGraphFrame. 767 if (_frame instanceof ActorGraphFrame) { 768 DialogTableau dialogTableau = DialogTableau.createDialog(_frame, 769 _configuration, ((ActorGraphFrame) _frame).getEffigy(), 770 UnitSolverDialog.class, 771 (Entity) ((ActorGraphFrame) _frame).getModel()); 772 773 if (dialogTableau != null) { 774 dialogTableau.show(); 775 } 776 } 777 } 778 } 779 780 /////////////////////////////////////////////////////////////////// 781 //// SchematicContextMenuFactory 782 783 /** Factory for context menus. */ 784 public static class SchematicContextMenuFactory extends PtolemyMenuFactory { 785 /** Create a new context menu factory associated with the 786 * specified controller. 787 * @param controller The controller. 788 */ 789 public SchematicContextMenuFactory(GraphController controller) { 790 super(controller); 791 _configureMenuFactory = new MenuActionFactory(_configureAction); 792 addMenuItemFactory(_configureMenuFactory); 793 } 794 795 @Override 796 protected NamedObj _getObjectFromFigure(Figure source) { 797 // NOTE: Between Ptolemy 3.0 and 5.0, this would ignore 798 // the source argument, even if it was non-null. Why? 799 // EAL 2/5/05. 800 if (source != null) { 801 Object object = source.getUserObject(); 802 return (NamedObj) getController().getGraphModel() 803 .getSemanticObject(object); 804 } else { 805 return (NamedObj) getController().getGraphModel().getRoot(); 806 } 807 } 808 } 809}