001/* A simple graph view for Ptolemy models 002 003 Copyright (c) 1998-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 2 027 */ 028package ptolemy.vergil.basic; 029 030import java.awt.BorderLayout; 031import java.awt.Color; 032import java.awt.Component; 033import java.awt.Cursor; 034import java.awt.Dimension; 035import java.awt.Event; 036import java.awt.FileDialog; 037import java.awt.FlowLayout; 038import java.awt.Frame; 039import java.awt.Graphics; 040import java.awt.GridBagConstraints; 041import java.awt.GridBagLayout; 042import java.awt.Toolkit; 043import java.awt.datatransfer.Clipboard; 044import java.awt.datatransfer.ClipboardOwner; 045import java.awt.datatransfer.DataFlavor; 046import java.awt.datatransfer.StringSelection; 047import java.awt.datatransfer.Transferable; 048import java.awt.event.ActionEvent; 049import java.awt.event.ActionListener; 050import java.awt.event.KeyEvent; 051import java.awt.event.MouseAdapter; 052import java.awt.event.MouseEvent; 053import java.awt.event.MouseListener; 054import java.awt.event.MouseMotionListener; 055import java.awt.event.MouseWheelEvent; 056import java.awt.event.MouseWheelListener; 057import java.awt.geom.AffineTransform; 058import java.awt.geom.NoninvertibleTransformException; 059import java.awt.geom.Point2D; 060import java.awt.geom.Rectangle2D; 061import java.awt.print.PageFormat; 062import java.awt.print.Printable; 063import java.awt.print.PrinterException; 064import java.io.File; 065import java.io.FileOutputStream; 066import java.io.FileWriter; 067import java.io.IOException; 068import java.io.OutputStream; 069import java.io.StringWriter; 070import java.io.Writer; 071import java.lang.reflect.Constructor; 072import java.lang.reflect.Method; 073import java.net.URL; 074import java.util.Arrays; 075import java.util.HashSet; 076import java.util.Iterator; 077import java.util.LinkedList; 078import java.util.List; 079import java.util.Locale; 080import java.util.Stack; 081 082import javax.swing.AbstractAction; 083import javax.swing.Action; 084import javax.swing.BorderFactory; 085import javax.swing.JComponent; 086import javax.swing.JFileChooser; 087import javax.swing.JLabel; 088import javax.swing.JMenu; 089import javax.swing.JMenuItem; 090import javax.swing.JPanel; 091import javax.swing.JScrollPane; 092import javax.swing.JSplitPane; 093import javax.swing.JTabbedPane; 094import javax.swing.JTextField; 095import javax.swing.JToolBar; 096import javax.swing.JTree; 097import javax.swing.KeyStroke; 098import javax.swing.filechooser.FileFilter; 099import javax.swing.tree.DefaultTreeCellRenderer; 100import javax.swing.tree.TreePath; 101 102import diva.canvas.CanvasComponent; 103import diva.canvas.CanvasUtilities; 104import diva.canvas.Figure; 105import diva.canvas.FigureLayer; 106import diva.canvas.JCanvas; 107import diva.canvas.event.EventLayer; 108import diva.canvas.event.LayerAdapter; 109import diva.canvas.event.LayerEvent; 110import diva.canvas.interactor.SelectionModel; 111import diva.graph.GraphController; 112import diva.graph.GraphEvent; 113import diva.graph.GraphModel; 114import diva.graph.GraphPane; 115import diva.graph.GraphUtilities; 116import diva.graph.JGraph; 117import diva.gui.GUIUtilities; 118import diva.gui.toolbox.JCanvasPanner; 119import diva.gui.toolbox.JContextMenu; 120import diva.util.Filter; 121import diva.util.UserObjectContainer; 122import diva.util.java2d.ShapeUtilities; 123import ptolemy.actor.Actor; 124import ptolemy.actor.DesignPatternGetMoMLAction; 125import ptolemy.actor.Director; 126import ptolemy.actor.IOPort; 127import ptolemy.actor.IORelation; 128import ptolemy.actor.QuasiTransparentDirector; 129import ptolemy.actor.gui.BrowserEffigy; 130import ptolemy.actor.gui.Configuration; 131import ptolemy.actor.gui.DialogTableau; 132import ptolemy.actor.gui.EditParametersDialog; 133import ptolemy.actor.gui.Effigy; 134import ptolemy.actor.gui.LevelSkippingTableauFactory; 135import ptolemy.actor.gui.PtolemyFrame; 136import ptolemy.actor.gui.PtolemyPreferences; 137import ptolemy.actor.gui.SizeAttribute; 138import ptolemy.actor.gui.Tableau; 139import ptolemy.actor.gui.UserActorLibrary; 140import ptolemy.actor.gui.WindowPropertiesAttribute; 141import ptolemy.actor.gui.properties.ToolBar; 142import ptolemy.actor.parameters.ParameterPort; 143import ptolemy.data.ArrayToken; 144import ptolemy.data.DoubleToken; 145import ptolemy.data.StringToken; 146import ptolemy.data.Token; 147import ptolemy.data.expr.ExpertParameter; 148import ptolemy.data.expr.Parameter; 149import ptolemy.data.expr.StringParameter; 150import ptolemy.gui.ComponentDialog; 151import ptolemy.gui.ExtensionFilenameFilter; 152import ptolemy.gui.ImageExportable; 153import ptolemy.gui.JFileChooserBugFix; 154import ptolemy.gui.MemoryCleaner; 155import ptolemy.gui.PtFileChooser; 156import ptolemy.gui.PtGUIUtilities; 157import ptolemy.gui.Query; 158import ptolemy.gui.Top; 159import ptolemy.kernel.ComponentEntity; 160import ptolemy.kernel.CompositeEntity; 161import ptolemy.kernel.Entity; 162import ptolemy.kernel.undo.RedoChangeRequest; 163import ptolemy.kernel.undo.UndoChangeRequest; 164import ptolemy.kernel.util.Attribute; 165import ptolemy.kernel.util.ChangeListener; 166import ptolemy.kernel.util.ChangeRequest; 167import ptolemy.kernel.util.IllegalActionException; 168import ptolemy.kernel.util.InternalErrorException; 169import ptolemy.kernel.util.KernelException; 170import ptolemy.kernel.util.Location; 171import ptolemy.kernel.util.NameDuplicationException; 172import ptolemy.kernel.util.NamedObj; 173import ptolemy.kernel.util.Settable; 174import ptolemy.kernel.util.StringAttribute; 175import ptolemy.kernel.util.Workspace; 176import ptolemy.moml.ErrorHandler; 177import ptolemy.moml.IconLoader; 178import ptolemy.moml.LibraryAttribute; 179import ptolemy.moml.MoMLChangeRequest; 180import ptolemy.moml.MoMLParser; 181import ptolemy.moml.MoMLVariableChecker; 182import ptolemy.moml.SimpleErrorHandler; 183import ptolemy.util.CancelException; 184import ptolemy.util.MessageHandler; 185import ptolemy.util.SimpleMessageHandler; 186import ptolemy.vergil.icon.DesignPatternIcon; 187import ptolemy.vergil.kernel.AttributeNodeModel; 188import ptolemy.vergil.modal.FSMGraphModel; 189import ptolemy.vergil.toolbox.MenuItemFactory; 190import ptolemy.vergil.toolbox.MoveAction; 191import ptolemy.vergil.tree.ClassAndEntityTreeModel; 192import ptolemy.vergil.tree.EntityTreeModel; 193import ptolemy.vergil.tree.PTree; 194import ptolemy.vergil.tree.PTreeMenuCreator; 195import ptolemy.vergil.tree.PtolemyTreeCellRenderer; 196import ptolemy.vergil.tree.VisibleTreeModel; 197 198/////////////////////////////////////////////////////////////////// 199//// BasicGraphFrame 200 201/** 202 A simple graph view for ptolemy models. This represents a level of 203 the hierarchy of a ptolemy model as a diva graph. Cut, copy and 204 paste operations are supported using MoML. 205 206 @author Steve Neuendorffer, Edward A. Lee, Contributors: Chad Berkeley (Kepler), Ian Brown (HSBC), Bert Rodiers, Christian Motika, Daniel Crawl 207 @version $Id$ 208 @since Ptolemy II 2.0 209 @Pt.ProposedRating Red (neuendor) 210 @Pt.AcceptedRating Red (johnr) 211 */ 212@SuppressWarnings("serial") 213public abstract class BasicGraphFrame extends PtolemyFrame implements Printable, 214 ClipboardOwner, ChangeListener, MouseWheelListener, MouseListener, 215 MouseMotionListener, ImageExportable, HTMLExportable { 216 217 /** Construct a frame associated with the specified Ptolemy II model 218 * or object. After constructing this, it is necessary 219 * to call setVisible(true) to make the frame appear. 220 * This is typically done by calling show() on the controlling tableau. 221 * This constructor results in a graph frame that obtains its library 222 * either from the model (if it has one) or the default library defined 223 * in the configuration. 224 * @see Tableau#show() 225 * @param entity The model or object to put in this frame. 226 * @param tableau The tableau responsible for this frame. 227 */ 228 public BasicGraphFrame(NamedObj entity, Tableau tableau) { 229 this(entity, tableau, null); 230 } 231 232 /** Construct a frame associated with the specified Ptolemy II model. 233 * After constructing this, it is necessary 234 * to call setVisible(true) to make the frame appear. 235 * This is typically done by calling show() on the controlling tableau. 236 * This constructor results in a graph frame that obtains its library 237 * either from the model (if it has one), or the <i>defaultLibrary</i> 238 * argument (if it is non-null), or the default library defined 239 * in the configuration. 240 * @see Tableau#show() 241 * @param entity The model or object to put in this frame. 242 * @param tableau The tableau responsible for this frame. 243 * @param defaultLibrary An attribute specifying the default library 244 * to use if the model does not have a library. The <i>defaultLibrary</i> 245 * attribute is only read if the model does not have a 246 * {@link ptolemy.moml.LibraryAttribute} with the name 247 * "<code>_library</code>", or if the LibraryAttribute cannot be 248 * read. 249 */ 250 public BasicGraphFrame(NamedObj entity, Tableau tableau, 251 LibraryAttribute defaultLibrary) { 252 super(entity, tableau); 253 _defaultLibrary = defaultLibrary; 254 _initBasicGraphFrame(); 255 } 256 257 /////////////////////////////////////////////////////////////////// 258 //// public methods //// 259 260 /** React to the fact that a change has been successfully executed 261 * by marking the data associated with this window modified. This 262 * will trigger a dialog when the window is closed, prompting the 263 * user to save the data. 264 * @param change The change that has been executed. 265 */ 266 @Override 267 public void changeExecuted(ChangeRequest change) { 268 boolean persistent = true; 269 270 // If the change is null, do not mark the model modified, 271 // but do update the graph panner. 272 if (change != null) { 273 persistent = change.isPersistent(); 274 275 // Note that we don't want to accidently reset to false here. 276 if (persistent) { 277 setModified(persistent); 278 } 279 } 280 281 if (_graphPanner != null) { 282 _graphPanner.repaint(); 283 } 284 } 285 286 /** React to the fact that a change has triggered an error by 287 * doing nothing (the effigy is also listening and will report 288 * the error). 289 * @param change The change that was attempted. 290 * @param exception The exception that resulted. 291 */ 292 @Override 293 public void changeFailed(ChangeRequest change, Exception exception) { 294 // Do not report if it has already been reported. 295 if (change == null) { 296 MessageHandler.error("Change failed", exception); 297 } else if (!change.isErrorReported()) { 298 change.setErrorReported(true); 299 MessageHandler.error("Change failed", exception); 300 } 301 } 302 303 /** Clear the selected objects in this frame. 304 */ 305 public void clearSelection() { 306 GraphController controller = _getGraphController(); 307 SelectionModel model = controller.getSelectionModel(); 308 model.clearSelection(); 309 } 310 311 /** Get the currently selected objects from this document, if any, 312 * and place them on the clipboard in MoML format. 313 */ 314 public void copy() { 315 HashSet<NamedObj> namedObjSet = _getSelectionSet(); 316 StringWriter buffer = new StringWriter(); 317 318 try { 319 NamedObj container = (NamedObj) _getGraphModel().getRoot(); 320 321 // NOTE: The order in the model must be respected. 322 Iterator<NamedObj> elements = container 323 .sortContainedObjects(namedObjSet).iterator(); 324 325 while (elements.hasNext()) { 326 NamedObj element = elements.next(); 327 328 // first level to avoid obnoxiousness with 329 // toplevel translations. 330 element.exportMoML(buffer, 0); 331 } 332 333 if (container instanceof CompositeEntity) { 334 buffer.write(((CompositeEntity) container).exportLinks(1, 335 namedObjSet)); 336 } 337 338 Clipboard clipboard = java.awt.Toolkit.getDefaultToolkit() 339 .getSystemClipboard(); 340 341 // The code below does not use a PtolemyTransferable, 342 // to work around 343 // a bug in the JDK that should be fixed as of jdk1.3.1. The bug 344 // is that cut and paste through the system clipboard to native 345 // applications doesn't work unless you use string selection. 346 String momlToBeCopied = buffer.toString(); 347 String variablesToBePrepended = ""; 348 try { 349 MoMLVariableChecker variableChecker = new MoMLVariableChecker(); 350 variablesToBePrepended = variableChecker 351 .checkCopy(momlToBeCopied, container); 352 } catch (IllegalActionException ex) { 353 // Ignore, maybe the missing symbols will work out 354 // in the pasted context. 355 } 356 clipboard.setContents(new StringSelection( 357 variablesToBePrepended + momlToBeCopied), this); 358 359 } catch (IOException ex) { 360 MessageHandler.error("Copy failed", ex); 361 } 362 } 363 364 /** Create a typed composite actor that contains the selected actors 365 * and connections. The created typed composite actor is transparent. 366 * The resulting topology is the same in the sense 367 * of deep connections. 368 */ 369 public void createHierarchy() { 370 GraphController controller = _getGraphController(); 371 SelectionModel model = controller.getSelectionModel(); 372 AbstractBasicGraphModel graphModel = (AbstractBasicGraphModel) controller 373 .getGraphModel(); 374 375 List<Object> origSelection = Arrays.asList(model.getSelectionAsArray()); 376 HashSet<Object> selection = new HashSet<Object>(); 377 selection.addAll(origSelection); 378 379 // A set, because some objects may represent the same 380 // ptolemy object. 381 HashSet<NamedObj> namedObjSet = new HashSet<NamedObj>(); 382 HashSet<Object> nodeSet = new HashSet<Object>(); 383 384 StringBuffer newPorts = new StringBuffer(); 385 StringBuffer extRelations = new StringBuffer(); 386 StringBuffer extConnections = new StringBuffer(); 387 StringBuffer intRelations = new StringBuffer(); 388 StringBuffer intConnections = new StringBuffer(); 389 390 HashSet<Object> selectedEdges = new HashSet<Object>(); 391 392 // First get all the nodes. 393 try { 394 NamedObj container = (NamedObj) graphModel.getRoot(); 395 396 if (!(container instanceof CompositeEntity)) { 397 // This is an internal error because a reasonable GUI should not 398 // provide access to this functionality. 399 throw new InternalErrorException( 400 "Cannot create hierarchy if the container is not a CompositeEntity."); 401 } 402 403 CompositeEntity compositeActor = (CompositeEntity) container; 404 405 String compositeActorName = container.uniqueName("CompositeActor"); 406 407 double[] location = new double[2]; 408 boolean gotLocation = false; 409 410 for (Object selectedItem : origSelection) { 411 if (selectedItem instanceof Figure) { 412 Object userObject = ((Figure) selectedItem).getUserObject(); 413 414 // We want to skip the selection of ports in composite actors since 415 // these should remain in the already existing composite actor. 416 // When the wire has been selected the port will be correctly duplicated. 417 if (userObject instanceof Location) { 418 Location loc = (Location) userObject; 419 NamedObj locationContainer = loc.getContainer(); 420 if (locationContainer != null 421 && locationContainer instanceof IOPort) { 422 NamedObj portContainer = locationContainer 423 .getContainer(); 424 if (portContainer != null 425 && portContainer instanceof CompositeEntity) { 426 // Remove element from selection 427 model.removeSelection(selectedItem); 428 selection.remove(selectedItem); 429 430 // Don't process this node. 431 continue; 432 } 433 } 434 435 } 436 437 if (!gotLocation) { 438 location[0] = ((Figure) selectedItem).getBounds() 439 .getCenterX(); 440 location[1] = ((Figure) selectedItem).getBounds() 441 .getCenterY(); 442 gotLocation = true; 443 } 444 445 if (graphModel.isNode(userObject)) { 446 nodeSet.add(userObject); 447 448 NamedObj actual = (NamedObj) graphModel 449 .getSemanticObject(userObject); 450 namedObjSet.add(actual); 451 452 // We will now add all links from and to the ports of the actor 453 // as selected links since we don't want to lose links and relations when creating 454 // hierarchies 455 456 if (actual instanceof Entity) { 457 for (IOPort port : (List<IOPort>) ((Entity) actual) 458 .portList()) { 459 Iterator<?> outEdges = graphModel 460 .outEdges(port); 461 while (outEdges.hasNext()) { 462 Object obj = outEdges.next(); 463 selectedEdges.add(obj); 464 selection.add(controller.getFigure(obj)); 465 } 466 Iterator<?> inEdges = graphModel.inEdges(port); 467 while (inEdges.hasNext()) { 468 Object obj = inEdges.next(); 469 selectedEdges.add(obj); 470 selection.add(controller.getFigure(obj)); 471 } 472 } 473 } 474 } else if (graphModel.isEdge(userObject)) { 475 selectedEdges.add(userObject); 476 } 477 } 478 } 479 480 int i = 0; 481 for (Object userObject : selectedEdges) { 482 assert graphModel.isEdge(userObject); 483 // Check to see if the head and tail are both being 484 // selected. 485 Object head = graphModel.getHead(userObject); 486 487 // System.out.println("head:" +((NamedObj)head).getName()); 488 Object tail = graphModel.getTail(userObject); 489 490 // System.out.println("tail:" +((NamedObj)tail).getName()); 491 boolean headOK = nodeSet.contains(head); 492 boolean tailOK = nodeSet.contains(tail); 493 Iterator<Object> objects = nodeSet.iterator(); 494 495 while (!(headOK && tailOK) && objects.hasNext()) { 496 Object object = objects.next(); 497 498 if (!headOK && GraphUtilities.isContainedNode(head, object, 499 graphModel)) { 500 headOK = true; 501 } 502 503 if (!tailOK && GraphUtilities.isContainedNode(tail, object, 504 graphModel)) { 505 tailOK = true; 506 } 507 } 508 509 // For the edges at the boundary. 510 if (!headOK && tailOK || headOK && !tailOK) { 511 512 LinkElementProperties headProperties = LinkElementProperties 513 .extractLinkProperties(head); 514 LinkElementProperties tailProperties = LinkElementProperties 515 .extractLinkProperties(tail); 516 517 if (headProperties.port == null 518 && tailProperties.port != null) { 519 //Swap head and tail 520 LinkElementProperties temp = headProperties; 521 headProperties = tailProperties; 522 tailProperties = temp; 523 } 524 525 IORelation relation = null; 526 527 boolean duplicateRelation = true; 528 if (headProperties.type == ElementInLinkType.RELATION) { 529 relation = (IORelation) graphModel 530 .getSemanticObject(headProperties.element); 531 duplicateRelation = false; 532 } else if (tailProperties.type == ElementInLinkType.RELATION) { 533 relation = (IORelation) graphModel 534 .getSemanticObject(tailProperties.element); 535 duplicateRelation = false; 536 } else { 537 relation = (IORelation) graphModel 538 .getSemanticObject(userObject); 539 duplicateRelation = true; 540 } 541 542 if (headProperties.port != null) { 543 ComponentEntity entity = (ComponentEntity) headProperties.port 544 .getContainer(); 545 String portName = "port_" + i; 546 boolean isInput = headProperties.port.isInput(); 547 boolean isOutput = headProperties.port.isOutput(); 548 newPorts.append("<port name=\"" + portName 549 + "\" class=\"ptolemy.actor.TypedIOPort" 550 + "\">\n"); 551 552 if (headProperties.port.isMultiport()) { 553 newPorts.append("<property name=\"multiport\"/>\n"); 554 } 555 556 if (namedObjSet.contains(entity)) { 557 // The port is inside the hierarchy. 558 // The relation must be outside. 559 // Create composite port. 560 if (isInput) { 561 newPorts.append("<property name=\"input\"/>"); 562 } 563 564 if (isOutput) { 565 newPorts.append("<property name=\"output\"/>"); 566 } 567 568 newPorts.append("\n</port>\n"); 569 570 // Create internal relation and links. 571 // Note we can only partially reuse 572 // the relation name, one original relation 573 // can be two internal relations. 574 String relationName = relation.getName() + "_" + i; 575 intRelations.append("<relation name=\"" 576 + relationName + "\" class=\"" 577 + "ptolemy.actor.TypedIORelation\"/>\n"); 578 intConnections.append( 579 "<link port=\"" + entity.getName() + "." 580 + headProperties.port.getName() 581 + "\" relation=\"" + relationName 582 + "\"/>\n"); 583 intConnections.append("<link port=\"" + portName 584 + "\" relation=\"" + relationName 585 + "\"/>\n"); 586 587 // Create external links. 588 if (duplicateRelation) { 589 extRelations.append("<relation name=\"" 590 + relation.getName() + "\" class=\"" 591 + "ptolemy.actor.TypedIORelation\"/>\n"); 592 593 ComponentEntity otherEntity = (ComponentEntity) tailProperties.port 594 .getContainer(); 595 596 if (otherEntity == container) { 597 // This is a boundary port at a higher level. 598 extConnections.append("<link port=\"" 599 + tailProperties.port.getName() 600 + "\" relation=\"" 601 + relation.getName() + "\"/>\n"); 602 } else { 603 extConnections.append("<link port=\"" 604 + otherEntity.getName() + "." 605 + tailProperties.port.getName() 606 + "\" relation=\"" 607 + relation.getName() + "\"/>\n"); 608 } 609 } 610 611 extConnections 612 .append("<link port=\"" + compositeActorName 613 + "." + portName + "\" relation=\"" 614 + relation.getName() + "\"/>\n"); 615 } else { 616 // The port is outside the hierarchy. 617 // The relation must be inside. 618 if (isInput 619 && headProperties.type == ElementInLinkType.PORT_IN_ACTOR 620 || isOutput 621 && headProperties.type == ElementInLinkType.STANDALONE_PORT) { 622 newPorts.append("<property name=\"output\"/>"); 623 } 624 625 if (isOutput 626 && headProperties.type == ElementInLinkType.PORT_IN_ACTOR 627 || isInput 628 && headProperties.type == ElementInLinkType.STANDALONE_PORT) { 629 newPorts.append("<property name=\"input\"/>"); 630 } 631 632 newPorts.append("\n</port>\n"); 633 634 String relationName = relation.getName() + "_" + i; 635 extRelations.append("<relation name=\"" 636 + relationName + "\" class=\"" 637 + "ptolemy.actor.TypedIORelation\"/>\n"); 638 String entityPrefix = ""; 639 if (getModel() != entity) { 640 entityPrefix = entity.getName() + "."; 641 } 642 extConnections.append("<link port=\"" + entityPrefix 643 + headProperties.port.getName() 644 + "\" relation=\"" + relationName 645 + "\"/>\n"); 646 extConnections 647 .append("<link port=\"" + compositeActorName 648 + "." + portName + "\" relation=\"" 649 + relationName + "\"/>\n"); 650 651 // Create external links. 652 if (duplicateRelation) { 653 intRelations.append("<relation name=\"" 654 + relation.getName() + "\" class=\"" 655 + "ptolemy.actor.TypedIORelation\"/>\n"); 656 657 ComponentEntity otherEntity = (ComponentEntity) tailProperties.port 658 .getContainer(); 659 660 String otherEntityPrefix = ""; 661 if (getModel() != otherEntity) { 662 otherEntityPrefix = otherEntity.getName() 663 + "."; 664 } 665 666 intConnections.append("<link port=\"" 667 + otherEntityPrefix 668 + tailProperties.port.getName() 669 + "\" relation=\"" + relation.getName() 670 + "\"/>\n"); 671 } 672 673 intConnections.append("<link port=\"" + portName 674 + "\" relation=\"" + relation.getName() 675 + "\"/>\n"); 676 } 677 } 678 // } else if (!headOK && !tailOK) { 679 // // We only selected an edge. Build one input 680 // // port, one output port for it, and build 681 // // a direct connection. 682 } 683 ++i; 684 } 685 686 // System.out.println(" new ports:" + newPorts); 687 688 // Create the MoML command. 689 StringBuffer moml = new StringBuffer(); 690 691 // If the dropObj defers to something else, then we 692 // have to check the parent of the object 693 // for import attributes, and then we have to 694 // generate import statements. Note that everything 695 // imported by the parent will be imported now by 696 // the object into which this is dropped. 697 moml.append("<group>\n"); 698 699 // Copy the selection, then get it from the clipboard 700 // and insert its MoML description in the new composite. 701 // This must be done before the call to _deleteMoML(), 702 // which clears the selection. 703 String selectionMoML; 704 copy(); 705 Clipboard clipboard = java.awt.Toolkit.getDefaultToolkit() 706 .getSystemClipboard(); 707 Transferable transferable = clipboard.getContents(this); 708 try { 709 selectionMoML = (String) transferable 710 .getTransferData(DataFlavor.stringFlavor); 711 } catch (Exception ex) { 712 throw new InternalErrorException(null, ex, 713 "Getting data from clipboard failed."); 714 } 715 716 // Generate the MoML to carry out the deletion. 717 718 moml.append(_deleteMoML(graphModel, 719 selection.toArray(new Object[selection.size()]), model)); 720 721 moml.append("<entity name=\"" + compositeActorName 722 + "\" class=\"ptolemy.actor.TypedCompositeActor\">\n"); 723 moml.append("\t<property name=\"_location\" class=\"" 724 + "ptolemy.kernel.util.Location\" value=\"" + location[0] 725 + ", " + location[1] + "\">\n"); 726 moml.append("\t</property>\n"); 727 moml.append(newPorts); 728 729 moml.append(selectionMoML); 730 731 // Internal relations and connections. 732 moml.append(intRelations); 733 moml.append(intConnections); 734 moml.append("</entity>\n"); 735 736 // External relations and connections. 737 moml.append(extRelations); 738 moml.append(extConnections); 739 740 moml.append("</group>\n"); 741 742 // System.out.println(moml.toString()); 743 744 MoMLChangeRequest request = null; 745 request = new MoMLChangeRequest(this, container, moml.toString()); 746 request.setUndoable(true); 747 748 container.requestChange(request); 749 NamedObj newObject = compositeActor.getEntity(compositeActorName); 750 // Kepler wants a different icon. 751 IconLoader _iconLoader = MoMLParser.getIconLoader(); 752 if (_iconLoader != null) { 753 _iconLoader.loadIconForClass( 754 "ptolemy.actor.TypedCompositeActor", newObject); 755 } 756 } catch (Throwable throwable) { 757 MessageHandler.error("Creating hierarchy failed", throwable); 758 } 759 } 760 761 /** Remove the currently selected objects from this document, if any, 762 * and place them on the clipboard. 763 */ 764 public void cut() { 765 copy(); 766 delete(); 767 } 768 769 /** Delete the currently selected objects from this document. 770 */ 771 public void delete() { 772 // Note that we previously a delete was handled at the model level. 773 // Now a delete is handled by generating MoML to carry out the delete 774 // and handing that MoML to the parser 775 GraphController controller = _getGraphController(); 776 SelectionModel model = controller.getSelectionModel(); 777 AbstractBasicGraphModel graphModel = (AbstractBasicGraphModel) controller 778 .getGraphModel(); 779 Object[] selection = model.getSelectionAsArray(); 780 781 // Used by Kepler's Comad. 782 selection = BasicGraphFrameExtension.filterDeletedObjects(graphModel, 783 selection); 784 785 // Generate the MoML to carry out the deletion 786 StringBuffer moml = _deleteMoML(graphModel, selection, model); 787 788 BasicGraphFrameExtension.filterDeleteMoml(graphModel, selection, moml); 789 790 // Next process the deletion MoML. This should be the large majority 791 // of most deletions. 792 try { 793 // Finally create and request the change 794 NamedObj container = graphModel.getPtolemyModel(); 795 MoMLChangeRequest change = new MoMLChangeRequest(this, container, 796 moml.toString()); 797 change.setUndoable(true); 798 container.requestChange(change); 799 BasicGraphFrameExtension.alternateDelete(selection, graphModel, 800 container); 801 } catch (Exception ex) { 802 MessageHandler.error("Delete failed, changeRequest was:" + moml, 803 ex); 804 } 805 806 graphModel.dispatchGraphEvent(new GraphEvent(this, 807 GraphEvent.STRUCTURE_CHANGED, graphModel.getRoot())); 808 } 809 810 /** Dispose of this frame. 811 * Override this dispose() method to unattach any listeners that may keep 812 * this model from getting garbage collected. This method calls 813 * {@link #disposeSuper()}. 814 */ 815 @Override 816 public void dispose() { 817 if (_debugClosing) { 818 System.out.println("BasicGraphFrame.dispose() : " + getName()); 819 } 820 821 // Remove the association with the library. This is necessary to allow 822 // this frame, and the rest of the model to be properly garbage 823 // collected 824 if (_libraryModel != null) { 825 _libraryModel.setRoot(null); 826 } 827 _openGraphFrames.remove(this); 828 829 if (_jgraph != null) { 830 GraphPane pane = _jgraph.getGraphPane(); 831 EventLayer foregroundEventLayer = pane.getForegroundEventLayer(); 832 foregroundEventLayer.removeLayerListener(_mousePressedLayerAdapter); 833 } 834 835 if (_findInLibraryEntryBox != null) { 836 ActionListener[] listeners = _findInLibraryEntryBox 837 .getActionListeners(); 838 if (listeners != null) { 839 for (ActionListener listener : listeners) { 840 //System.out.println("BGF.dispose(): _findInLibraryEntryBox: Removing " + listener); 841 _findInLibraryEntryBox.removeActionListener(listener); 842 } 843 } 844 } 845 //int removed = 846 MemoryCleaner.removeActionListeners(_toolbar); 847 //System.out.println("BasicGraphFrame toolbar action listeners removed: " + removed); 848 849 NamedObj model = getModel(); 850 if (model != null) { 851 // SaveAs of an ontology solver resulted in a NPE. 852 getModel().removeChangeListener(this); 853 } 854 855 if (_rightComponent != null) { 856 _rightComponent.removeMouseWheelListener(this); 857 _rightComponent.removeMouseMotionListener(this); 858 _rightComponent.removeMouseListener(this); 859 860 // Free up BasicGraphFrame$MoveToFrontAction. 861 MemoryCleaner.removeActionListeners(_rightComponent); 862 } 863 864 if (_libraryContextMenuCreator != null) { 865 _libraryContextMenuCreator.clear(); 866 } 867 868 _mousePressedLayerAdapter = null; 869 870 if (_treeView != null) { 871 MouseListener mouseListeners[] = _treeView.getMouseListeners(); 872 for (int i = 0; i < mouseListeners.length; i++) { 873 _treeView.removeMouseListener(mouseListeners[i]); 874 } 875 876 // Free up BasicGraphFrame$HierarchyTreeCellRenderer. 877 MemoryCleaner.removeActionListeners(_treeView); 878 _treeView.setCellRenderer(null); 879 // See http://stackoverflow.com/questions/4517931/java-swing-jtree-is-not-garbage-collected 880 _treeView.setModel(null); 881 _treeView.setUI(null); 882 _treeView = null; 883 // See https://wiki.eecs.berkeley.edu/ptexternal/Main/Main/MemoryLeaks#ManagerAgain_treeViewModel 884 _treeViewModel = null; 885 } 886 887 // Top.dispose() sets all the AbstractAction to null. 888 disposeSuper(); 889 } 890 891 /** Invoke the dispose() method of the superclass, 892 * {@link ptolemy.actor.gui.PtolemyFrame}. 893 */ 894 public void disposeSuper() { 895 if (_debugClosing) { 896 System.out.println("BasicGraphFrame.disposeSuper() : " + getName()); 897 } 898 899 // This method is used by Kepler for the tabbed pane interface. 900 super.dispose(); 901 } 902 903 /** Expand all the rows of the library. 904 * Expanding all the rows is useful for testing. 905 */ 906 @Override 907 public void expandAllLibraryRows() { 908 for (int i = 0; i < _library.getRowCount(); i++) { 909 _library.expandRow(i); 910 } 911 } 912 913 /** Export the current submodel as a design pattern using a method similar to 914 * Save As. 915 */ 916 public void exportDesignPattern() { 917 StringAttribute alternateGetMoml = null; 918 DesignPatternIcon icon = null; 919 try { 920 NamedObj model = getModel(); 921 try { 922 if (model.getAttribute("_alternateGetMomlAction") == null) { 923 alternateGetMoml = new StringAttribute(model, 924 "_alternateGetMomlAction"); 925 alternateGetMoml.setExpression( 926 DesignPatternGetMoMLAction.class.getName()); 927 } 928 929 if (model.getAttribute("_designPatternIcon") == null) { 930 icon = new DesignPatternIcon(model, "_designPatternIcon"); 931 } 932 } catch (Exception e) { 933 throw new InternalErrorException(null, e, 934 "Fail to prepare " + "for exporting a design pattern."); 935 } 936 937 _prepareExportDesignPattern(); 938 _saveAs(); 939 } finally { 940 _finishExportDesignPattern(); 941 942 if (alternateGetMoml != null) { 943 try { 944 alternateGetMoml.setContainer(null); 945 } catch (KernelException e) { 946 // Ignore. This shouldn't happen. 947 } 948 } 949 if (icon != null) { 950 try { 951 icon.setContainer(null); 952 } catch (KernelException e) { 953 // Ignore. This shouldn't happen. 954 } 955 } 956 } 957 } 958 959 /** Given a NamedObj, return the corresponding BasicGraphFrame. 960 * @param model The NamedObj for the model. See 961 * {@link ptolemy.actor.gui.ConfigurationApplication#openModel(String)} 962 * for a static method that returns the model 963 * @return The BasicGraphFrame that corresponds with the model or 964 * null if the model argument was null, the effigy for the model 965 * cannot be found or if the Effigy does not contain a Tableau. 966 */ 967 public static BasicGraphFrame getBasicGraphFrame(NamedObj model) { 968 if (model == null) { 969 return null; 970 } 971 // See PtolemyLayoutAction for similar code. 972 Effigy effigy = Configuration.findEffigy(model); 973 return getBasicGraphFrame(effigy); 974 } 975 976 /** Given an Effigy, return the corresponding BasicGraphFrame, if any. 977 * @param effigy The Effigy. To determine the Effigy of a 978 * NamedObj, use {@link ptolemy.actor.gui.Configuration#findEffigy(NamedObj)}. 979 * @return The BasicGraphFrame that corresponds with the Effigy 980 * or null if the Effigy does not contain a Tableau. 981 */ 982 public static BasicGraphFrame getBasicGraphFrame(Effigy effigy) { 983 if (effigy == null) { 984 return null; 985 } 986 List entities = effigy.entityList(Tableau.class); 987 if (entities == null) { 988 return null; 989 } 990 991 BasicGraphFrame frame = null; 992 Iterator tableaux = entities.iterator(); 993 while (tableaux.hasNext()) { 994 Tableau tableau = (Tableau) tableaux.next(); 995 if (tableau.getFrame() instanceof BasicGraphFrame) { 996 frame = (BasicGraphFrame) tableau.getFrame(); 997 break; 998 } 999 } 1000 return frame; 1001 } 1002 1003 /** Return the center location of the visible part of the pane. 1004 * @return The center of the visible part. 1005 * @see #setCenter(Point2D) 1006 */ 1007 public Point2D getCenter() { 1008 return _getCenter(getJGraph()); 1009 } 1010 1011 /** Return the size of the contents of this window. 1012 * @return The size of the contents. 1013 */ 1014 @Override 1015 public Dimension getContentSize() { 1016 return getJGraph().getSize(); 1017 } 1018 1019 /** Return the figure that is an icon of a NamedObj and is 1020 * under the specified point, or null if there is none. 1021 * The point argument may need to be transformed, see 1022 * {@link ptolemy.vergil.basic.EditorDropTargetListener#_getFigureUnder(Point2D)}. 1023 * 1024 * @param pane The pane in which to search 1025 * @param point The point in the graph pane. 1026 * @param filteredFigures figures that are filtered from the object search 1027 * @return The object under the specified point, or null if there 1028 * is none or it is not a NamedObj. 1029 */ 1030 public static Figure getFigureUnder(GraphPane pane, Point2D point, 1031 final Object[] filteredFigures) { 1032 1033 FigureLayer layer = pane.getForegroundLayer(); 1034 1035 // Find the figure under the point. 1036 // NOTE: Unfortunately, FigureLayer.getCurrentFigure() doesn't 1037 // work with a drop target (I guess it hasn't seen the mouse events), 1038 // so we have to use a lower level mechanism. 1039 double halo = layer.getPickHalo(); 1040 double width = halo * 2; 1041 Rectangle2D region = new Rectangle2D.Double(point.getX() - halo, 1042 point.getY() - halo, width, width); 1043 // Filter away all figures given by the filteredFigures array 1044 CanvasComponent figureUnderMouse = layer.pick(region, new Filter() { 1045 @Override 1046 public boolean accept(Object o) { 1047 for (Object filter : filteredFigures) { 1048 CanvasComponent figure = (CanvasComponent) o; 1049 while (figure != null) { 1050 if (figure.equals(filter)) { 1051 return false; 1052 } 1053 figure = figure.getParent(); 1054 } 1055 } 1056 return true; 1057 } 1058 }); 1059 1060 // Find a user object belonging to the figure under the mouse 1061 // or to any figure containing it (it may be a composite figure). 1062 Object objectUnderMouse = null; 1063 1064 while (figureUnderMouse instanceof UserObjectContainer 1065 && objectUnderMouse == null) { 1066 objectUnderMouse = ((UserObjectContainer) figureUnderMouse) 1067 .getUserObject(); 1068 1069 if (objectUnderMouse instanceof NamedObj) { 1070 if (figureUnderMouse instanceof Figure) { 1071 return (Figure) figureUnderMouse; 1072 } 1073 } 1074 1075 figureUnderMouse = figureUnderMouse.getParent(); 1076 } 1077 1078 return null; 1079 } 1080 1081 /** The frame (window) being exported to HTML. 1082 * @return This frame. 1083 */ 1084 public PtolemyFrame getFrame() { 1085 return this; 1086 } 1087 1088 /** Return the JGraph instance that this view uses to represent the 1089 * ptolemy model. 1090 * @return the JGraph. 1091 * @see #setJGraph(JGraph) 1092 */ 1093 public JGraph getJGraph() { 1094 return _jgraph; 1095 } 1096 1097 /** Return the JCanvasPanner instance. 1098 * @return the JCanvasPanner 1099 */ 1100 public JCanvasPanner getGraphPanner() { 1101 return _graphPanner; 1102 } 1103 1104 /** Get the directory that was last accessed. 1105 * @return The last directory 1106 * @see #setLastDirectory(File) 1107 */ 1108 public File getLastDirectory() { 1109 return _directory; 1110 } 1111 1112 /** Set the directory that was last accessed by this window. 1113 * @see #getLastDirectory() 1114 * @param directory The directory last accessed. 1115 */ 1116 public void setLastDirectory(File directory) { 1117 // NOTE: This method is necessary because we wish to have 1118 // this accessed by inner classes, and there is a bug in 1119 // jdk1.2.2 where inner classes cannot access protected 1120 // static members. 1121 setDirectory(directory); 1122 } 1123 1124 /** Return a set of instances of NamedObj representing the objects 1125 * that are currently selected. This set has no particular order 1126 * to it. If you need the selection objects in proper order, as 1127 * defined by the container, then call sortContainedObjects() 1128 * on the container to sort the result. 1129 * @return The set of selected objects. 1130 */ 1131 public HashSet<NamedObj> getSelectionSet() { 1132 return _getSelectionSet(); 1133 } 1134 1135 /** Return the rectangle representing the visible part of the 1136 * pane, transformed into canvas coordinates. This is the range 1137 * of locations that are visible, given the current pan and zoom. 1138 * @return The rectangle representing the visible part. 1139 */ 1140 public Rectangle2D getVisibleCanvasRectangle() { 1141 return _getVisibleCanvasRectangle(getJGraph()); 1142 } 1143 1144 /** Return the rectangle representing the visible part of the 1145 * pane, in pixel coordinates on the screen. 1146 * @return A rectangle whose upper left corner is at (0, 0) and whose 1147 * size is the size of the canvas component. 1148 */ 1149 public Rectangle2D getVisibleRectangle() { 1150 return _getVisibleRectangle(getJGraph()); 1151 } 1152 1153 /** Import a design pattern into the current design. 1154 */ 1155 public void importDesignPattern() { 1156 JFileChooserBugFix jFileChooserBugFix = new JFileChooserBugFix(); 1157 Color background = null; 1158 PtFileChooser ptFileChooser = null; 1159 1160 try { 1161 background = jFileChooserBugFix.saveBackground(); 1162 ptFileChooser = new PtFileChooser(this, 1163 "Select a design pattern file.", JFileChooser.OPEN_DIALOG); 1164 //if (_fileFilter != null) { 1165 // ptFileChooser.addChoosableFileFilter(_fileFilter); 1166 //} 1167 1168 ptFileChooser.setCurrentDirectory(_directory); 1169 1170 int returnVal = ptFileChooser.showDialog(this, "Import"); 1171 1172 if (returnVal == JFileChooser.APPROVE_OPTION) { 1173 Top.setDirectory(ptFileChooser.getCurrentDirectory()); 1174 NamedObj model = null; 1175 File file = null; 1176 try { 1177 file = ptFileChooser.getSelectedFile().getCanonicalFile(); 1178 URL url = file.toURI().toURL(); 1179 MoMLParser parser = new MoMLParser(); 1180 MoMLParser.purgeModelRecord(url); 1181 model = parser.parse(url, url); 1182 MoMLParser.purgeModelRecord(url); 1183 } catch (Exception e) { 1184 report(new IllegalActionException(getModel(), e, 1185 "Error reading input file \"" + file + "\".")); 1186 } 1187 if (model != null) { 1188 Attribute attribute = model 1189 .getAttribute("_alternateGetMomlAction"); 1190 String className = DesignPatternGetMoMLAction.class 1191 .getName(); 1192 if (attribute == null 1193 || !(attribute instanceof StringAttribute) 1194 || !((StringAttribute) attribute).getExpression() 1195 .equals(className)) { 1196 report(new IllegalActionException("The model \"" + file 1197 + "\" is not a design pattern.")); 1198 } else { 1199 String moml = new DesignPatternGetMoMLAction() 1200 .getMoml(model, model.getName()); 1201 NamedObj context = getModel(); 1202 MoMLChangeRequest request = new MoMLChangeRequest(this, 1203 context, moml); 1204 context.requestChange(request); 1205 } 1206 } 1207 } 1208 } finally { 1209 jFileChooserBugFix.restoreBackground(background); 1210 } 1211 } 1212 1213 /** Do nothing. 1214 */ 1215 @Override 1216 public void lostOwnership(Clipboard clipboard, Transferable transferable) { 1217 } 1218 1219 /** Open the container, if any, of the entity. 1220 * If this entity has no container, then do nothing. 1221 */ 1222 public void openContainer() { 1223 GraphModel model = _getGraphModel(); 1224 NamedObj thisEntity = (NamedObj) model.getRoot(); 1225 if (thisEntity != thisEntity.toplevel()) { 1226 // Not already at the top level. 1227 try { 1228 // See whether the container contains an instance of LevelSkippingTableauFactory. 1229 NamedObj container = thisEntity.getContainer(); 1230 List<LevelSkippingTableauFactory> skip = container 1231 .attributeList(LevelSkippingTableauFactory.class); 1232 while (skip != null && skip.size() > 0) { 1233 container = container.getContainer(); 1234 if (container == null) { 1235 // This should not occur. 1236 return; 1237 } 1238 skip = container 1239 .attributeList(LevelSkippingTableauFactory.class); 1240 } 1241 // If the container is a ModalModel, the also skip a level. 1242 // Note that it is not enough to just check whether the name of the 1243 // class matches ModalModel, because then subclasses of ModalModel 1244 // are not recognized. This unfortunately creates a new dependence 1245 // on the modal package. 1246 if (thisEntity instanceof Actor) { 1247 Director director = ((Actor) thisEntity).getDirector(); 1248 if (director instanceof QuasiTransparentDirector) { 1249 if (thisEntity.getName().equals("_Controller")) { 1250 container = container.getContainer(); 1251 if (container == null) { 1252 // This should not occur. 1253 return; 1254 } 1255 } 1256 } 1257 } 1258 1259 Configuration configuration = getConfiguration(); 1260 // FIXME: do what with the return value? 1261 configuration.openInstance(container); 1262 } catch (Throwable throwable) { 1263 MessageHandler.error("Failed to open container", throwable); 1264 } 1265 } 1266 } 1267 1268 /** Opens the nearest composite actor above the target in the hierarchy 1269 * and possibly change the zoom and centering to show the target. 1270 * This method is useful for displaying search results and actors that 1271 * cause errors. 1272 * @param target The target. 1273 * @param owner The frame that, per the user, is generating the dialog. 1274 */ 1275 public static void openComposite(final Frame owner, final NamedObj target) { 1276 // This method is static so that it 1277 NamedObj container = target.getContainer(); 1278 while (container != null && !(container instanceof CompositeEntity)) { 1279 container = container.getContainer(); 1280 } 1281 if (container == null) { 1282 // Hmm. Could not find container? 1283 container = target; 1284 } 1285 try { 1286 if (owner != null) { 1287 report(owner, "Opening " + container.getFullName()); 1288 } 1289 Effigy effigy = Configuration.findEffigy(target.toplevel()); 1290 if (effigy == null) { 1291 throw new IllegalActionException(target, 1292 "Failed to find an " + "effigy for the toplevel " 1293 + target.toplevel().getFullName()); 1294 } 1295 Configuration configuration = (Configuration) effigy.toplevel(); 1296 Tableau tableau = configuration.openInstance(container); 1297 1298 // Try to zoom and center on the target. 1299 1300 // Get the _location attribute. If it is null, then maybe 1301 // this is a parameter in an actor so go up the hierarchy 1302 // until we get to the container we found above or we find 1303 // a non-null location attribute. 1304 Location locationAttribute = (Location) target 1305 .getAttribute("_location", Location.class); 1306 if (locationAttribute == null) { 1307 NamedObj targetContainer = target.getContainer(); 1308 while (targetContainer != null 1309 && (locationAttribute = (Location) targetContainer 1310 .getAttribute("_location", 1311 Location.class)) == null) { 1312 // FindBugs: Load of known null value. locationAttribute is always null here. 1313 // The break is unnecessary as if locationAttribute is non-null, then 1314 // the body of the while loop is not executed. 1315 //if (locationAttribute != null 1316 if (targetContainer.equals(container)) { 1317 break; 1318 } 1319 targetContainer = targetContainer.getContainer(); 1320 } 1321 } 1322 if (locationAttribute != null) { 1323 Frame frame = tableau.getFrame(); 1324 if (frame instanceof BasicGraphFrame) { 1325 BasicGraphFrame basicGraphFrame = (BasicGraphFrame) frame; 1326 1327 double[] locationArray = locationAttribute.getLocation(); 1328 Point2D locationPoint2D = new Point2D.Double( 1329 locationArray[0], locationArray[1]); 1330 1331 GraphPane pane = basicGraphFrame.getJGraph().getGraphPane(); 1332 1333 // The value returned by Rectangle2D.outcode() 1334 int outcode = 0; 1335 Figure figure = BasicGraphFrame.getFigureUnder(pane, 1336 locationPoint2D, new Object[] {}); 1337 if (figure == null) { 1338 // If we can't find the figure, then force zoom and center. 1339 // I'm not sure if this can ever happen, but it might help 1340 outcode = 666; 1341 } else { 1342 Rectangle2D figureBounds = figure.getBounds(); 1343 Rectangle2D canvasBounds = basicGraphFrame 1344 .getVisibleCanvasRectangle(); 1345 outcode = canvasBounds.outcode(figureBounds.getX(), 1346 figureBounds.getY()); 1347 //basicGraphFrame.zoomFit(pane, figureBounds); 1348 //basicGraphFrame.zoom(0.6); 1349 } 1350 1351 // Get the scale, assume that the scaling in the X 1352 // and Y directions are the same. 1353 AffineTransform current = pane.getCanvas().getCanvasPane() 1354 .getTransformContext().getTransform(); 1355 double scale = current.getScaleX(); 1356 if (scale < 0.8 || scale > 2.0 || outcode != 0) { 1357 // Only reset the zoom if the would be difficult to see 1358 // the component or if the component is not visible. 1359 basicGraphFrame.zoomReset(); 1360 basicGraphFrame.setCenter(locationPoint2D); 1361 } 1362 } 1363 } 1364 if (owner != null) { 1365 report(owner, "Opened " + container.getFullName()); 1366 } 1367 } catch (Throwable throwable) { 1368 MessageHandler.error("Failed to open container", throwable); 1369 } 1370 } 1371 1372 /** Assuming the contents of the clipboard is MoML code, paste it into 1373 * the current model by issuing a change request. 1374 */ 1375 public void paste() { 1376 Clipboard clipboard = java.awt.Toolkit.getDefaultToolkit() 1377 .getSystemClipboard(); 1378 Transferable transferable = clipboard.getContents(this); 1379 GraphModel model = _getGraphModel(); 1380 1381 if (transferable == null) { 1382 return; 1383 } 1384 1385 try { 1386 NamedObj container = (NamedObj) model.getRoot(); 1387 StringBuffer moml = new StringBuffer(); 1388 1389 // The pasted version will have the names generated by the 1390 // uniqueName() method of the container, to ensure that they 1391 // do not collide with objects already in the container. 1392 moml.append("<group name=\"auto\">\n"); 1393 //moml.append("<group>\n"); 1394 moml.append((String) transferable 1395 .getTransferData(DataFlavor.stringFlavor)); 1396 1397 // Needed by Kepler's Comad. 1398 BasicGraphFrameExtension.alternatePasteMomlModification(container, 1399 moml); 1400 1401 moml.append("</group>\n"); 1402 1403 MoMLChangeRequest change = new OffsetMoMLChangeRequest(this, 1404 container, moml.toString()); 1405 change.setUndoable(true); 1406 container.requestChange(change); 1407 1408 // Added by Lei Dou to update the signature for Kepler/Comad 1409 BasicGraphFrameExtension.alternatePaste(container, moml); 1410 } catch (Exception ex) { 1411 MessageHandler.error("Paste failed", ex); 1412 } 1413 } 1414 1415 /** Print the visible portion of the graph to a printer, 1416 * which is represented by the specified graphics object. 1417 * @param graphics The context into which the page is drawn. 1418 * @param format The size and orientation of the page being drawn. 1419 * @param index The zero based index of the page to be drawn. 1420 * @return PAGE_EXISTS if the page is rendered successfully, or 1421 * NO_SUCH_PAGE if pageIndex specifies a non-existent page. 1422 * @exception PrinterException If the print job is terminated. 1423 */ 1424 @Override 1425 public int print(Graphics graphics, PageFormat format, int index) 1426 throws PrinterException { 1427 if (getJGraph() != null) { 1428 Rectangle2D view = getVisibleRectangle(); 1429 return getJGraph().print(graphics, format, index, view); 1430 } else { 1431 return NO_SUCH_PAGE; 1432 } 1433 } 1434 1435 /** Redo the last undone change on the model. 1436 * @see #undo() 1437 */ 1438 public void redo() { 1439 GraphModel model = _getGraphModel(); 1440 1441 try { 1442 NamedObj toplevel = (NamedObj) model.getRoot(); 1443 RedoChangeRequest change = new RedoChangeRequest(this, toplevel); 1444 toplevel.requestChange(change); 1445 } catch (Exception ex) { 1446 MessageHandler.error("Redo failed", ex); 1447 } 1448 } 1449 1450 // /** Open a file browser and save the given entity in the file specified 1451 // * by the user. 1452 // * @param entity The entity to save. 1453 // * @exception Exception If there is a problem saving the component. 1454 // * @since Ptolemy 4.0 1455 // */ 1456 // public void saveComponentInFile(Entity entity) throws Exception { 1457 // // FIXME: This method is probably no 1458 // // NOTE: This mirrors similar code in Top and TableauFrame, but 1459 // // I can't find any way to re-use that code, since the details 1460 // // are slightly different at each step here. 1461 1462 // JFileChooserBugFix jFileChooserBugFix = new JFileChooserBugFix(); 1463 // Color background = null; 1464 // PtFileChooser ptFileChooser = null; 1465 1466 // try { 1467 // background = jFileChooserBugFix.saveBackground(); 1468 // ptFileChooser = new PtFileChooser(this, 1469 // "Save Component as...", 1470 // JFileChooser.SAVE_DIALOG); 1471 // ptFileChooser.setCurrentDirectory(_directory); 1472 // // Hmm, is getCurrentDirectory necessary here? 1473 // ptFileChooser.setSelectedFile(new File(ptFileChooser.getCurrentDirectory(), 1474 // entity.getName() + ".xml")); 1475 1476 // int returnVal = ptFileChooser.showDialog(this, 1477 // "Save"); 1478 // if (returnVal == JFileChooser.APPROVE_OPTION) { 1479 // // We set _directory below. 1480 // File file = ptFileChooser.getSelectedFile(); 1481 1482 // if (!_confirmFile(entity, file)) { 1483 // return; 1484 // } 1485 1486 // // Record the selected directory. 1487 // _directory = ptFileChooser.getCurrentDirectory(); 1488 1489 // java.io.FileWriter fileWriter = null; 1490 1491 // try { 1492 // fileWriter = new java.io.FileWriter(file); 1493 1494 // // Make sure the entity name saved matches the file name. 1495 // String name = entity.getName(); 1496 // String filename = file.getName(); 1497 // int period = filename.indexOf("."); 1498 1499 // if (period > 0) { 1500 // name = filename.substring(0, period); 1501 // } else { 1502 // name = filename; 1503 // } 1504 1505 // fileWriter.write("<?xml version=\"1.0\" standalone=\"no\"?>\n" 1506 // + "<!DOCTYPE " + entity.getElementName() + " PUBLIC " 1507 // + "\"-//UC Berkeley//DTD MoML 1//EN\"\n" 1508 // + " \"http://ptolemy.eecs.berkeley.edu" 1509 // + "/xml/dtd/MoML_1.dtd\">\n"); 1510 1511 // entity.exportMoML(fileWriter, 0, name); 1512 // } finally { 1513 // if (fileWriter != null) { 1514 // fileWriter.close(); 1515 // } 1516 // } 1517 // } 1518 // } finally { 1519 // jFileChooserBugFix.restoreBackground(background); 1520 // } 1521 // } 1522 1523 /** Report a message to either the status bar or message handler. 1524 * @param owner The frame that, per the user, is generating the 1525 * dialog. 1526 * @param message The message. 1527 */ 1528 public static void report(Frame owner, String message) { 1529 if (owner instanceof Top) { 1530 ((Top) owner).report(message); 1531 } else { 1532 MessageHandler.message(message); 1533 } 1534 } 1535 1536 /** Save the given entity in the user library in the given 1537 * configuration. 1538 * @param configuration The configuration. 1539 * @param entity The entity to save. 1540 * @since Ptolemy 2.1 1541 * @deprecated Use {@link ptolemy.actor.gui.UserActorLibrary#saveComponentInLibrary(Configuration, Entity)} 1542 */ 1543 @Deprecated 1544 public static void saveComponentInLibrary(Configuration configuration, 1545 Entity entity) { 1546 try { 1547 ptolemy.actor.gui.UserActorLibrary 1548 .saveComponentInLibrary(configuration, entity); 1549 } catch (Exception ex) { 1550 // We catch exceptions here because this method used to 1551 // not throw Exceptions, and we don't want to break compatibility. 1552 MessageHandler 1553 .error("Failed to save \"" + entity.getName() + "\"."); 1554 } 1555 } 1556 1557 /** Set the center location of the visible part of the pane. 1558 * This will cause the panner to center on the specified location 1559 * with the current zoom factor. 1560 * @param center The center of the visible part. 1561 * @see #getCenter() 1562 */ 1563 public void setCenter(Point2D center) { 1564 _setCenter(getJGraph(), center); 1565 } 1566 1567 /** Set the JGraph instance that this view uses to represent the 1568 * ptolemy model. 1569 * @param jgraph The JGraph. 1570 * @see #getJGraph() 1571 */ 1572 public void setJGraph(JGraph jgraph) { 1573 _jgraph = jgraph; 1574 } 1575 1576 /** Undo the last undoable change on the model. 1577 * @see #redo() 1578 */ 1579 public void undo() { 1580 GraphModel model = _getGraphModel(); 1581 1582 try { 1583 NamedObj toplevel = (NamedObj) model.getRoot(); 1584 UndoChangeRequest change = new UndoChangeRequest(this, toplevel); 1585 toplevel.requestChange(change); 1586 } catch (Exception ex) { 1587 MessageHandler.error("Undo failed", ex); 1588 } 1589 } 1590 1591 /** Update the size, zoom and position of the window. 1592 * This method is typically called when closing the window 1593 * or writing the moml file out. 1594 * @exception IllegalActionException If there is a problem 1595 * getting a parameter. 1596 * @exception NameDuplicationException If there is a problem 1597 * creating a parameter. 1598 */ 1599 public void updateWindowAttributes() 1600 throws IllegalActionException, NameDuplicationException { 1601 // First, record size and position. 1602 1603 // See "composite window size & position not always saved" 1604 // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5637 1605 1606 // Record the position of the top-level frame, assuming 1607 // there is one. 1608 Component component = _getRightComponent().getParent(); 1609 Component parent = component.getParent(); 1610 1611 while (parent != null && !(parent instanceof Frame)) { 1612 component = parent; 1613 parent = component.getParent(); 1614 } 1615 1616 // Oddly, sometimes getModel returns null? $PTII/bin/ptinvoke 1617 // ptolemy.vergil.basic.export.ExportModel -force htm -run 1618 // -openComposites -whiteBackground 1619 // ptolemy/actor/gt/demo/MapReduce/MapReduce.xml 1620 // $PTII/ptolemy/actor/gt/demo/MapReduce/MapReduce 1621 NamedObj model = getModel(); 1622 if (model != null) { 1623 _updateWindowAttributes((Frame) parent, model, getJGraph()); 1624 } 1625 } 1626 1627 /** Write an HTML page based on the current view of the model 1628 * to the specified destination directory. The file will be 1629 * named "index.html," and supporting files, including at 1630 * least a gif image showing the contents currently visible in 1631 * the graph frame, will be created. If there are any plot windows 1632 * open or any composite actors open, then gif and/or HTML will 1633 * be generated for those as well and linked to the gif image 1634 * created for this frame. 1635 * <p> 1636 * The generated page has a header with the name of the model, 1637 * a reference to a GIF image file with name equal to the name 1638 * of the model with a ".gif" extension appended, and a script 1639 * that reacts when the mouse is moved over an actor by 1640 * displaying a table with the parameter values of the actor. 1641 * The gif image is assumed to have been generated with the 1642 * current view using the {@link #writeImage(OutputStream, String)} 1643 * method. 1644 * @param parameters The parameters that control the export. 1645 * @param writer The writer to use the write the HTML. If this is null, 1646 * then create an index.html file in the 1647 * directory given by the directoryToExportTo field of the parameters. 1648 * @exception IOException If unable to write associated files, or if the 1649 * current configuration does not support it. 1650 * @exception PrinterException If unable to write associated files. 1651 * @exception IllegalActionException If something goes wrong accessing the model. 1652 */ 1653 @Override 1654 public void writeHTML(ExportParameters parameters, Writer writer) 1655 throws PrinterException, IOException, IllegalActionException { 1656 if (_exportHTMLAction != null) { 1657 ((HTMLExportable) _exportHTMLAction).writeHTML(parameters, writer); 1658 } else { 1659 throw new IOException( 1660 "Export to Web not supported. Probably the configuration does not have a _exportHTMLActionClassName parameter or the class named by that parameter is not present."); 1661 } 1662 } 1663 1664 /** Write an image to the specified output stream in the specified format. 1665 * Supported formats include at least "gif" and "png", standard image file formats. 1666 * The image is a rendition of the current view of the model. 1667 * <p>{@link ptolemy.vergil.basic.export.ExportModel} is a standalone class 1668 * that exports an image of a model. 1669 * @param stream The output stream to write to. 1670 * @param format The image format to generate. 1671 * @see #writeHTML(ExportParameters, Writer) 1672 * @exception IOException If writing to the stream fails. 1673 * @exception PrinterException If the specified format is not supported. 1674 */ 1675 @Override 1676 public void writeImage(OutputStream stream, String format) 1677 throws PrinterException, IOException { 1678 writeImage(stream, format, null); 1679 } 1680 1681 /** Write an image to the specified output stream in the specified format with 1682 * the specified background color. 1683 * Supported formats include at least "gif" and "png", standard image file formats. 1684 * The image is a rendition of the current view of the model. 1685 * <p>{@link ptolemy.vergil.basic.export.ExportModel} is a standalone class 1686 * that exports an image of a model. 1687 * @param stream The output stream to write to. 1688 * @param format The image format to generate. 1689 * @param background The background color, or null to use the current color. 1690 * @see #writeHTML(ExportParameters, Writer) 1691 * @exception IOException If writing to the stream fails. 1692 * @exception PrinterException If the specified format is not supported. 1693 */ 1694 public void writeImage(OutputStream stream, String format, Color background) 1695 throws PrinterException, IOException { 1696 JCanvas canvas = getJGraph().getGraphPane().getCanvas(); 1697 Color previousBackground = canvas.getBackground(); 1698 try { 1699 if (background != null) { 1700 canvas.setBackground(background); 1701 } 1702 getJGraph().exportImage(stream, format); 1703 } finally { 1704 if (background != null) { 1705 canvas.setBackground(previousBackground); 1706 } 1707 } 1708 } 1709 1710 /** Zoom in or out to magnify by the specified factor, from the current 1711 * magnification. 1712 * @param factor The magnification factor (relative to 1.0). 1713 */ 1714 public void zoom(double factor) { 1715 _zoom(getJGraph(), factor); 1716 } 1717 1718 /** Zoom to fit the current figures. 1719 */ 1720 public void zoomFit() { 1721 GraphPane pane = getJGraph().getGraphPane(); 1722 Rectangle2D bounds = pane.getForegroundLayer().getLayerBounds(); 1723 zoomFit(pane, bounds); 1724 } 1725 1726 /** Zoom to fit the bounds. 1727 * @param pane The pane. 1728 * @param bounds The bound to zoom to. 1729 */ 1730 public void zoomFit(GraphPane pane, Rectangle2D bounds) { 1731 if (bounds.isEmpty()) { 1732 // Empty diagram. 1733 return; 1734 } 1735 1736 Rectangle2D viewSize = getVisibleRectangle(); 1737 Rectangle2D paddedViewSize = new Rectangle2D.Double( 1738 viewSize.getX() + _ZOOM_FIT_PADDING, 1739 viewSize.getY() + _ZOOM_FIT_PADDING, 1740 viewSize.getWidth() - 2 * _ZOOM_FIT_PADDING, 1741 viewSize.getHeight() - 2 * _ZOOM_FIT_PADDING); 1742 AffineTransform newTransform = CanvasUtilities 1743 .computeFitTransform(bounds, paddedViewSize); 1744 JCanvas canvas = pane.getCanvas(); 1745 canvas.getCanvasPane().setTransform(newTransform); 1746 1747 if (_graphPanner != null) { 1748 _graphPanner.repaint(); 1749 } 1750 } 1751 1752 /** Set zoom to the nominal. 1753 */ 1754 public void zoomReset() { 1755 JCanvas canvas = getJGraph().getGraphPane().getCanvas(); 1756 AffineTransform current = canvas.getCanvasPane().getTransformContext() 1757 .getTransform(); 1758 current.setToIdentity(); 1759 canvas.getCanvasPane().setTransform(current); 1760 1761 if (_graphPanner != null) { 1762 _graphPanner.repaint(); 1763 } 1764 } 1765 1766 /** 1767 * Called when the mouse is clicked. 1768 * This base class does nothing when the mouse is clicked. 1769 * However, events _are_ handled by the components within this component. 1770 * @param event The mouse event. 1771 */ 1772 @Override 1773 public void mouseClicked(MouseEvent event) { 1774 // Implementation of the MouseMotionListener interface. 1775 } 1776 1777 /** Transform the graph by the amount the mouse is dragged 1778 * while the middle mouse button is held down. 1779 * @param event The drag event. 1780 */ 1781 @Override 1782 public void mouseDragged(MouseEvent event) { 1783 // Implementation of the MouseMotionListener interface. 1784 // See https://chess.eecs.berkeley.edu/bugzilla/show_bug.cgi?id=73 1785 1786 if (event.isAltDown()) { 1787 // Only interested in middle button. (defined as the alt modifier) 1788 int deltaX = event.getX() - _previousMouseX; 1789 int deltaY = event.getY() - _previousMouseY; 1790 1791 AffineTransform newTransform = getJGraph().getCanvasPane() 1792 .getTransformContext().getTransform(); 1793 newTransform.translate(deltaX, deltaY); 1794 getJGraph().getCanvasPane().setTransform(newTransform); 1795 1796 _previousMouseX = event.getX(); 1797 _previousMouseY = event.getY(); 1798 event.consume(); 1799 } 1800 } 1801 1802 /** 1803 * Called when the mouse enters this component. 1804 * This base class does nothing when the enters this component. 1805 * However, events _are_ handled by the components within this component. 1806 * @param event The mouse event. 1807 */ 1808 @Override 1809 public void mouseEntered(MouseEvent event) { 1810 // Implementation of the MouseMotionListener interface. 1811 } 1812 1813 /** 1814 * Called when the mouse leaves this component. 1815 * This base class does nothing when the exits this component. 1816 * However, events _are_ handled by the components within this component. 1817 * @param event The mouse event. 1818 */ 1819 @Override 1820 public void mouseExited(MouseEvent event) { 1821 // Implementation of the MouseMotionListener interface. 1822 } 1823 1824 /** Called when the mouse is moved. 1825 * This base class does nothing when the mouse is moved. 1826 * @param event Contains details of the movement event. 1827 * However, events _are_ handled by the components within this component. 1828 */ 1829 @Override 1830 public void mouseMoved(MouseEvent event) { 1831 // Implementation of the MouseMotionListener interface. 1832 } 1833 1834 /** Store the location of the middle mouse event. 1835 * @param event The mouse event. 1836 */ 1837 @Override 1838 public void mousePressed(MouseEvent event) { 1839 if (event.isAltDown()) { 1840 // Only interested in middle button. (defined as the alt modifier) 1841 _previousMouseX = event.getX(); 1842 _previousMouseY = event.getY(); 1843 event.consume(); 1844 } 1845 } 1846 1847 /** 1848 * Called when the mouse is released. 1849 * This base class does nothing when the mouse is moved. 1850 * However, events _are_ handled by the components within this component. 1851 * @param event The mouse event. 1852 */ 1853 @Override 1854 public void mouseReleased(MouseEvent event) { 1855 // Implementation of the MouseMotionListener interface. 1856 } 1857 1858 /** Scroll in when the mouse wheel is moved. 1859 * @param event The mouse wheel event. 1860 */ 1861 @Override 1862 public void mouseWheelMoved(MouseWheelEvent event) { 1863 // Scrolling the wheel away from you zooms in. This is arbitrary and 1864 // should be configurable by the user. 1865 // 1866 // TODO: It would be nice to centre the zoom on where the 1867 // mouse is. That would mirror what apps like google earth do. 1868 1869 int notches = event.getWheelRotation(); 1870 double zoomFactor = 1.25; 1871 if (notches > 0) { 1872 zoomFactor = 1.0 / zoomFactor; 1873 } 1874 zoom(zoomFactor); 1875 } 1876 1877 /////////////////////////////////////////////////////////////////// 1878 //// public variables //// 1879 1880 /** Default background color is a light grey. */ 1881 public static final Color BACKGROUND_COLOR = new Color(0xe5e5e5); 1882 1883 /** The name of the user library. The default value is 1884 * "UserLibrary". The value of this variable is what appears 1885 * in the Vergil left hand tree menu. 1886 * @deprecated Use {@link ptolemy.actor.gui.UserActorLibrary#USER_LIBRARY_NAME} 1887 */ 1888 @Deprecated 1889 public static String VERGIL_USER_LIBRARY_NAME = UserActorLibrary.USER_LIBRARY_NAME; 1890 1891 /////////////////////////////////////////////////////////////////// 1892 //// protected methods //// 1893 1894 /** Add a layout menu. 1895 * @param graphMenu The menu to which to add the layout menu. 1896 */ 1897 protected void _addLayoutMenu(JMenu graphMenu) { 1898 // The layout action is created by BasicGraphFrame. 1899 if (_layoutAction != null) { 1900 // If we are running with -ptinyViewer, then the layout facility 1901 // might not be present. 1902 GUIUtilities.addHotKey(_getRightComponent(), _layoutAction); 1903 GUIUtilities.addMenuItem(graphMenu, _layoutAction); 1904 if (_layoutConfigDialogAction != null) { 1905 GUIUtilities.addMenuItem(graphMenu, _layoutConfigDialogAction); 1906 } 1907 graphMenu.addSeparator(); 1908 } 1909 } 1910 1911 /** Create the menus that are used by this frame. 1912 */ 1913 @Override 1914 protected void _addMenus() { 1915 super._addMenus(); 1916 1917 _editMenu = new JMenu("Edit"); 1918 _editMenu.setMnemonic(KeyEvent.VK_E); 1919 _menubar.add(_editMenu); 1920 1921 // Add the undo action, followed by a separator then the editing actions 1922 diva.gui.GUIUtilities.addHotKey(_getRightComponent(), _undoAction); 1923 diva.gui.GUIUtilities.addMenuItem(_editMenu, _undoAction); 1924 diva.gui.GUIUtilities.addHotKey(_getRightComponent(), _redoAction); 1925 diva.gui.GUIUtilities.addMenuItem(_editMenu, _redoAction); 1926 _editMenu.addSeparator(); 1927 GUIUtilities.addHotKey(_getRightComponent(), _cutAction); 1928 GUIUtilities.addMenuItem(_editMenu, _cutAction); 1929 GUIUtilities.addHotKey(_getRightComponent(), _copyAction); 1930 GUIUtilities.addMenuItem(_editMenu, _copyAction); 1931 GUIUtilities.addHotKey(_getRightComponent(), _pasteAction); 1932 GUIUtilities.addMenuItem(_editMenu, _pasteAction); 1933 1934 _editMenu.addSeparator(); 1935 1936 GUIUtilities.addHotKey(_getRightComponent(), _moveToBackAction); 1937 GUIUtilities.addMenuItem(_editMenu, _moveToBackAction); 1938 GUIUtilities.addHotKey(_getRightComponent(), _moveToFrontAction); 1939 GUIUtilities.addMenuItem(_editMenu, _moveToFrontAction); 1940 1941 _editMenu.addSeparator(); 1942 GUIUtilities.addMenuItem(_editMenu, _editPreferencesAction); 1943 1944 // Hot key for configure (edit parameters). 1945 GUIUtilities.addHotKey(_getRightComponent(), 1946 BasicGraphController._configureAction); 1947 1948 // May be null if there are not multiple views in the configuration. 1949 if (_viewMenu == null) { 1950 _viewMenu = new JMenu("View"); 1951 _viewMenu.setMnemonic(KeyEvent.VK_V); 1952 _menubar.add(_viewMenu); 1953 } else { 1954 _viewMenu.addSeparator(); 1955 } 1956 1957 GUIUtilities.addHotKey(_getRightComponent(), _zoomInAction); 1958 GUIUtilities.addMenuItem(_viewMenu, _zoomInAction); 1959 GUIUtilities.addHotKey(_getRightComponent(), _zoomResetAction); 1960 GUIUtilities.addMenuItem(_viewMenu, _zoomResetAction); 1961 GUIUtilities.addHotKey(_getRightComponent(), _zoomFitAction); 1962 GUIUtilities.addMenuItem(_viewMenu, _zoomFitAction); 1963 GUIUtilities.addHotKey(_getRightComponent(), _zoomOutAction); 1964 GUIUtilities.addMenuItem(_viewMenu, _zoomOutAction); 1965 1966 _graphMenu = new JMenu("Graph"); 1967 _graphMenu.setMnemonic(KeyEvent.VK_G); 1968 _menubar.add(_graphMenu); 1969 GUIUtilities.addHotKey(_getRightComponent(), _findAction); 1970 GUIUtilities.addMenuItem(_graphMenu, _findAction); 1971 } 1972 1973 /** Add a Reload Accessors menu choice. 1974 * @param graphMenu The menu to which to add the Reload Accessors 1975 * menu choice. 1976 */ 1977 protected void _addReloadAccessorsMenu(JMenu graphMenu) { 1978 // The action is created by BasicGraphFrame. 1979 if (_reloadAccessorsAction != null) { 1980 GUIUtilities.addMenuItem(graphMenu, _reloadAccessorsAction); 1981 graphMenu.addSeparator(); 1982 } 1983 } 1984 1985 /** Return true if any element of the specified list is implied. 1986 * An element is implied if its getDerivedLevel() method returns 1987 * anything smaller than Integer.MAX_VALUE. 1988 * @param elements A list of instances of NamedObj. 1989 * @return True if any element in the list is implied. 1990 * @see NamedObj#getDerivedLevel() 1991 */ 1992 protected boolean _checkForImplied(List<NamedObj> elements) { 1993 Iterator<NamedObj> elementIterator = elements.iterator(); 1994 1995 while (elementIterator.hasNext()) { 1996 NamedObj element = elementIterator.next(); 1997 1998 if (element.getDerivedLevel() < Integer.MAX_VALUE) { 1999 MessageHandler.error( 2000 "Cannot change the position of " + element.getFullName() 2001 + " because the position is set by the class."); 2002 return true; 2003 } 2004 } 2005 2006 return false; 2007 } 2008 2009 /** Override the base class to remove the listeners we have 2010 * created when the frame closes. Specifically, 2011 * remove our panner-updating listener from the entity. 2012 * Also remove the listeners our graph model has created. 2013 * @return True if the close completes, and false otherwise. 2014 */ 2015 @Override 2016 protected boolean _close() { 2017 if (_debugClosing) { 2018 System.out.println("BasicGraphFrame._close() : " + getName()); 2019 } 2020 2021 // See "composite window size & position not always saved" 2022 // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5637 2023 2024 // Don't update the _windowProperties attribute during _close() 2025 // For example, if the model is a large model and there is an 2026 // error and the user clicks on "Go To Actor", then the model 2027 // may be zoomed. When the user closes the model, they will 2028 // be prompted to save. Even worse, it appears that the size 2029 // and location of windows can be slightly different between 2030 // different platforms. 2031 2032 // try { 2033 // _updateWindowAttributes(); 2034 // } catch (KernelException ex) { 2035 // // Ignore problems here. Errors simply result in a default 2036 // // size and location. 2037 // System.out.println("While closing, failed to update size, position or zoom factor: " + ex); 2038 // } 2039 2040 boolean result = super._close(); 2041 2042 if (result) { 2043 AbstractBasicGraphModel graphModel = _getGraphModel(); 2044 graphModel.removeListeners(); 2045 } 2046 2047 return result; 2048 } 2049 2050 /** Create the default library to use if an entity has no 2051 * LibraryAttribute. Note that this is called in the 2052 * constructor and therefore overrides in subclasses 2053 * should not refer to any members that may not have been 2054 * initialized. If no library is found in the configuration, 2055 * then an empty one is created in the specified workspace. 2056 * @param workspace The workspace in which to create 2057 * the library, if one needs to be created. 2058 * @return The new library, or null if there is no 2059 * configuration. 2060 */ 2061 protected CompositeEntity _createDefaultLibrary(Workspace workspace) { 2062 Configuration configuration = getConfiguration(); 2063 2064 if (configuration != null) { 2065 CompositeEntity result = (CompositeEntity) configuration 2066 .getEntity("actor library"); 2067 2068 if (result == null) { 2069 // Create an empty library by default. 2070 result = new CompositeEntity(workspace); 2071 2072 try { 2073 result.setName("topLibrary"); 2074 2075 // Put a marker in so that this is 2076 // recognized as a library. 2077 new Attribute(result, "_libraryMarker"); 2078 } catch (Exception ex) { 2079 throw new InternalErrorException( 2080 "Library configuration failed: " + ex); 2081 } 2082 } 2083 2084 return result; 2085 } else { 2086 return null; 2087 } 2088 } 2089 2090 /** Create the items in the File menu's Export section 2091 * This method adds a menu items to export images of the plot 2092 * in GIF, PNG, and possibly PDF. 2093 * @return The items in the File menu. 2094 */ 2095 @Override 2096 protected JMenuItem[] _createFileMenuItems() { 2097 JMenuItem[] fileMenuItems = super._createFileMenuItems(); 2098 2099 JMenu importMenu = (JMenu) fileMenuItems[_IMPORT_MENU_INDEX]; 2100 importMenu.setEnabled(true); 2101 2102 JMenu exportMenu = (JMenu) fileMenuItems[_EXPORT_MENU_INDEX]; 2103 exportMenu.setEnabled(true); 2104 2105 // Get the "export PDF" action classname from the configuration. 2106 // This may or many not be included because it depends on GPL'd code, 2107 // and hence cannot be included included in any pure BSD distribution. 2108 // NOTE: Cannot use getConfiguration() because the configuration is 2109 // not set when this method is called. Hence, we assume that there 2110 // is only one configuration, or that if there are multiple configurations 2111 // in this execution, that the first one will determine whether PDF 2112 // export is provided. 2113 Configuration configuration = Configuration.configurations().get(0); 2114 // NOTE: Configuration should not be null, but just in case: 2115 if (configuration != null) { 2116 2117 // Here, we get the _importActionClassNames from the configuration. 2118 // _importActionClassNames is an array of Strings where each element 2119 // names a class to that is an import action. 2120 // See also _classesToRemove in Configuration.java 2121 try { 2122 Parameter importActionClassNames = (Parameter) configuration 2123 .getAttribute("_importActionClassNames", 2124 Parameter.class); 2125 if (importActionClassNames != null) { 2126 ArrayToken importActionClassNamesToken = (ArrayToken) importActionClassNames 2127 .getToken(); 2128 for (int i = 0; i < importActionClassNamesToken 2129 .length(); i++) { 2130 String importActionClassName = ((StringToken) importActionClassNamesToken 2131 .getElement(i)).stringValue(); 2132 try { 2133 // Get the class, instantiate it and add it to the menu. 2134 Class importActionClass = Class 2135 .forName(importActionClassName); 2136 Constructor constructor = importActionClass 2137 .getDeclaredConstructor( 2138 new Class[] { Top.class }); 2139 AbstractAction importAction = (AbstractAction) constructor 2140 .newInstance(new Object[] { this }); 2141 JMenuItem importItem = new JMenuItem(importAction); 2142 importMenu.add(importItem); 2143 } catch (Throwable throwable) { 2144 // We do not want to abort at this point because the worst 2145 // case is that we will have no Import FMU in the menu. 2146 // That is better than preventing the user from opening a model. 2147 System.err.println( 2148 "Warning: Tried to create the an import menu item by looking for the " 2149 + importActionClassName 2150 + " Java class, but failed: " 2151 + throwable); 2152 } 2153 } 2154 } 2155 } catch (Throwable throwable) { 2156 if (!_printedImportActionClassNamesMessage) { 2157 _printedImportActionClassNamesMessage = true; 2158 System.err.println( 2159 "Problem reading the _importActionClassNames parameter from " 2160 + "the configuration: " + throwable); 2161 } 2162 } 2163 2164 // PDF Action. 2165 try { 2166 _exportPDFAction = (AbstractAction) configuration 2167 .getStringParameterAsClass("_exportPDFActionClassName", 2168 new Class[] { Top.class }, 2169 new Object[] { this }); 2170 } catch (Throwable throwable) { 2171 // We do not want to abort at this point because the worst 2172 // case is that we will have no Export PDF in the menu. 2173 // That is better than preventing the user from opening a model. 2174 //System.err 2175 // .printlns("Warning: Tried to create the Export PDF menu item, but failed: " 2176 // + throwable); 2177 } 2178 2179 // Deal with the HTML Action next. 2180 try { 2181 _exportHTMLAction = (AbstractAction) configuration 2182 .getStringParameterAsClass("_exportHTMLActionClassName", 2183 new Class[] { BasicGraphFrame.class }, 2184 new Object[] { this }); 2185 } catch (Throwable throwable) { 2186 // We do not want to abort at this point because the worst 2187 // case is that we will have no Export to Web in the menu. 2188 // That is better than preventing the user from opening a model. 2189 2190 // We don't include the GPL'd iText PDF in the 2191 // release, so don't print a message if it is missing. 2192 2193 //System.err 2194 // .println("Warning: Tried to create the Export to Web menu item, but failed: " 2195 // + throwable); 2196 } 2197 } 2198 2199 // Uncomment the next block to have Export PDF *ALWAYS* enabled. 2200 // We don't want it always enabled because ptiny, the applets and 2201 // Web Start should not included this AGPL'd piece of software 2202 2203 // NOTE: Comment out the entire block with lines that begin with // 2204 // so that the test in adm notices that the block is commented out. 2205 2206 // if (_exportPDFAction == null) { 2207 // //String exportPDFActionClassName = exportPDFActionClassNameParameter.stringValue(); 2208 // String exportPDFActionClassName = "ptolemy.vergil.basic.export.itextpdf.ExportPDFAction"; 2209 // try { 2210 // Class exportPDFActionClass = Class 2211 // .forName(exportPDFActionClassName); 2212 // Constructor exportPDFActionConstructor = exportPDFActionClass 2213 // .getDeclaredConstructor(Top.class); 2214 // _exportPDFAction = (AbstractAction) exportPDFActionConstructor 2215 // .newInstance(this); 2216 // } catch (Throwable throwable) { 2217 // new InternalErrorException(null, throwable, 2218 // "Failed to construct export PDF class \"" 2219 // + exportPDFActionClassName 2220 // + "\", which was read from the configuration."); 2221 // } 2222 // } 2223 2224 // End of block to uncomment. 2225 2226 if (_exportPDFAction != null) { 2227 // Insert the Export PDF item. 2228 JMenuItem exportItem = new JMenuItem(_exportPDFAction); 2229 exportMenu.add(exportItem); 2230 } 2231 2232 // Next do the export GIF action. 2233 if (_exportGIFAction == null) { 2234 _exportGIFAction = new ExportImageAction("GIF"); 2235 } 2236 JMenuItem exportItem = new JMenuItem(_exportGIFAction); 2237 exportMenu.add(exportItem); 2238 2239 // Next do the export PNG action. 2240 if (_exportPNGAction == null) { 2241 _exportPNGAction = new ExportImageAction("PNG"); 2242 } 2243 exportItem = new JMenuItem(_exportPNGAction); 2244 exportMenu.add(exportItem); 2245 2246 // Next do the export HTML action. 2247 if (_exportHTMLAction != null) { 2248 // Insert the Export to Web item. 2249 exportItem = new JMenuItem(_exportHTMLAction); 2250 exportMenu.add(exportItem); 2251 } 2252 return fileMenuItems; 2253 } 2254 2255 /** Create a new graph pane. Subclasses will override this to change 2256 * the pane that is created. Note that this method is called in 2257 * constructor, so derived classes must be careful to not reference 2258 * local variables that may not have yet been created. 2259 * @param entity The object to be displayed in the pane. 2260 * @return The pane that is created. 2261 */ 2262 protected abstract GraphPane _createGraphPane(NamedObj entity); 2263 2264 /** Create the component that goes to the right of the library. 2265 * @param entity The entity to display in the component. 2266 * @return The component that goes to the right of the library. 2267 */ 2268 protected JComponent _createRightComponent(NamedObj entity) { 2269 GraphPane pane = _createGraphPane(entity); 2270 2271 FigureLayer fl = pane.getForegroundLayer(); 2272 fl.setPickHalo(2); 2273 2274 EventLayer fel = pane.getForegroundEventLayer(); 2275 fel.setConsuming(false); 2276 fel.setEnabled(true); 2277 2278 _mousePressedLayerAdapter = new MousePressedLayerAdapter(); 2279 fel.addLayerListener(_mousePressedLayerAdapter); 2280 2281 JGraph graph = new JGraph(pane); 2282 setJGraph(graph); 2283 _dropTarget = new EditorDropTarget(_jgraph); 2284 return _jgraph; 2285 } 2286 2287 /** Create a SizeAttribute for the current model when it is being saved to 2288 * a file. The size recorded in the SizeAttribute is the size of the 2289 * current canvas. 2290 * @return The SizeAttribute. 2291 * @exception IllegalActionException If "_vergilSize" is found but is not 2292 * an instance of SizeAttribute, or if a SizeAttribute is not accepted by 2293 * the current model. 2294 * @exception NameDuplicationException If the name "_vergilSize" is already 2295 * used when trying to create the SizeAttribute. 2296 */ 2297 protected SizeAttribute _createSizeAttribute() 2298 throws IllegalActionException, NameDuplicationException { 2299 return _createSizeAttribute(getModel()); 2300 } 2301 2302 /** Create a SizeAttribute for a specific model when it is being saved to 2303 * a file. The size recorded in the SizeAttribute is the size of the 2304 * current canvas. 2305 * @param model The model. 2306 * @return The SizeAttribute. 2307 * @exception IllegalActionException If "_vergilSize" is found but is not 2308 * an instance of SizeAttribute, or if a SizeAttribute is not accepted by 2309 * the current model. 2310 * @exception NameDuplicationException If the name "_vergilSize" is already 2311 * used when trying to create the SizeAttribute. 2312 */ 2313 protected SizeAttribute _createSizeAttribute(NamedObj model) 2314 throws IllegalActionException, NameDuplicationException { 2315 // Have to also record the size of the JGraph because 2316 // setting the size of the frame is ignored if we don't 2317 // also set the size of the JGraph. Why? Who knows. Swing. 2318 if (model != null) { 2319 SizeAttribute size = (SizeAttribute) model 2320 .getAttribute("_vergilSize", SizeAttribute.class); 2321 2322 if (size == null) { 2323 size = new SizeAttribute(model, "_vergilSize"); 2324 } 2325 2326 size.recordSize(_getRightComponent()); 2327 return size; 2328 } 2329 return null; 2330 } 2331 2332 /** Export the model into the writer with the given name. If 2333 * the _query has a selected entry and it is true, 2334 * then only the selected named objects are exported; 2335 * otherwise, the whole model is exported with its exportMoML() 2336 * method. 2337 * 2338 * @param writer The writer. 2339 * @param model The model to export. 2340 * @param name The name of the exported model. 2341 * @exception IOException If an I/O error occurs. 2342 */ 2343 protected void _exportDesignPattern(Writer writer, NamedObj model, 2344 String name) throws IOException { 2345 if (_query != null && _query.hasEntry("selected") 2346 && _query.getBooleanValue("selected")) { 2347 try { 2348 model.workspace().getReadAccess(); 2349 String elementName = model.getElementName(); 2350 writer.write("<?xml version=\"1.0\" standalone=\"no\"?>\n" 2351 + "<!DOCTYPE " + elementName + " PUBLIC " 2352 + "\"-//UC Berkeley//DTD MoML 1//EN\"\n" 2353 + " \"http://ptolemy.eecs.berkeley.edu" 2354 + "/xml/dtd/MoML_1.dtd\">\n"); 2355 2356 writer.write("<" + elementName + " name=\"" + name 2357 + "\" class=\"" + model.getClassName() + "\""); 2358 2359 if (model.getSource() != null) { 2360 writer.write(" source=\"" + model.getSource() + "\">\n"); 2361 } else { 2362 writer.write(">\n"); 2363 } 2364 2365 String[] attributeNames = { "_alternateGetMomlAction", 2366 "_designPatternIcon", "_transformationBefore", 2367 "_transformationAfter" }; 2368 for (String attributeName : attributeNames) { 2369 Attribute attribute = model.getAttribute(attributeName); 2370 if (attribute != null) { 2371 attribute.exportMoML(writer, 1); 2372 } 2373 } 2374 2375 HashSet<NamedObj> namedObjSet = _getSelectionSet(); 2376 NamedObj container = (NamedObj) _getGraphModel().getRoot(); 2377 Iterator<NamedObj> elements = container 2378 .sortContainedObjects(namedObjSet).iterator(); 2379 while (elements.hasNext()) { 2380 elements.next().exportMoML(writer, 1); 2381 } 2382 2383 if (model instanceof CompositeEntity) { 2384 writer.write(((CompositeEntity) model).exportLinks(1, 2385 namedObjSet)); 2386 } 2387 2388 writer.write("</" + elementName + ">\n"); 2389 } finally { 2390 model.workspace().doneReading(); 2391 } 2392 } else { 2393 if (model.getContainer() != null) { 2394 writer.write("<?xml version=\"1.0\" standalone=\"no\"?>\n" 2395 + "<!DOCTYPE " + model.getElementName() + " PUBLIC " 2396 + "\"-//UC Berkeley//DTD MoML 1//EN\"\n" 2397 + " \"http://ptolemy.eecs.berkeley.edu" 2398 + "/xml/dtd/MoML_1.dtd\">\n"); 2399 } 2400 model.exportMoML(writer, 0, name); 2401 } 2402 } 2403 2404 /** Finish exporting a design pattern. 2405 */ 2406 protected void _finishExportDesignPattern() { 2407 } 2408 2409 /** Return the center location of the visible part of the pane 2410 * for a JGraph. 2411 * @param graph The JGraph. 2412 * @return The center of the visible part. 2413 * @see #setCenter(Point2D) 2414 */ 2415 protected Point2D _getCenter(JGraph graph) { 2416 Rectangle2D rect = _getVisibleCanvasRectangle(graph); 2417 return new Point2D.Double(rect.getCenterX(), rect.getCenterY()); 2418 } 2419 2420 /** Return the rectangle representing the visible part of the 2421 * pane for a JGraph, transformed into canvas coordinates. This is the range 2422 * of locations that are visible, given the current pan and zoom. 2423 * @param graph The JGraph. 2424 * @return The rectangle representing the visible part. 2425 */ 2426 protected Rectangle2D _getVisibleCanvasRectangle(JGraph graph) { 2427 AffineTransform current = graph.getCanvasPane().getTransformContext() 2428 .getTransform(); 2429 2430 AffineTransform inverse; 2431 try { 2432 inverse = current.createInverse(); 2433 } catch (NoninvertibleTransformException e) { 2434 throw new RuntimeException(e.toString()); 2435 } 2436 2437 Rectangle2D visibleRect = _getVisibleRectangle(graph); 2438 return ShapeUtilities.transformBounds(visibleRect, inverse); 2439 } 2440 2441 /** Return the rectangle representing the visible part of the 2442 * pane for a JGraph, in pixel coordinates on the screen. 2443 * @param graph The JGraph. 2444 * @return A rectangle whose upper left corner is at (0, 0) and whose 2445 * size is the size of the canvas component. 2446 */ 2447 protected Rectangle2D _getVisibleRectangle(JGraph graph) { 2448 Dimension size = graph.getSize(); 2449 return new Rectangle2D.Double(0, 0, size.getWidth(), size.getHeight()); 2450 } 2451 2452 /** Get the directory that was last accessed by this window. 2453 * @see #_setDirectory 2454 * @return The directory last accessed. 2455 * @deprecated Use {@link #getLastDirectory()} instead 2456 */ 2457 @Deprecated 2458 protected File _getDirectory() { 2459 return _getCurrentDirectory(); 2460 } 2461 2462 /** Return the graph controller associated with this frame. 2463 * @return The graph controller associated with this frame. 2464 */ 2465 protected GraphController _getGraphController() { 2466 GraphPane graphPane = getJGraph().getGraphPane(); 2467 return graphPane.getGraphController(); 2468 } 2469 2470 /** Return the graph model associated with this frame. 2471 * @return The graph model associated with this frame. 2472 */ 2473 protected AbstractBasicGraphModel _getGraphModel() { 2474 GraphController controller = _getGraphController(); 2475 return (AbstractBasicGraphModel) controller.getGraphModel(); 2476 } 2477 2478 /** Return the right component on which graph editing occurs. 2479 * @return The JGraph on which graph editing occurs. 2480 */ 2481 protected JComponent _getRightComponent() { 2482 return _rightComponent; 2483 } 2484 2485 /** Return a set of instances of NamedObj representing the objects 2486 * that are currently selected. This set has no particular order 2487 * to it. If you need the selection objects in proper order, as 2488 * defined by the container, then call sortContainedObjects() 2489 * on the container to sort the result. 2490 * @return The set of selected objects. 2491 */ 2492 protected HashSet<NamedObj> _getSelectionSet() { 2493 GraphController controller = _getGraphController(); 2494 GraphModel graphModel = controller.getGraphModel(); 2495 SelectionModel model = controller.getSelectionModel(); 2496 Object[] selection = model.getSelectionAsArray(); 2497 2498 // A set, because some objects may represent the same 2499 // ptolemy object. 2500 HashSet<NamedObj> namedObjSet = new HashSet<NamedObj>(); 2501 HashSet<Object> nodeSet = new HashSet<Object>(); 2502 2503 // First get all the nodes. 2504 for (Object element : selection) { 2505 if (element instanceof Figure) { 2506 Object userObject = ((Figure) element).getUserObject(); 2507 2508 if (graphModel.isNode(userObject)) { 2509 NamedObj actual = (NamedObj) graphModel 2510 .getSemanticObject(userObject); 2511 //System.out.println("BasicGraphFrame._getSelectionSet() actual: " + actual.getClass().getName()); 2512 //if ( !(actual instanceof PortParameter)) { 2513 nodeSet.add(userObject); 2514 namedObjSet.add(actual); 2515 //} 2516 } 2517 } 2518 } 2519 2520 for (Object element : selection) { 2521 if (element instanceof Figure) { 2522 Object userObject = ((Figure) element).getUserObject(); 2523 2524 if (graphModel.isEdge(userObject)) { 2525 // Check to see if the head and tail are both being 2526 // copied. Only if so, do we actually take the edge. 2527 Object head = graphModel.getHead(userObject); 2528 Object tail = graphModel.getTail(userObject); 2529 boolean headOK = nodeSet.contains(head); 2530 boolean tailOK = nodeSet.contains(tail); 2531 Iterator<Object> objects = nodeSet.iterator(); 2532 2533 while (!(headOK && tailOK) && objects.hasNext()) { 2534 Object object = objects.next(); 2535 2536 if (!headOK && GraphUtilities.isContainedNode(head, 2537 object, graphModel)) { 2538 headOK = true; 2539 } 2540 2541 if (!tailOK && GraphUtilities.isContainedNode(tail, 2542 object, graphModel)) { 2543 tailOK = true; 2544 } 2545 } 2546 2547 if (headOK && tailOK) { 2548 // Add the relation. 2549 NamedObj actual = (NamedObj) graphModel 2550 .getSemanticObject(userObject); 2551 namedObjSet.add(actual); 2552 } 2553 } 2554 } 2555 } 2556 2557 return namedObjSet; 2558 } 2559 2560 /** 2561 * Initialize this BasicGraphFrame. 2562 * Derived classes may call this method in their constructors. 2563 * Derived classes should call the various _initBasicGraphFrame*() methods 2564 * so as to avoid code duplication 2565 */ 2566 protected void _initBasicGraphFrame() { 2567 2568 // WARNING: If you change this method, then Kepler will probably break. 2569 // kepler/gui/src/org/kepler/gui/KeplerGraphFrame.java extends BasicGraphFrame 2570 // and has an _initBasicGraphFrame() method. 2571 2572 // To build Kepler under Eclipse, see 2573 // https://kepler-project.org/developers/reference/kepler-and-eclipse 2574 2575 // This method calls a series of other protected methods whose 2576 // names start with _initBasicGraphFrame. These methods contain common 2577 // functionality between this class and KeplerGraphFrame so 2578 // that there is a chance that we avoid a ton of code 2579 // duplication. 2580 2581 // Code that is different between this class and KeplerGraphFrame 2582 // appears below. 2583 2584 // Eventually, perhaps the common functionality can be put into one 2585 // method. 2586 2587 _initBasicGraphFrameInitialization(); 2588 2589 ActionListener deletionListener = new DeletionListener(); 2590 2591 _rightComponent.registerKeyboardAction(deletionListener, "Delete", 2592 KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), 2593 JComponent.WHEN_IN_FOCUSED_WINDOW); 2594 _rightComponent.registerKeyboardAction(deletionListener, "BackSpace", 2595 KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), 2596 JComponent.WHEN_IN_FOCUSED_WINDOW); 2597 2598 _initBasicGraphFrameRightComponent(); 2599 2600 // Background color is parameterizable by preferences. 2601 _setBackgroundColor(_rightComponent); 2602 2603 _initBasicGraphFrameRightComponentMouseListeners(); 2604 2605 try { 2606 // The SizeAttribute property is used to specify the size 2607 // of the JGraph component. Unfortunately, with Swing's 2608 // mysterious and undocumented handling of component sizes, 2609 // there appears to be no way to control the size of the 2610 // JGraph from the size of the Frame, which is specified 2611 // by the WindowPropertiesAttribute. 2612 SizeAttribute size = (SizeAttribute) getModel() 2613 .getAttribute("_vergilSize", SizeAttribute.class); 2614 2615 if (size != null) { 2616 size.setSize(_rightComponent); 2617 } else { 2618 // Set the default size. 2619 // Note that the location is of the frame, while the size 2620 // is of the scrollpane. 2621 _rightComponent.setMinimumSize(new Dimension(200, 200)); 2622 _rightComponent.setPreferredSize(new Dimension(700, 500)); 2623 _rightComponent.setSize(600, 450); 2624 } 2625 _rightComponent.setBorder(BorderFactory.createEtchedBorder()); 2626 2627 _initBasicGraphFrameSetZoomAndPan(); 2628 } catch (Throwable throwable) { 2629 // Ignore problems here. Errors simply result in a default 2630 // size and location. 2631 } 2632 2633 // If we don't have a library, we might be trying to only show 2634 // models 2635 // FIXME: should we be checking for _library instead? 2636 Configuration configuration = getConfiguration(); 2637 if (configuration != null && (CompositeEntity) configuration 2638 .getEntity("actor library") != null) { 2639 // Create the panner. 2640 _graphPanner = new JCanvasPanner(getJGraph()); 2641 _graphPanner.setPreferredSize(new Dimension(200, 150)); 2642 // _graphPanner.setMaximumSize(new Dimension(200, 450)); 2643 _graphPanner.setSize(200, 150); 2644 // NOTE: Border causes all kinds of problems! 2645 _graphPanner.setBorder(BorderFactory.createEtchedBorder()); 2646 } 2647 2648 // Create the library of actors, or use the one in the entity, 2649 // if there is one. 2650 // FIXME: How do we make changes to the library persistent? 2651 boolean gotLibrary = false; 2652 2653 try { 2654 LibraryAttribute libraryAttribute = (LibraryAttribute) getModel() 2655 .getAttribute("_library", LibraryAttribute.class); 2656 2657 if (libraryAttribute != null) { 2658 // The model contains a library. 2659 try { 2660 _topLibrary = libraryAttribute.getLibrary(); 2661 if (_topLibrary != null) { 2662 gotLibrary = true; 2663 } 2664 } catch (SecurityException ex) { 2665 System.out.println("Warning: failed to parse " 2666 + "_library attribute (running in an applet " 2667 + "or sandbox always causes this)"); 2668 } 2669 } 2670 } catch (Exception ex) { 2671 try { 2672 MessageHandler.warning("Invalid library in the model.", ex); 2673 } catch (CancelException e) { 2674 } 2675 } 2676 2677 if (!gotLibrary) { 2678 try { 2679 if (_defaultLibrary != null) { 2680 // A default library has been specified. 2681 _topLibrary = _defaultLibrary.getLibrary(); 2682 gotLibrary = true; 2683 } 2684 } catch (SecurityException ex) { 2685 // Ignore, we are in an applet or sandbox. 2686 // We already printed a message, why print it again? 2687 } catch (Exception ex) { 2688 try { 2689 // FIXME: It seems wrong to call MessageHandler here, 2690 // instead, we should throw an IllegalActionException? 2691 MessageHandler.warning( 2692 "Invalid default library for the frame.", ex); 2693 } catch (CancelException e) { 2694 } 2695 } 2696 } 2697 2698 if (!gotLibrary) { 2699 // Neither the model nor the argument have specified a library. 2700 // See if there is a default library in the configuration. 2701 _topLibrary = _createDefaultLibrary(getModel().workspace()); 2702 } 2703 2704 // Only include the palettePane and panner if there is an actor library. 2705 // The ptinyViewer configuration uses this. 2706 if (configuration != null && (CompositeEntity) configuration 2707 .getEntity("actor library") != null) { 2708 _libraryModel = new VisibleTreeModel(_topLibrary); 2709 // Second arguments prevents parameter values from showing in the library. 2710 _library = new PTree(_libraryModel, false); 2711 _library.setRootVisible(false); 2712 _library.setBackground(BACKGROUND_COLOR); 2713 2714 // If you want to expand the top-level libraries, uncomment this. 2715 // Object[] path = new Object[2]; 2716 // path[0] = _topLibrary; 2717 // Iterator libraries = _topLibrary.entityList().iterator(); 2718 // while (libraries.hasNext()) { 2719 // path[1] = libraries.next(); 2720 // _library.expandPath(new javax.swing.tree.TreePath(path)); 2721 // } 2722 2723 _libraryContextMenuCreator = new PTreeMenuCreator(); 2724 _libraryContextMenuCreator 2725 .addMenuItemFactory(new OpenLibraryMenuItemFactory()); 2726 _libraryContextMenuCreator 2727 .addMenuItemFactory(new DocumentationMenuItemFactory()); 2728 _library.addMouseListener(_libraryContextMenuCreator); 2729 2730 _libraryScrollPane = new JScrollPane(_library); 2731 // See _treeViewScrollPane below. 2732 _libraryScrollPane.setMinimumSize(new Dimension(200, 200)); 2733 _libraryScrollPane.setPreferredSize(new Dimension(200, 300)); 2734 _libraryScrollPane.setBorder(BorderFactory.createEtchedBorder()); 2735 _setBackgroundColor(_library); 2736 2737 // create the palette on the left. 2738 _palettePane = new JPanel(); 2739 _palettePane.setBorder(null); 2740 _palettePane.setLayout(new GridBagLayout()); 2741 2742 // create a query for search. 2743 JPanel findPanel = new JPanel(new GridBagLayout()); 2744 2745 // Put in the label. 2746 GridBagConstraints labelConstraints = new GridBagConstraints(); 2747 labelConstraints.gridx = 0; 2748 labelConstraints.gridy = 0; 2749 JLabel label = new JLabel("Find:"); 2750 findPanel.add(label, labelConstraints); 2751 2752 // Put in the entry box. 2753 _findInLibraryEntryBox = new JTextField(12); 2754 _findInLibraryEntryBox.addActionListener(new FindInLibraryAction()); 2755 GridBagConstraints entryBoxConstraints = new GridBagConstraints(); 2756 entryBoxConstraints.gridx = 1; 2757 entryBoxConstraints.gridy = 0; 2758 entryBoxConstraints.fill = GridBagConstraints.HORIZONTAL; 2759 entryBoxConstraints.weightx = 1.0; 2760 findPanel.add(_findInLibraryEntryBox, entryBoxConstraints); 2761 2762 // Put in the find panel. 2763 GridBagConstraints findPanelConstraints = new GridBagConstraints(); 2764 findPanelConstraints.gridx = 0; 2765 findPanelConstraints.gridy = 0; 2766 findPanelConstraints.fill = GridBagConstraints.HORIZONTAL; 2767 _palettePane.add(findPanel, findPanelConstraints); 2768 2769 // The Hierarchy Tree browser for CompositeEntities. 2770 NamedObj model = getModel(); 2771 if (!(model instanceof CompositeEntity)) { 2772 // EditIconFrame will have a EditorIcon as a model, not a CompositeEntity. 2773 _treeViewScrollPane = null; 2774 } else { 2775 _treeViewModel = new ClassAndEntityTreeModel( 2776 getModel().toplevel()); 2777 2778 // Second arguments prevents parameter values from showing in the library, 2779 // I'm not sure if that is relevant for the hierarchy tree browser. 2780 _treeView = new PTree(_treeViewModel, false); 2781 // Replaced by mouse listener. 2782 // _treeView.addTreeSelectionListener(new HierarchyTreeSelectionListener()); 2783 _treeView.addMouseListener(new HierarchyTreeMouseAdapter()); 2784 _treeView.setBackground(BACKGROUND_COLOR); 2785 _treeView.setCellRenderer(new HierarchyTreeCellRenderer()); 2786 2787 _treeViewScrollPane = new JScrollPane(_treeView); 2788 // See _libraryScrollPane above. 2789 _treeViewScrollPane.setMinimumSize(new Dimension(200, 200)); 2790 _treeViewScrollPane.setPreferredSize(new Dimension(200, 300)); 2791 _treeViewScrollPane 2792 .setBorder(BorderFactory.createEtchedBorder()); 2793 _setBackgroundColor(_treeView); 2794 2795 // Make the Ptolemy model visible in the tree. 2796 TreePath modelTreePath = null; 2797 { 2798 // Traverse the Ptolemy model hierarchy, create a list, reverse it, 2799 // create an array and then a TreePath. 2800 List<NamedObj> compositeList = new LinkedList<NamedObj>(); 2801 NamedObj composite = getModel(); 2802 while (composite != null) { 2803 compositeList.add(composite); 2804 composite = composite.getContainer(); 2805 } 2806 java.util.Collections.reverse(compositeList); 2807 Object[] composites = compositeList.toArray(); 2808 modelTreePath = new TreePath(composites); 2809 } 2810 _treeView.expandPath(modelTreePath); 2811 _treeView.makeVisible(modelTreePath); 2812 _treeView.scrollPathToVisible(modelTreePath); 2813 } 2814 2815 // Put in the tabbed pane that contains the hierarchy browser and the library 2816 JTabbedPane libraryTreeTabbedPane = new JTabbedPane(); 2817 libraryTreeTabbedPane.add("Library", _libraryScrollPane); 2818 2819 if (_treeViewScrollPane != null) { 2820 libraryTreeTabbedPane.add("Tree", _treeViewScrollPane); 2821 } 2822 2823 GridBagConstraints tabbedPaneConstraints = new GridBagConstraints(); 2824 tabbedPaneConstraints.gridx = 0; 2825 tabbedPaneConstraints.gridy = 1; 2826 tabbedPaneConstraints.fill = GridBagConstraints.BOTH; 2827 tabbedPaneConstraints.weightx = 1.0; 2828 tabbedPaneConstraints.weighty = 0.7; 2829 _palettePane.add(libraryTreeTabbedPane, tabbedPaneConstraints); 2830 2831 // Add the graph panner. 2832 if (_graphPanner != null) { 2833 GridBagConstraints pannerConstraints = new GridBagConstraints(); 2834 pannerConstraints.gridx = 0; 2835 pannerConstraints.gridy = 2; 2836 pannerConstraints.weighty = 0.3; 2837 pannerConstraints.fill = GridBagConstraints.BOTH; 2838 _palettePane.add(_graphPanner, pannerConstraints); 2839 } 2840 2841 _splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); 2842 _splitPane.setLeftComponent(_palettePane); 2843 _splitPane.setRightComponent(_rightComponent); 2844 getContentPane().add(_splitPane, BorderLayout.CENTER); 2845 } else { 2846 getContentPane().add(_rightComponent, BorderLayout.CENTER); 2847 } 2848 2849 _toolbar = new JToolBar(); 2850 _toolbar.setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0)); 2851 try { 2852 new ToolBar(getTableau(), "toolbar", _toolbar, BorderLayout.NORTH); 2853 } catch (Exception e) { 2854 throw new InternalErrorException(getTableau(), e, 2855 "Unable to create tool bar."); 2856 } 2857 2858 GUIUtilities.addToolBarButton(_toolbar, _saveAction); 2859 2860 // Note that in Top we disable Print unless the class implements 2861 // the Printable or Pageable interfaces. By definition, this class 2862 // implements the Printable interface 2863 GUIUtilities.addToolBarButton(_toolbar, _printAction); 2864 2865 _initBasicGraphFrameToolBarZoomButtons(); 2866 2867 GUIUtilities.addToolBarButton(_toolbar, _openContainerAction); 2868 if (getModel() == getModel().toplevel()) { 2869 // If we are at the top level, disable. 2870 _openContainerAction.setEnabled(false); 2871 } 2872 2873 _initBasicGraphFrameActions(); 2874 2875 // Add a weak reference to this to keep track of all 2876 // the graph frames that have been created. 2877 _openGraphFrames.add(this); 2878 } 2879 2880 /** Add the cut, copy, paste, move to front, mode to back 2881 * actions. Also add the EditPreferencesAction and initialize 2882 * the layout gui. 2883 * Derived classes usually call this at the end of 2884 * _initBasicGraphFrame(). 2885 */ 2886 protected void _initBasicGraphFrameActions() { 2887 _cutAction = new CutAction(); 2888 _copyAction = new CopyAction(); 2889 _pasteAction = new PasteAction(); 2890 _findAction = new FindAction(); 2891 2892 // FIXME: vergil.kernel.AttributeController also defines context 2893 // menu choices that do the same thing. 2894 _moveToFrontAction = new MoveToFrontAction(); 2895 _moveToBackAction = new MoveToBackAction(); 2896 2897 _editPreferencesAction = new EditPreferencesAction(); 2898 2899 _initLayoutGuiAction(); 2900 _initReloadAccessorsAction(); 2901 } 2902 2903 /** Set up the right component. */ 2904 protected void _initBasicGraphFrameRightComponent() { 2905 _rightComponent.setRequestFocusEnabled(true); 2906 _rightComponent.setAlignmentX(1); 2907 _rightComponent.setAlignmentY(1); 2908 } 2909 2910 /** Add listeners to the right component. */ 2911 protected void _initBasicGraphFrameRightComponentMouseListeners() { 2912 _rightComponent.addMouseWheelListener(this); 2913 _rightComponent.addMouseMotionListener(this); 2914 _rightComponent.addMouseListener(this); 2915 } 2916 2917 /** Common initialization for a BasicGraphFrame. 2918 * Derived classes should call this method early in 2919 * _initBasicGraphFrame(). 2920 */ 2921 protected void _initBasicGraphFrameInitialization() { 2922 getModel().addChangeListener(this); 2923 getContentPane().setLayout(new BorderLayout()); 2924 _rightComponent = _createRightComponent(getModel()); 2925 } 2926 2927 /** Add tool bar buttons. 2928 * Derived classes should set _toolbar before calling 2929 * this method. 2930 */ 2931 protected void _initBasicGraphFrameToolBarZoomButtons() { 2932 GUIUtilities.addToolBarButton(_toolbar, _zoomInAction); 2933 GUIUtilities.addToolBarButton(_toolbar, _zoomResetAction); 2934 GUIUtilities.addToolBarButton(_toolbar, _zoomFitAction); 2935 GUIUtilities.addToolBarButton(_toolbar, _zoomOutAction); 2936 } 2937 2938 /** Set the zoom factor and the pan. 2939 * @exception IllegalActionException If the zoom or pan parameters 2940 * cannot be read. 2941 */ 2942 protected void _initBasicGraphFrameSetZoomAndPan() 2943 throws IllegalActionException { 2944 _initBasicGraphFrameSetZoomAndPane(getModel(), getJGraph()); 2945 } 2946 2947 /** Set the zoom factor and the pan for a specific model and JGraph. 2948 * @param model The model. 2949 * @param jgraph The JGraph. 2950 * @exception IllegalActionException If the zoom or pan parameters 2951 * cannot be read. 2952 */ 2953 protected void _initBasicGraphFrameSetZoomAndPane(NamedObj model, 2954 JGraph jgraph) throws IllegalActionException { 2955 2956 // Set the zoom factor. 2957 Parameter zoom = (Parameter) model.getAttribute("_vergilZoomFactor", 2958 Parameter.class); 2959 if (zoom != null) { 2960 _zoom(jgraph, ((DoubleToken) zoom.getToken()).doubleValue()); 2961 // Make sure the visibility is only expert. 2962 zoom.setVisibility(Settable.EXPERT); 2963 } 2964 2965 // Set the pan position. 2966 Parameter pan = (Parameter) model.getAttribute("_vergilCenter", 2967 Parameter.class); 2968 2969 if (pan != null) { 2970 ArrayToken panToken = (ArrayToken) pan.getToken(); 2971 Point2D center = new Point2D.Double( 2972 ((DoubleToken) panToken.getElement(0)).doubleValue(), 2973 ((DoubleToken) panToken.getElement(1)).doubleValue()); 2974 _setCenter(jgraph, center); 2975 2976 // Make sure the visibility is only expert. 2977 pan.setVisibility(Settable.EXPERT); 2978 } 2979 2980 // If we have neither zooming or panning info... 2981 if (zoom == null && pan == null) { 2982 // ...set the top left corner of the view to the top left corner of the model. 2983 // Note: This code only works for a zoom factor of 1.0, which is no problem at 2984 // this stage since that's the default and no zooming info was found in the model. 2985 GraphPane pane = jgraph.getGraphPane(); 2986 Rectangle2D bounds = pane.getForegroundLayer().getLayerBounds(); 2987 Rectangle2D visible = _getVisibleRectangle(jgraph); 2988 2989 double centerX = visible.getCenterX() 2990 - (visible.getX() - bounds.getX()); 2991 double centerY = visible.getCenterY() 2992 - (visible.getY() - bounds.getY()); 2993 2994 // Set the new center point, but add a little free space between model and border 2995 _setCenter(jgraph, 2996 new Point2D.Double(centerX - 10.0, centerY - 10.0)); 2997 } 2998 } 2999 3000 /** Initialize the layout gui. */ 3001 protected void _initLayoutGuiAction() { 3002 // Try to create an advanced layout action. 3003 final IGuiAction layoutGuiAction = _createLayoutAction(); 3004 if (layoutGuiAction != null) { 3005 _layoutAction = new AbstractAction("Automatic Layout") { 3006 @Override 3007 public void actionPerformed(ActionEvent e) { 3008 layoutGuiAction.doAction(getModel()); 3009 } 3010 }; 3011 // The advanced layout action is available, so create the configuration 3012 // dialog for displaying layout parameters. 3013 _layoutConfigDialogAction = new LayoutConfigDialogAction(); 3014 } else { 3015 // The advanced layout action is not available, so use the simple 3016 // Ptolemy layout algorithm. 3017 _layoutAction = new AbstractAction("Automatic Layout") { 3018 @Override 3019 public void actionPerformed(ActionEvent e) { 3020 new PtolemyLayoutAction().doAction(getModel()); 3021 } 3022 }; 3023 } 3024 _layoutAction.putValue("tooltip", "Layout the graph (Ctrl+T)"); 3025 _layoutAction.putValue(GUIUtilities.ACCELERATOR_KEY, 3026 KeyStroke.getKeyStroke(KeyEvent.VK_T, 3027 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); 3028 _layoutAction.putValue(GUIUtilities.MNEMONIC_KEY, 3029 Integer.valueOf(KeyEvent.VK_L)); 3030 } 3031 3032 /** Initialize the reload accessors menu choice. */ 3033 protected void _initReloadAccessorsAction() { 3034 // If the JSAccessors.reloadAllAccessors(CompositeEntity) 3035 // method can be found, then add a menu choice. 3036 Method accessorReloader = null; 3037 try { 3038 Class accessorClass = Class 3039 .forName("org.terraswarm.accessor.JSAccessor"); 3040 accessorReloader = accessorClass.getDeclaredMethod( 3041 "reloadAllAccessors", 3042 new Class[] { CompositeEntity.class }); 3043 } catch (Throwable throwable) { 3044 // Fail silently! 3045 } 3046 final Method accessorReloaderFinal = accessorReloader; 3047 if (accessorReloader != null) { 3048 _reloadAccessorsAction = new AbstractAction("Reload Accessors") { 3049 @Override 3050 public void actionPerformed(ActionEvent e) { 3051 try { 3052 accessorReloaderFinal.invoke(null, 3053 (CompositeEntity) getModel()); 3054 } catch (Exception ex) { 3055 MessageHandler.error( 3056 "Failed to reload all the accessors.", ex); 3057 } 3058 } 3059 }; 3060 } 3061 } 3062 3063 /** Return true if this is a design pattern. 3064 * @return true if the model corresponding to this object 3065 * has a DesignPatternIcon attribute. 3066 */ 3067 protected boolean _isDesignPattern() { 3068 NamedObj model = getModel(); 3069 return !model.attributeList(DesignPatternIcon.class).isEmpty(); 3070 } 3071 3072 /** Prepare to export a design pattern. 3073 * In this base class, do nothing. 3074 */ 3075 protected void _prepareExportDesignPattern() { 3076 } 3077 3078 /** Create and return a file dialog for the "Save As" command. 3079 * This overrides the base class so that if this is a design pattern 3080 * and items are selected, then the user is asked if they 3081 * want to save only the selected objects. 3082 * <p>If {@link ptolemy.gui.PtGUIUtilities#useFileDialog()} returns true 3083 * then {@link ptolemy.gui.Top#_saveAs()} uses this method. Otherwise, 3084 * {@link #_saveAsJFileChooserComponent()} is used.</p> 3085 * @return A file dialog for save as. 3086 */ 3087 @Override 3088 protected FileDialog _saveAsFileDialogComponent() { 3089 FileDialog fileDialog = super._saveAsFileDialogComponent(); 3090 if (_isDesignPattern()) { 3091 if (!_getSelectionSet().isEmpty()) { 3092 // FIXME: It is not clear to me when this code would be called. 3093 // File -> New -> Ptera Model, then opening DesignPatterns, 3094 // dragging in a ListenToInput, selecting it and doing Save As 3095 // does not do it. 3096 3097 _query = new Query(); 3098 _query.addCheckBox("selected", "Selected objects only", true); 3099 // The problem here is that with FileDialog, we can't add the 3100 // query as an accessory like we can with JFileChooser. So, we 3101 // pop up a check box dialog before bringing up the FileDialog. 3102 new ComponentDialog(this, "Save submodel only?", _query); 3103 } 3104 } 3105 3106 return fileDialog; 3107 } 3108 3109 /** Create and return a file dialog for the "Save As" command. 3110 * This overrides the base class so that if this is a design pattern 3111 * and items are selected, then the user is asked if they 3112 * want to save only the selected objects. 3113 * <p>If {@link ptolemy.gui.PtGUIUtilities#useFileDialog()} returns false 3114 * then {@link ptolemy.gui.Top#_saveAs()} uses this method. Otherwise, 3115 * {@link #_saveAsFileDialogComponent()} is used.</p> 3116 3117 * @return A file dialog for save as. 3118 */ 3119 @Override 3120 protected JFileChooser _saveAsJFileChooserComponent() { 3121 JFileChooser fileChooser = super._saveAsJFileChooserComponent(); 3122 3123 if (_isDesignPattern()) { 3124 if (_getSelectionSet().isEmpty()) { 3125 fileChooser.setAccessory(null); 3126 } else { 3127 _query = new Query(); 3128 _query.addCheckBox("selected", "Selected objects only", true); 3129 fileChooser.setAccessory(_query); 3130 } 3131 } 3132 3133 return fileChooser; 3134 } 3135 3136 /** Set the background color of the specified component to the default, 3137 * or if it is given, to the background color given in the preferences. 3138 * @param component The component to set the color of. 3139 */ 3140 protected void _setBackgroundColor(Component component) { 3141 Configuration configuration = getConfiguration(); 3142 component.setBackground(BACKGROUND_COLOR); 3143 if (configuration != null) { 3144 try { 3145 PtolemyPreferences preferences = PtolemyPreferences 3146 .getPtolemyPreferencesWithinConfiguration( 3147 configuration); 3148 if (preferences != null) { 3149 component.setBackground( 3150 preferences.backgroundColor.asColor()); 3151 } 3152 } catch (IllegalActionException e1) { 3153 // Ignore the exception and use the default color. 3154 } 3155 } 3156 } 3157 3158 /** Set the center location of the visible part of a specific pane. 3159 * This will cause the panner to center on the specified location 3160 * with the current zoom factor. 3161 * @param jgraph The JGraph. 3162 * @param center The center of the visible part. 3163 * @see #getCenter() 3164 */ 3165 protected void _setCenter(JGraph jgraph, Point2D center) { 3166 Rectangle2D visibleRect = _getVisibleCanvasRectangle(jgraph); 3167 AffineTransform newTransform = jgraph.getCanvasPane() 3168 .getTransformContext().getTransform(); 3169 3170 newTransform.translate(visibleRect.getCenterX() - center.getX(), 3171 visibleRect.getCenterY() - center.getY()); 3172 3173 jgraph.getCanvasPane().setTransform(newTransform); 3174 3175 /* 3176 System.out.println("set pan to " + 3177 center.getX() + " " + center.getY() + " " + 3178 "for " + ((AbstractBasicGraphModel) jgraph.getGraphPane().getGraphModel()).getPtolemyModel().getFullName()); 3179 */ 3180 } 3181 3182 /** Set the directory that was last accessed by this window. 3183 * @see #getLastDirectory 3184 * @param directory The directory last accessed. 3185 * @deprecated Use {@link #setDirectory(File)} instead 3186 */ 3187 @Deprecated 3188 protected void _setDirectory(File directory) { 3189 setDirectory(directory); 3190 } 3191 3192 /** Enable or disable drop into. 3193 * @param enable False to disable. 3194 */ 3195 protected void _setDropIntoEnabled(boolean enable) { 3196 _dropTarget.setDropIntoEnabled(enable); 3197 } 3198 3199 /** 3200 * Update the size, zoom and position of the window for a specific model 3201 * and JGraph. This method is typically called when closing the window or 3202 * writing the moml file out. 3203 * @param parent The parent frame. 3204 * @param model The model. 3205 * @param graph The JGraph. 3206 * @exception IllegalActionException If there is a problem getting a 3207 * parameter. 3208 * @exception NameDuplicationException If there is a problem creating a 3209 * parameter. 3210 */ 3211 protected void _updateWindowAttributes(Frame parent, NamedObj model, 3212 JGraph graph) 3213 throws IllegalActionException, NameDuplicationException { 3214 3215 // If there is no parent that is a Frame, do nothing. 3216 // We know that: (parent == null) || (parent instanceof Frame) 3217 if (parent != null) { 3218 WindowPropertiesAttribute properties = (WindowPropertiesAttribute) model 3219 .getAttribute("_windowProperties", 3220 WindowPropertiesAttribute.class); 3221 3222 if (properties == null) { 3223 properties = new WindowPropertiesAttribute(model, 3224 "_windowProperties"); 3225 } 3226 3227 // This method uses MoMLChangeRequest 3228 properties.recordProperties(parent); 3229 } 3230 3231 _createSizeAttribute(model); 3232 3233 // Also record zoom and pan state. 3234 AffineTransform current = graph.getCanvasPane().getTransformContext() 3235 .getTransform(); 3236 3237 // We assume the scaling in the X and Y directions are the same. 3238 double scale = current.getScaleX(); 3239 Parameter zoom = (Parameter) model.getAttribute("_vergilZoomFactor", 3240 Parameter.class); 3241 3242 boolean updateValue = false; 3243 if (zoom == null || zoom.getToken() == null) { 3244 // NOTE: This will not propagate. 3245 zoom = new ExpertParameter(model, "_vergilZoomFactor"); 3246 zoom.setToken("1.0"); 3247 updateValue = true; 3248 } else { 3249 double oldZoom = ((DoubleToken) zoom.getToken()).doubleValue(); 3250 if (oldZoom != scale) { 3251 updateValue = true; 3252 } 3253 } 3254 3255 if (updateValue) { 3256 // Don't call setToken(), instead use a MoMLChangeRequest so that 3257 // the model is marked modified so that any changes are preserved. 3258 //zoom.setToken(new DoubleToken(scale)); 3259 String moml = "<property name=\"_vergilZoomFactor\" " + " value=\"" 3260 + scale + "\"/>"; 3261 MoMLChangeRequest request = new MoMLChangeRequest(this, model, 3262 moml); 3263 request.setUndoable(true); 3264 model.requestChange(request); 3265 3266 // Make sure the visibility is only expert. 3267 zoom.setVisibility(Settable.EXPERT); 3268 3269 //System.out.println("updating zoom to " + scale + " for " + model.getFullName()); 3270 } 3271 3272 // Save the center, to record the pan state. 3273 Point2D center = _getCenter(graph); 3274 Parameter pan = (Parameter) model.getAttribute("_vergilCenter", 3275 Parameter.class); 3276 3277 updateValue = false; 3278 if (pan == null || pan.getToken() == null) { 3279 // NOTE: This will not propagate. 3280 pan = new ExpertParameter(model, "_vergilCenter"); 3281 pan.setToken("{" + center.getX() + ", " + center.getY() + "}"); 3282 updateValue = true; 3283 } else { 3284 Token[] oldCenter = ((ArrayToken) pan.getToken()).arrayValue(); 3285 double oldCenterX = ((DoubleToken) oldCenter[0]).doubleValue(); 3286 double oldCenterY = ((DoubleToken) oldCenter[1]).doubleValue(); 3287 if (center.getX() != oldCenterX || center.getY() != oldCenterY) { 3288 updateValue = true; 3289 } 3290 } 3291 3292 if (updateValue) { 3293 //Token[] centerArray = new Token[2]; 3294 //centerArray[0] = new DoubleToken(center.getX()); 3295 //centerArray[1] = new DoubleToken(center.getY()); 3296 //pan.setToken(new ArrayToken(centerArray)); 3297 3298 String moml = "<property name=\"_vergilCenter\" " + " value=\"{" 3299 + center.getX() + ", " + center.getY() + "}\"/>"; 3300 MoMLChangeRequest request = new MoMLChangeRequest(this, model, 3301 moml); 3302 request.setUndoable(true); 3303 model.requestChange(request); 3304 3305 // Make sure the visibility is only expert. 3306 pan.setVisibility(Settable.EXPERT); 3307 3308 /* 3309 System.out.println("saving pan as " + 3310 center.getX() + " " + 3311 center.getY() + 3312 " for " + model.getFullName()); 3313 */ 3314 } 3315 } 3316 3317 /** Write the model to the specified file. This overrides the base 3318 * class to record the current size and position of the window 3319 * in the model. 3320 * @param file The file to write to. 3321 * @exception IOException If the write fails. 3322 */ 3323 @Override 3324 protected void _writeFile(File file) throws IOException { 3325 try { 3326 updateWindowAttributes(); 3327 } catch (KernelException ex) { 3328 // Ignore problems here. Errors simply result in a 3329 // default size and location. 3330 System.out.println( 3331 "While writing, failed to save size, position or zoom factor: " 3332 + ex); 3333 } 3334 3335 if (_isDesignPattern()) { 3336 FileWriter fileWriter = null; 3337 3338 try { 3339 fileWriter = new FileWriter(file); 3340 String name = getModel().getName(); 3341 String filename = file.getName(); 3342 int period = filename.indexOf("."); 3343 if (period > 0) { 3344 name = filename.substring(0, period); 3345 } else { 3346 name = filename; 3347 } 3348 _exportDesignPattern(fileWriter, getModel(), name); 3349 } finally { 3350 if (fileWriter != null) { 3351 fileWriter.close(); 3352 } 3353 } 3354 } else { 3355 super._writeFile(file); 3356 } 3357 } 3358 3359 /** Return the MoML to delete the specified selection objects. 3360 * This has the side effect of unselecting the objects. It also 3361 * deletes edges that are not fully connected (these deletions 3362 * cannot be done through MoML, and cannot be undone). 3363 * @param graphModel The graph model. 3364 * @param selection The selection. 3365 * @param model The selection model. 3366 * @return The MoML to delete the selected objects. 3367 */ 3368 protected StringBuffer _deleteMoML(AbstractBasicGraphModel graphModel, 3369 Object[] selection, SelectionModel model) { 3370 3371 // First collect selected objects into the userObjects array 3372 // and deselect them. 3373 Object[] userObjects = new Object[selection.length]; 3374 for (int i = 0; i < selection.length; i++) { 3375 userObjects[i] = ((Figure) selection[i]).getUserObject(); 3376 model.removeSelection(selection[i]); 3377 } 3378 3379 // Create a set to hold those elements whose deletion 3380 // does not go through MoML. This is only links that 3381 // are not connected to another port or a relation. 3382 HashSet<Object> edgeSet = new HashSet<Object>(); 3383 3384 StringBuffer moml = new StringBuffer("<group>\n"); 3385 3386 // Delete edges then nodes, since deleting relations may 3387 // result in deleting links to that relation. 3388 for (int i = 0; i < selection.length; i++) { 3389 Object userObject = userObjects[i]; 3390 3391 if (graphModel.isEdge(userObject)) { 3392 NamedObj actual = (NamedObj) graphModel 3393 .getSemanticObject(userObject); 3394 3395 // If there is no semantic object, then this edge is 3396 // not fully connected, so we can't go through MoML. 3397 if (actual == null) { 3398 edgeSet.add(userObject); 3399 } else { 3400 moml.append(graphModel.getDeleteEdgeMoML(userObject)); 3401 } 3402 } 3403 } 3404 3405 // First, delete all the non-attributes. 3406 // This helps avoid deleting properties such as top level parameters 3407 // upon which the entities depend. 3408 // FIXME: what if we have a parameter that is used by both the selection 3409 // and the other parts of the model? 3410 for (int i = 0; i < selection.length; i++) { 3411 Object userObject = userObjects[i]; 3412 3413 NamedObjNodeModel namedObjNodeModel = (NamedObjNodeModel) graphModel 3414 .getNodeModel(userObject); 3415 if (graphModel.isNode(userObject) 3416 && !(namedObjNodeModel instanceof AttributeNodeModel)) { 3417 NamedObj actual = (NamedObj) graphModel 3418 .getSemanticObject(userObject); 3419 if (!(actual instanceof ParameterPort)) { 3420 // We don't delete ParameterPorts here because if 3421 // we drag a region around a ParmeterPort, then 3422 // both the PortParameter and the ParameterPort 3423 // are selected. Deleting both results in an 3424 // error. If we just click (not drag) on a 3425 // ParameterPort, then the PortParameter is only 3426 // selected and deletion work ok. See 3427 // https://chess.eecs.berkeley.edu/bugzilla/show_bug.cgi?id=311 3428 moml.append(graphModel.getDeleteNodeMoML(userObject)); 3429 } 3430 } 3431 } 3432 3433 // Now delete attributes. 3434 for (int i = 0; i < selection.length; i++) { 3435 Object userObject = userObjects[i]; 3436 3437 NamedObjNodeModel namedObjNodeModel = (NamedObjNodeModel) graphModel 3438 .getNodeModel(userObject); 3439 if (graphModel.isNode(userObject) 3440 && namedObjNodeModel instanceof AttributeNodeModel) { 3441 moml.append(graphModel.getDeleteNodeMoML(userObject)); 3442 } 3443 } 3444 3445 moml.append("</group>\n"); 3446 3447 // Have both MoML to perform deletion and set of objects whose 3448 // deletion does not go through MoML. This set of objects 3449 // should be very small and so far consists of only links that are not 3450 // connected to a relation 3451 try { 3452 // First manually delete any objects whose deletion does not go 3453 // through MoML and so are not undoable 3454 // Note that we turn off event dispatching so that each individual 3455 // removal does not trigger graph redrawing. 3456 graphModel.setDispatchEnabled(false); 3457 3458 Iterator<Object> edges = edgeSet.iterator(); 3459 3460 while (edges.hasNext()) { 3461 Object nextEdge = edges.next(); 3462 3463 if (graphModel.isEdge(nextEdge)) { 3464 graphModel.disconnectEdge(this, nextEdge); 3465 } 3466 } 3467 } finally { 3468 graphModel.setDispatchEnabled(true); 3469 } 3470 3471 return moml; 3472 } 3473 3474 /** Zoom in or out to magnify by the specified factor, from the current 3475 * magnification for a specific JGraph. 3476 * @param jgraph The JGraph. 3477 * @param factor The magnification factor (relative to 1.0). 3478 */ 3479 protected void _zoom(JGraph jgraph, double factor) { 3480 try { 3481 _zoomFlag = true; 3482 JCanvas canvas = jgraph.getGraphPane().getCanvas(); 3483 AffineTransform current = canvas.getCanvasPane() 3484 .getTransformContext().getTransform(); 3485 3486 // Save the center, so we remember what we were looking at. 3487 Point2D center = _getCenter(jgraph); 3488 current.scale(factor, factor); 3489 canvas.getCanvasPane().setTransform(current); 3490 3491 /* 3492 System.out.println("set zoom to " + 3493 factor + " " + 3494 "for " + ((AbstractBasicGraphModel) jgraph.getGraphPane().getGraphModel()).getPtolemyModel().getFullName()); 3495 */ 3496 3497 // Reset the center. 3498 _setCenter(jgraph, center); 3499 3500 // Only repaint the panner if it exists and is for the 3501 // same JGraph. 3502 if (_graphPanner != null && _graphPanner.getCanvas() == jgraph) { 3503 _graphPanner.repaint(); 3504 } 3505 } finally { 3506 _zoomFlag = false; 3507 } 3508 } 3509 3510 /////////////////////////////////////////////////////////////////// 3511 //// protected variables //// 3512 3513 /** The copy action. */ 3514 protected Action _copyAction; 3515 3516 /** The cut action. */ 3517 protected Action _cutAction; 3518 3519 /** The default Library. **/ 3520 protected LibraryAttribute _defaultLibrary; 3521 3522 /** The instance of EditorDropTarget associated with the JGraph. */ 3523 protected EditorDropTarget _dropTarget; 3524 3525 /** The edit menu. */ 3526 protected JMenu _editMenu; 3527 3528 /** The action to edit preferences. */ 3529 protected EditPreferencesAction _editPreferencesAction; 3530 3531 /** The export to GIF action. */ 3532 protected Action _exportGIFAction; 3533 3534 /** The export HTML action. */ 3535 protected Action _exportHTMLAction; 3536 3537 /** The export to PDF action. */ 3538 protected Action _exportPDFAction; 3539 3540 /** The export to PNG action. */ 3541 protected Action _exportPNGAction; 3542 3543 /** The find action. */ 3544 protected Action _findAction; 3545 3546 /** The graph menu. */ 3547 protected JMenu _graphMenu; 3548 3549 /** The panner. Note that this variable 3550 * can be null if the configuration does not have an entity named 3551 * "actor library". For example, see $PTII/bin/vergil -ptinyViewer. 3552 */ 3553 protected JCanvasPanner _graphPanner; 3554 3555 /** The instance of JGraph for this editor. */ 3556 protected JGraph _jgraph; 3557 3558 /** The action for automatically laying out the graph. 3559 * This can be either an advanced layout or the simple Ptolemy layout, 3560 * depending on whether the better one is available. 3561 */ 3562 protected Action _layoutAction; 3563 3564 /** The action for opening the layout configuration dialog. 3565 * This reference can be {@code null}, since the dialog is only supported 3566 * if advanced layout is available. In this case the action should not 3567 * be shown in menus. 3568 */ 3569 protected Action _layoutConfigDialogAction; 3570 3571 /** The library display widget. */ 3572 protected JTree _library; 3573 3574 /** The library context menu creator. */ 3575 protected PTreeMenuCreator _libraryContextMenuCreator; 3576 3577 /** The library model. */ 3578 protected EntityTreeModel _libraryModel; 3579 3580 /** The library scroll pane. */ 3581 protected JScrollPane _libraryScrollPane; 3582 3583 /** Action to move to the back. */ 3584 protected MoveToBackAction _moveToBackAction; 3585 3586 /** Action to move to the front. */ 3587 protected MoveToFrontAction _moveToFrontAction; 3588 3589 /** List of references to graph frames that are open. */ 3590 protected static LinkedList<BasicGraphFrame> _openGraphFrames = new LinkedList<BasicGraphFrame>(); 3591 3592 /** The library display panel. */ 3593 protected JPanel _palettePane; 3594 3595 /** The paste action. */ 3596 protected Action _pasteAction; 3597 3598 /** The action for reloading accessors. */ 3599 protected Action _reloadAccessorsAction; 3600 3601 /** The right component for this editor. */ 3602 protected JComponent _rightComponent; 3603 3604 /** The split pane for library and editor. Note that this variable 3605 * can be null if the configuration does not have an entity named 3606 * "actor library". For example, see $PTII/bin/vergil -ptinyViewer. 3607 */ 3608 protected JSplitPane _splitPane; 3609 3610 /** The toolbar. */ 3611 protected JToolBar _toolbar; 3612 3613 /** The library. */ 3614 protected CompositeEntity _topLibrary; 3615 3616 /** The tree view of the model, used for browsing large models. */ 3617 protected PTree _treeView; 3618 3619 /** The tree view scroll pane. */ 3620 protected JScrollPane _treeViewScrollPane; 3621 3622 /** The tree view model. */ 3623 protected ClassAndEntityTreeModel _treeViewModel; 3624 3625 /** Action for zoom fitting. */ 3626 protected Action _zoomFitAction = new ZoomFitAction("Zoom Fit"); 3627 3628 /** Action for zooming in. */ 3629 protected Action _zoomInAction = new ZoomInAction("Zoom In"); 3630 3631 /** Action for zooming out. */ 3632 protected Action _zoomOutAction = new ZoomOutAction("Zoom Out"); 3633 3634 /** Action for zoom reset. */ 3635 protected Action _zoomResetAction = new ZoomResetAction("Zoom Reset"); 3636 3637 /** True if we are inside zoom(). Used by derived classes with scrollbars. 3638 */ 3639 protected boolean _zoomFlag = false; 3640 3641 /////////////////////////////////////////////////////////////////// 3642 //// private methods //// 3643 3644 /** 3645 * Create an action for advanced automatic layout, if possible. 3646 * 3647 * @return a layout action, or null if it cannot be created 3648 */ 3649 private IGuiAction _createLayoutAction() { 3650 try { 3651 StringParameter layoutGraphActionParameter = (StringParameter) getConfiguration() 3652 .getAttribute("_layoutGraphAction", StringParameter.class); 3653 if (layoutGraphActionParameter != null) { 3654 // Try to find the class given in the configuration. 3655 Class layoutGraphActionClass = Class 3656 .forName(layoutGraphActionParameter.stringValue()); 3657 3658 // Try to create an instance using the default constructor. 3659 Object object = layoutGraphActionClass.getDeclaredConstructor() 3660 .newInstance(); 3661 3662 if (object instanceof IGuiAction) { 3663 // If the action is a filter and the model is set, ask the action 3664 // whether is supports the model. 3665 if (object instanceof Filter && getModel() != null) { 3666 if (!((Filter) object).accept(getModel())) { 3667 return null; 3668 } 3669 } 3670 3671 return (IGuiAction) object; 3672 } 3673 } 3674 } catch (Throwable throwable) { 3675 // Fail silently! 3676 } 3677 return null; 3678 } 3679 3680 /////////////////////////////////////////////////////////////////// 3681 //// private variables //// 3682 3683 /** The entry box for find in library. */ 3684 JTextField _findInLibraryEntryBox; 3685 3686 /** A layer adapter to handle the mousePressed event. */ 3687 private MousePressedLayerAdapter _mousePressedLayerAdapter; 3688 3689 /** Action for opening the container, moving uplevel. */ 3690 private Action _openContainerAction = new OpenContainerAction( 3691 "Open the container"); 3692 3693 /** X coordinate of where we last processed a press or drag of the 3694 * middle mouse button. 3695 */ 3696 private int _previousMouseX = 0; 3697 3698 /** Y coordinate of where we last processed a press or drag of the 3699 * middle mouse button. 3700 */ 3701 private int _previousMouseY = 0; 3702 3703 /** Action to print the model. */ 3704 private Action _printAction = new PrintAction("Print"); 3705 3706 /** True if the message about problems reading 3707 * _importActionClassNames has been printed. 3708 */ 3709 private static boolean _printedImportActionClassNamesMessage = false; 3710 3711 /** Action to redo the last undone MoML change. */ 3712 private Action _redoAction = new RedoAction(); 3713 3714 /** Action to save the model. */ 3715 private Action _saveAction = new SaveAction("Save"); 3716 3717 /** Action to undo the last MoML change. */ 3718 private Action _undoAction = new UndoAction(); 3719 3720 private static double _ZOOM_FIT_PADDING = 5.0; 3721 3722 /////////////////////////////////////////////////////////////////// 3723 //// inner classes //// 3724 3725 /** A Layer Adapter to handle the mousePressed layer event. */ 3726 protected static class MousePressedLayerAdapter extends LayerAdapter { 3727 // FindBugs indicates that this should be a static class. 3728 3729 /** Invoked when the mouse is pressed on a layer 3730 * or figure. 3731 */ 3732 @Override 3733 public void mousePressed(LayerEvent event) { 3734 Component component = event.getComponent(); 3735 3736 if (!component.hasFocus()) { 3737 component.requestFocus(); 3738 } 3739 } 3740 } 3741 3742 /////////////////////////////////////////////////////////////////// 3743 //// CopyAction 3744 3745 /** Action to copy the current selection. */ 3746 protected class CopyAction extends AbstractAction { 3747 /** Create a new action to copy the current selection. */ 3748 public CopyAction() { 3749 super("Copy"); 3750 putValue("tooltip", 3751 "Copy the current selection onto the clipboard."); 3752 putValue(GUIUtilities.ACCELERATOR_KEY, KeyStroke.getKeyStroke( 3753 KeyEvent.VK_C, 3754 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); 3755 putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_C)); 3756 } 3757 3758 /** Copy the current selection. */ 3759 @Override 3760 public void actionPerformed(ActionEvent e) { 3761 copy(); 3762 } 3763 } 3764 3765 /////////////////////////////////////////////////////////////////// 3766 //// CutAction 3767 3768 /** Action to copy and delete the current selection. */ 3769 protected class CutAction extends AbstractAction { 3770 /** Create a new action to copy and delete the current selection. */ 3771 public CutAction() { 3772 super("Cut"); 3773 putValue("tooltip", 3774 "Cut the current selection onto the clipboard."); 3775 putValue(GUIUtilities.ACCELERATOR_KEY, KeyStroke.getKeyStroke( 3776 KeyEvent.VK_X, 3777 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); 3778 putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_T)); 3779 } 3780 3781 /** Copy and delete the current selection. */ 3782 @Override 3783 public void actionPerformed(ActionEvent e) { 3784 cut(); 3785 } 3786 } 3787 3788 /////////////////////////////////////////////////////////////////// 3789 //// DeletionListener 3790 3791 /** An ActionListener for handling deletion events. */ 3792 private class DeletionListener implements ActionListener { 3793 /** Delete any nodes or edges from the graph that are 3794 * currently selected. In addition, delete any edges 3795 * that are connected to any deleted nodes. 3796 */ 3797 @Override 3798 public void actionPerformed(ActionEvent e) { 3799 delete(); 3800 } 3801 } 3802 3803 /////////////////////////////////////////////////////////////////// 3804 //// DocumentationMenuItemFactory 3805 3806 /** 3807 * Create a menu item that will show documentation 3808 */ 3809 private class DocumentationMenuItemFactory implements MenuItemFactory { 3810 /** 3811 * Add an item to the given context menu that bring up the 3812 * documentation for the given object 3813 */ 3814 @Override 3815 public JMenuItem create(final JContextMenu menu, 3816 final NamedObj object) { 3817 Action action = new GetDocumentationAction() { 3818 @Override 3819 public void actionPerformed(ActionEvent e) { 3820 Configuration configuration = getConfiguration(); 3821 setConfiguration(configuration); 3822 super.actionPerformed(e); 3823 } 3824 }; 3825 3826 action.putValue("tooltip", "Get Documentation."); 3827 action.putValue(diva.gui.GUIUtilities.MNEMONIC_KEY, 3828 Integer.valueOf(KeyEvent.VK_D)); 3829 return menu.add(action, (String) action.getValue(Action.NAME)); 3830 } 3831 } 3832 3833 /////////////////////////////////////////////////////////////////// 3834 //// EditPreferencesAction 3835 3836 /** Action to edit the preferences. 3837 */ 3838 protected class EditPreferencesAction extends AbstractAction { 3839 public EditPreferencesAction() { 3840 super("Edit Preferences"); 3841 putValue("tooltip", "Change the Vergil preferences"); 3842 putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_E)); 3843 } 3844 3845 @Override 3846 public void actionPerformed(ActionEvent e) { 3847 Configuration configuration = getConfiguration(); 3848 PtolemyPreferences preferences = null; 3849 3850 try { 3851 preferences = (PtolemyPreferences) configuration.getAttribute( 3852 PtolemyPreferences.PREFERENCES_WITHIN_CONFIGURATION, 3853 PtolemyPreferences.class); 3854 } catch (IllegalActionException ex) { 3855 MessageHandler.error("Preferences attribute found, " 3856 + "but not of the right class.", ex); 3857 } 3858 3859 if (preferences == null) { 3860 MessageHandler 3861 .message("No preferences given in the configuration."); 3862 } else { 3863 // Open a modal dialog to edit the parameters. 3864 new EditParametersDialog(BasicGraphFrame.this, preferences, 3865 "Edit Ptolemy Preferences"); 3866 3867 // Make the current global variables conform with the 3868 // new values. 3869 try { 3870 preferences.setAsDefault(); 3871 } catch (IllegalActionException ex) { 3872 MessageHandler.error("Invalid expression.", ex); 3873 actionPerformed(e); 3874 } 3875 3876 // If any parameter has changed, all open vergil 3877 // windows need to be notified. 3878 Iterator<BasicGraphFrame> frames = _openGraphFrames.iterator(); 3879 3880 while (frames.hasNext()) { 3881 BasicGraphFrame frame = frames.next(); 3882 GraphModel graphModel = frame._getGraphController() 3883 .getGraphModel(); 3884 graphModel.dispatchGraphEvent( 3885 new GraphEvent(this, GraphEvent.STRUCTURE_CHANGED, 3886 graphModel.getRoot())); 3887 3888 if (frame._graphPanner != null) { 3889 frame._graphPanner.repaint(); 3890 } 3891 } 3892 3893 // Make the changes persistent. 3894 try { 3895 preferences.save(); 3896 } catch (IOException ex) { 3897 try { 3898 MessageHandler.warning("Failed to save preferences.", 3899 ex); 3900 } catch (CancelException e1) { 3901 // Ignore cancel. 3902 } 3903 } 3904 } 3905 } 3906 } 3907 3908 /////////////////////////////////////////////////////////////////// 3909 //// ElementInLinkType 3910 /** 3911 * An enumerate to specifies what kind of element the element (head or tail) is in a link. 3912 */ 3913 private enum ElementInLinkType { 3914 PORT_IN_ACTOR, STANDALONE_PORT, RELATION 3915 } 3916 3917 /////////////////////////////////////////////////////////////////// 3918 //// ExecuteSystemAction 3919 3920 /** An action to open a run control window. */ 3921 // private class ExecuteSystemAction extends AbstractAction { 3922 // /** Construct an action to execute the model. */ 3923 // public ExecuteSystemAction() { 3924 // super("Go"); 3925 // putValue("tooltip", "Execute The Model"); 3926 // putValue(GUIUtilities.ACCELERATOR_KEY, KeyStroke.getKeyStroke( 3927 // KeyEvent.VK_G, Toolkit.getDefaultToolkit() 3928 // .getMenuShortcutKeyMask())); 3929 // putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_G)); 3930 // } 3931 // 3932 // /** Open a run control window. */ 3933 // public void actionPerformed(ActionEvent e) { 3934 // try { 3935 // PtolemyEffigy effigy = (PtolemyEffigy) getTableau() 3936 // .getContainer(); 3937 // new RunTableau(effigy, effigy.uniqueName("tableau")); 3938 // } catch (Exception ex) { 3939 // MessageHandler.error("Execution Failed", ex); 3940 // } 3941 // } 3942 // } 3943 3944 /////////////////////////////////////////////////////////////////// 3945 //// ExportImageAction 3946 3947 /** Export an image of the model. */ 3948 public class ExportImageAction extends AbstractAction { 3949 /** Create a new action to export an image. 3950 * @param formatName The image format to be exported, 3951 * currently, "GIF" and "PNG" are supported. 3952 */ 3953 public ExportImageAction(String formatName) { 3954 super("Export " + formatName); 3955 _formatName = formatName.toLowerCase(Locale.getDefault()); 3956 putValue("tooltip", "Export " + formatName + " image to a file."); 3957 // putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_G)); 3958 } 3959 3960 /////////////////////////////////////////////////////////////////// 3961 //// public methods //// 3962 3963 /** Export an image. 3964 * 3965 * <p> Under Mac OS X, use java.awt.FileDialog. 3966 * Under other OS's, use javax.swing.JFileChooser. Under Mac OS 3967 * X, see {@link ptolemy.gui.PtGUIUtilities#useFileDialog()} for 3968 * how to select between the two.</p> 3969 * 3970 * @param e The event that triggered this action. 3971 */ 3972 @Override 3973 public void actionPerformed(ActionEvent e) { 3974 JFileChooserBugFix jFileChooserBugFix = new JFileChooserBugFix(); 3975 Color background = null; 3976 PtFileChooser ptFileChooser = null; 3977 try { 3978 background = jFileChooserBugFix.saveBackground(); 3979 3980 ptFileChooser = new PtFileChooser(BasicGraphFrame.this, 3981 "Specify a " + _formatName + " file to be written.", 3982 JFileChooser.SAVE_DIALOG); 3983 3984 ptFileChooser.setSelectedFile( 3985 new File(getModel().getName() + "." + _formatName)); 3986 LinkedList extensions = new LinkedList(); 3987 extensions.add(_formatName); 3988 ptFileChooser.addChoosableFileFilter( 3989 new ExtensionFilenameFilter(extensions)); 3990 ptFileChooser.setCurrentDirectory(_directory); 3991 3992 int returnVal = ptFileChooser.showDialog(BasicGraphFrame.this, 3993 "Export " 3994 + _formatName.toUpperCase(Locale.getDefault())); 3995 3996 if (returnVal == JFileChooser.APPROVE_OPTION) { 3997 _directory = ptFileChooser.getCurrentDirectory(); 3998 File imageFile = ptFileChooser.getSelectedFile() 3999 .getCanonicalFile(); 4000 4001 if (imageFile.getName().indexOf(".") == -1) { 4002 // If the user has not given the file an extension, add it 4003 imageFile = new File(imageFile.getAbsolutePath() + "." 4004 + _formatName); 4005 } 4006 // The Mac OS X FileDialog will ask if we want to save before this point. 4007 if (imageFile.exists() && !PtGUIUtilities.useFileDialog()) { 4008 if (!MessageHandler.yesNoQuestion( 4009 "Overwrite \"" + imageFile.getName() + "\"?")) { 4010 return; 4011 } 4012 } 4013 OutputStream out = null; 4014 try { 4015 out = new FileOutputStream(imageFile); 4016 getJGraph().exportImage(out, _formatName); 4017 } finally { 4018 if (out != null) { 4019 out.close(); 4020 } 4021 } 4022 4023 // Open the image file. 4024 if (MessageHandler.yesNoQuestion( 4025 "Open \"" + imageFile.getCanonicalPath() 4026 + "\" in a browser?")) { 4027 Configuration configuration = getConfiguration(); 4028 try { 4029 URL imageURL = new URL( 4030 imageFile.toURI().toURL().toString() 4031 + "#in_browser"); 4032 configuration.openModel(imageURL, imageURL, 4033 imageURL.toExternalForm(), 4034 BrowserEffigy.staticFactory); 4035 } catch (Throwable throwable) { 4036 MessageHandler.error("Failed to open \"" 4037 + imageFile.getName() + "\".", throwable); 4038 } 4039 } 4040 } 4041 } catch (Exception ex) { 4042 MessageHandler.error("Export to " 4043 + _formatName.toUpperCase(Locale.getDefault()) 4044 + " failed", ex); 4045 } finally { 4046 jFileChooserBugFix.restoreBackground(background); 4047 } 4048 } 4049 4050 private String _formatName; 4051 } 4052 4053 /////////////////////////////////////////////////////////////////// 4054 //// ExportMapAction 4055 4056 /** Accept only folders in a file browser. */ 4057 static public class FolderFileFilter extends FileFilter { 4058 /** Accept only folders. 4059 * @param fileOrDirectory The file or directory to be checked. 4060 * @return true if the file is a directory. 4061 */ 4062 @Override 4063 public boolean accept(File fileOrDirectory) { 4064 if (fileOrDirectory.isDirectory()) { 4065 return true; 4066 } 4067 return false; 4068 } 4069 4070 /** The description of this filter. */ 4071 @Override 4072 public String getDescription() { 4073 return "Choose a Folder"; 4074 } 4075 } 4076 4077 /////////////////////////////////////////////////////////////////// 4078 //// FindAction 4079 4080 /** Action to search for text in a model. */ 4081 protected class FindAction extends AbstractAction { 4082 /** Create a new action to search for text. */ 4083 public FindAction() { 4084 super("Find"); 4085 putValue("tooltip", "Find occurrences of specified text."); 4086 putValue(GUIUtilities.ACCELERATOR_KEY, KeyStroke.getKeyStroke( 4087 KeyEvent.VK_F, 4088 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); 4089 putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_F)); 4090 } 4091 4092 /** Open a dialog to find the specified text. */ 4093 @Override 4094 public void actionPerformed(ActionEvent e) { 4095 DialogTableau dialogTableau = DialogTableau.createDialog( 4096 BasicGraphFrame.this, getConfiguration(), getEffigy(), 4097 SearchResultsDialog.class, (Entity) getModel()); 4098 4099 if (dialogTableau != null) { 4100 dialogTableau.show(); 4101 } 4102 } 4103 } 4104 4105 /////////////////////////////////////////////////////////////////// 4106 //// FindInLibraryAction 4107 4108 /** An ActionListener for handling deletion events. */ 4109 private class FindInLibraryAction implements ActionListener { 4110 /** Delete any nodes or edges from the graph that are 4111 * currently selected. In addition, delete any edges 4112 * that are connected to any deleted nodes. 4113 */ 4114 @Override 4115 public void actionPerformed(ActionEvent e) { 4116 _library.clearSelection(); 4117 String text = _findInLibraryEntryBox.getText().trim() 4118 .toLowerCase(Locale.getDefault()); 4119 if (text.equals("")) { 4120 // Nothing to search for. Ignore. 4121 _previousText = null; 4122 return; 4123 } 4124 NamedObj root = (NamedObj) _libraryModel.getRoot(); 4125 if (!text.equals(_previousText)) { 4126 // Restart the search from the beginning. 4127 if (_stack == null) { 4128 _stack = new Stack<NamedObj>(); 4129 _indexes = new Stack<Integer>(); 4130 } else { 4131 _stack.clear(); 4132 _indexes.clear(); 4133 } 4134 _stack.push(root); 4135 _indexes.push(Integer.valueOf(1)); 4136 } 4137 _previousText = text; 4138 try { 4139 // Indicate that something is happening with the cursor. 4140 // setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 4141 _findInLibraryEntryBox.setCursor( 4142 Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 4143 _library.setCursor( 4144 Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 4145 4146 // Put a message in the progress bar at the bottom of the window. 4147 // FIXME: Sadly, this doesn't work. 4148 report("Opening Libraries ..."); 4149 4150 // Traverse the model until we get a match. 4151 // _stack will be empty only when no further match is found. 4152 boolean foundOne = false; 4153 int index = 0; 4154 while (!_stack.isEmpty()) { 4155 NamedObj parent = _stack.peek(); 4156 if (_findInSublibrary(text, parent, index)) { 4157 // Found one. Stop the search. 4158 foundOne = true; 4159 break; 4160 } else { 4161 // Pop up one level and continue the search. 4162 _stack.pop(); 4163 index = _indexes.pop(); 4164 } 4165 } 4166 if (!foundOne) { 4167 // Reached the end of the library. 4168 try { 4169 if (MessageHandler.yesNoCancelQuestion( 4170 "Reached the end of the library. Start search again from the top?")) { 4171 // Restart the search. 4172 _stack.clear(); 4173 _indexes.clear(); 4174 _stack.push(root); 4175 _indexes.push(Integer.valueOf(0)); 4176 4177 actionPerformed(e); 4178 } 4179 } catch (CancelException e1) { 4180 // Canceled by user. 4181 return; 4182 } 4183 } 4184 } finally { 4185 // Restore the cursor. 4186 // setCursor(Cursor.getDefaultCursor()); 4187 _findInLibraryEntryBox.setCursor(Cursor.getDefaultCursor()); 4188 _library.setCursor(Cursor.getDefaultCursor()); 4189 4190 // Clear the message in the progress bar at the bottom of the window. 4191 report(""); 4192 } 4193 } 4194 4195 /** Search the specified library for a name or class name that 4196 * contains the specified text. 4197 * @param text The text to search for. 4198 * @param library The library to search. 4199 * @param index The index at which to start the search. 4200 * @return True if a match is found. 4201 */ 4202 private boolean _findInSublibrary(String text, NamedObj library, 4203 int index) { 4204 int count = _libraryModel.getChildCount(library); 4205 for (int i = index; i < count; i++) { 4206 NamedObj candidate = (NamedObj) _libraryModel.getChild(library, 4207 i); 4208 String name = candidate.getName(); 4209 if (name.toLowerCase(Locale.getDefault()).contains(text)) { 4210 // Found a match to the name. 4211 _stack.push(candidate); 4212 Object[] path = _stack.toArray(); 4213 TreePath pathToHit = new TreePath(path); 4214 _library.makeVisible(pathToHit); 4215 _library.scrollPathToVisible(pathToHit); 4216 _library.addSelectionPath(pathToHit); 4217 // Start the next search at the next item. 4218 _indexes.push(Integer.valueOf(i + 1)); 4219 return true; 4220 } 4221 // Not a match. See whether any of its children are a match. 4222 int childCount = 0; 4223 ErrorHandler momlErrorHandler = MoMLParser.getErrorHandler(); 4224 MoMLParser.setErrorHandler(new SimpleErrorHandler()); 4225 MessageHandler messageHandler = MessageHandler 4226 .getMessageHandler(); 4227 MessageHandler.setMessageHandler(new SimpleMessageHandler()); 4228 try { 4229 childCount = _libraryModel.getChildCount(candidate); 4230 } catch (Throwable throwable) { 4231 report("Skipping opening " + candidate.getName() + ": " 4232 + throwable); 4233 } finally { 4234 MoMLParser.setErrorHandler(momlErrorHandler); 4235 MessageHandler.setMessageHandler(messageHandler); 4236 } 4237 if (!_libraryModel.isLeaf(candidate) && childCount > 0) { 4238 _stack.push(candidate); 4239 _indexes.push(Integer.valueOf(i + 1)); 4240 if (_findInSublibrary(text, candidate, 0)) { 4241 return true; 4242 } else { 4243 _stack.pop(); 4244 _indexes.pop(); 4245 } 4246 } 4247 } 4248 return false; 4249 } 4250 4251 private String _previousText; 4252 private Stack<NamedObj> _stack; 4253 private Stack<Integer> _indexes; 4254 } 4255 4256 /////////////////////////////////////////////////////////////////// 4257 //// LinkElementProperties 4258 /** 4259 * A class that keeps stores basic properties of element (head, tail) in a link 4260 */ 4261 static private class LinkElementProperties { 4262 /** 4263 * Create a LinkElementProperties from the element (head or tail), a port if one is available and the ElementInLinkType 4264 */ 4265 LinkElementProperties(Object element, IOPort port, 4266 ElementInLinkType type) { 4267 this.element = element; 4268 this.port = port; 4269 this.type = type; 4270 } 4271 4272 /** 4273 * Extract the properties from an element (head or tail) in a link a return these as an ElementInLinkType 4274 */ 4275 static LinkElementProperties extractLinkProperties(Object element) { 4276 IOPort elementPort = null; 4277 ElementInLinkType elementType = ElementInLinkType.PORT_IN_ACTOR; 4278 if (element instanceof IOPort) { 4279 //This is a port of an actor 4280 elementPort = (IOPort) element; 4281 elementType = ElementInLinkType.PORT_IN_ACTOR; 4282 } else if (element instanceof Location) { 4283 //Either a port (not one of an actor) or a relation 4284 NamedObj elementContainer = ((Location) element).getContainer(); 4285 if (elementContainer instanceof IOPort) { 4286 //This is a port 4287 elementPort = (IOPort) elementContainer; 4288 elementType = ElementInLinkType.STANDALONE_PORT; 4289 } else { 4290 //This is a relation 4291 assert elementContainer instanceof IORelation; 4292 elementType = ElementInLinkType.RELATION; 4293 } 4294 } 4295 return new LinkElementProperties(element, elementPort, elementType); 4296 } 4297 4298 public final Object element; 4299 public final IOPort port; 4300 public final ElementInLinkType type; 4301 } 4302 4303 /////////////////////////////////////////////////////////////////// 4304 //// HierarchyTreeCellRenderer 4305 4306 /** Render a cell in the model hierarchy tree. The model being 4307 * displayed is highlighted. 4308 */ 4309 class HierarchyTreeCellRenderer extends PtolemyTreeCellRenderer { 4310 4311 /** Create a new rendition for the given object. 4312 * If the object is the same as the currently displayed Ptolemy 4313 * model, then make it bold. 4314 */ 4315 @Override 4316 public Component getTreeCellRendererComponent(JTree tree, Object value, 4317 boolean sel, boolean expanded, boolean leaf, int row, 4318 boolean hasFocus) { 4319 4320 DefaultTreeCellRenderer component = (DefaultTreeCellRenderer) super.getTreeCellRendererComponent( 4321 tree, value, selected, expanded, leaf, row, hasFocus); 4322 NamedObj model = getModel(); 4323 if (model != null && component != null && model.equals(value)) { 4324 component.setText( 4325 "<html><b>" + component.getText() + "</b></html>"); 4326 } 4327 return this; 4328 } 4329 } 4330 4331 /////////////////////////////////////////////////////////////////// 4332 //// HierarchyTreeSelectionListener 4333 4334 /** The user selected a node in the Hierarchy tree browser */ 4335 // Replaced by mouse listener 4336 /* 4337 private class HierarchyTreeSelectionListener implements 4338 TreeSelectionListener { 4339 // The value of the selection in the model hierarchy tree 4340 // browser changed. 4341 @Override 4342 public void valueChanged(TreeSelectionEvent event) { 4343 // Returns the last path element of the selection. 4344 // This method is useful only when the selection model allows a single selection. 4345 Object lastSelectedPathComponent = _treeView 4346 .getLastSelectedPathComponent(); 4347 if (lastSelectedPathComponent instanceof NamedObj) { 4348 try { 4349 getConfiguration().openInstance( 4350 (NamedObj) lastSelectedPathComponent); 4351 } catch (Throwable throwable) { 4352 MessageHandler.error("Could not open " 4353 + lastSelectedPathComponent, throwable); 4354 } 4355 } 4356 } 4357 } 4358 */ 4359 4360 /////////////////////////////////////////////////////////////////// 4361 //// HierarchyTreeMouseAdapter 4362 4363 /** Listen for clicks of the mouse on the tree. 4364 */ 4365 private class HierarchyTreeMouseAdapter extends MouseAdapter { 4366 @Override 4367 public void mousePressed(MouseEvent e) { 4368 if (e.getClickCount() == 2) { 4369 // Returns the last path element of the selection. 4370 // This method is useful only when the selection model allows a single selection. 4371 Object lastSelectedPathComponent = _treeView 4372 .getLastSelectedPathComponent(); 4373 if (lastSelectedPathComponent instanceof NamedObj) { 4374 try { 4375 getConfiguration().openInstance( 4376 (NamedObj) lastSelectedPathComponent); 4377 } catch (Throwable throwable) { 4378 MessageHandler.error( 4379 "Could not open " + lastSelectedPathComponent, 4380 throwable); 4381 } 4382 } 4383 } 4384 } 4385 } 4386 4387 /////////////////////////////////////////////////////////////////// 4388 //// MoveToBackAction 4389 /** Action to move the current selection to the back (which corresponds 4390 * to first in the ordered list). 4391 */ 4392 protected class MoveToBackAction extends AbstractAction { 4393 public MoveToBackAction() { 4394 // Note that we also have "Send to Back" in 4395 // vergil/kernel/AttributeController.java 4396 super("Send to Back"); 4397 putValue("tooltip", "Send to back of like objects"); 4398 putValue(GUIUtilities.ACCELERATOR_KEY, KeyStroke.getKeyStroke( 4399 KeyEvent.VK_B, 4400 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); 4401 putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_B)); 4402 } 4403 4404 @Override 4405 public void actionPerformed(ActionEvent e) { 4406 final NamedObj container = (NamedObj) _getGraphModel().getRoot(); 4407 4408 // Get the selection objects. 4409 // NOTE: The order in the model must be respected. 4410 HashSet<NamedObj> namedObjSet = _getSelectionSet(); 4411 final List<NamedObj> elements = container 4412 .sortContainedObjects(namedObjSet); 4413 4414 // Return if any is a derived object. 4415 if (_checkForImplied(elements)) { 4416 return; 4417 } 4418 4419 // Issue a change request, since this requires write access. 4420 ChangeRequest request = new ChangeRequest(container, 4421 "Send to back") { 4422 @Override 4423 protected void _execute() throws IllegalActionException { 4424 MoveAction.move(elements, MoveAction.TO_FIRST, container); 4425 } 4426 }; 4427 4428 container.requestChange(request); 4429 } 4430 } 4431 4432 /////////////////////////////////////////////////////////////////// 4433 //// MoveToFrontAction 4434 4435 /** Action to move the current selection to the back (which corresponds 4436 * to first in the ordered list). 4437 */ 4438 protected class MoveToFrontAction extends AbstractAction { 4439 public MoveToFrontAction() { 4440 // Note that we also have "Bring to Front" in 4441 // vergil/kernel/AttributeController.java 4442 super("Bring to Front"); 4443 putValue("tooltip", "Bring to front of like objects"); 4444 putValue(GUIUtilities.ACCELERATOR_KEY, KeyStroke.getKeyStroke( 4445 KeyEvent.VK_F, 4446 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); 4447 putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_F)); 4448 } 4449 4450 @Override 4451 public void actionPerformed(ActionEvent e) { 4452 final NamedObj container = (NamedObj) _getGraphModel().getRoot(); 4453 4454 // Get the selection objects. 4455 // NOTE: The order in the model must be respected. 4456 HashSet<NamedObj> namedObjSet = _getSelectionSet(); 4457 final List<NamedObj> elements = container 4458 .sortContainedObjects(namedObjSet); 4459 4460 // Return if any is a derived object. 4461 if (_checkForImplied(elements)) { 4462 return; 4463 } 4464 4465 // Issue a change request, since this requires write access. 4466 ChangeRequest request = new ChangeRequest(container, 4467 "Bring to front") { 4468 @Override 4469 protected void _execute() throws IllegalActionException { 4470 MoveAction.move(elements, MoveAction.TO_LAST, container); 4471 } 4472 }; 4473 4474 container.requestChange(request); 4475 } 4476 } 4477 4478 /////////////////////////////////////////////////////////////////// 4479 //// PasteAction 4480 4481 /** Paste the current contents of the clipboard into the current model. */ 4482 protected class PasteAction extends AbstractAction { 4483 /** Create a new action to paste the current contents of the 4484 * clipboard into the current model. 4485 */ 4486 public PasteAction() { 4487 super("Paste"); 4488 putValue("tooltip", "Paste the contents of the clipboard."); 4489 putValue(GUIUtilities.ACCELERATOR_KEY, KeyStroke.getKeyStroke( 4490 KeyEvent.VK_V, 4491 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); 4492 putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P)); 4493 } 4494 4495 /** Paste the current contents of the clipboard into 4496 * the current model. 4497 */ 4498 @Override 4499 public void actionPerformed(ActionEvent e) { 4500 paste(); 4501 } 4502 } 4503 4504 /////////////////////////////////////////////////////////////////// 4505 //// OpenContainerAction 4506 4507 /** An action to open the container of this entity. */ 4508 private class OpenContainerAction extends AbstractAction { 4509 /** Construct an open container action. This action opens 4510 * the container of this class. If this entity is the toplevel 4511 * then the icon is disabled. 4512 * @param description A string that describes the action. Spaces are 4513 * permitted, each word is usually capitalized. 4514 */ 4515 public OpenContainerAction(String description) { 4516 super(description); 4517 // Load the image by using the absolute path to the gif. 4518 // Using a relative location should work, but it does not. 4519 // Use the resource locator of the class. 4520 // For more information, see 4521 // jdk1.3/docs/guide/resources/resources.html 4522 GUIUtilities.addIcons(this, 4523 new String[][] { 4524 { "/ptolemy/vergil/basic/img/up.gif", 4525 GUIUtilities.LARGE_ICON }, 4526 { "/ptolemy/vergil/basic/img/up_o.gif", 4527 GUIUtilities.ROLLOVER_ICON }, 4528 { "/ptolemy/vergil/basic/img/up_ov.gif", 4529 GUIUtilities.ROLLOVER_SELECTED_ICON }, 4530 { "/ptolemy/vergil/basic/img/up_on.gif", 4531 GUIUtilities.SELECTED_ICON } }); 4532 4533 putValue("tooltip", description); 4534 4535 } 4536 4537 /** Open the parent container, if any. 4538 * @param event The action event, ignored by this method. 4539 */ 4540 @Override 4541 public void actionPerformed(ActionEvent event) { 4542 openContainer(); 4543 } 4544 } 4545 4546 /////////////////////////////////////////////////////////////////// 4547 //// OpenLibraryMenuItemFactory 4548 4549 /** 4550 * Create a menu item that will open a library in editable form. 4551 */ 4552 private class OpenLibraryMenuItemFactory implements MenuItemFactory { 4553 /** 4554 * Add an item to the given context menu that will open the 4555 * given object as an editable model. 4556 */ 4557 @Override 4558 public JMenuItem create(final JContextMenu menu, 4559 final NamedObj object) { 4560 Action action = new AbstractAction("Open for Editing") { 4561 @Override 4562 public void actionPerformed(ActionEvent e) { 4563 try { 4564 getConfiguration().openModel(object); 4565 } catch (KernelException ex) { 4566 MessageHandler.error("Open failed.", ex); 4567 } 4568 } 4569 }; 4570 4571 action.putValue("tooltip", "Open library for editing."); 4572 action.putValue(diva.gui.GUIUtilities.MNEMONIC_KEY, 4573 Integer.valueOf(KeyEvent.VK_O)); 4574 return menu.add(action, (String) action.getValue(Action.NAME)); 4575 } 4576 } 4577 4578 /////////////////////////////////////////////////////////////////// 4579 //// PrintAction 4580 4581 /** 4582 * Print the current model. 4583 */ 4584 private class PrintAction extends AbstractAction { 4585 /** Construct a print action. 4586 * @param description A string that describes the action. Spaces are 4587 * permitted, each word is usually capitalized. 4588 */ 4589 public PrintAction(String description) { 4590 super(description); 4591 putValue("tooltip", description); 4592 4593 // Load the image by using the absolute path to the gif. 4594 // Using a relative location should work, but it does not. 4595 // Use the resource locator of the class. 4596 // For more information, see 4597 // jdk1.3/docs/guide/resources/resources.html 4598 GUIUtilities.addIcons(this, 4599 new String[][] { 4600 { "/ptolemy/vergil/basic/img/print.gif", 4601 GUIUtilities.LARGE_ICON }, 4602 { "/ptolemy/vergil/basic/img/print_o.gif", 4603 GUIUtilities.ROLLOVER_ICON }, 4604 { "/ptolemy/vergil/basic/img/print_ov.gif", 4605 GUIUtilities.ROLLOVER_SELECTED_ICON }, 4606 { "/ptolemy/vergil/basic/img/print_on.gif", 4607 GUIUtilities.SELECTED_ICON } }); 4608 } 4609 4610 /** Print the current layout. 4611 * @param event The action event, ignored by this method. 4612 */ 4613 @Override 4614 public void actionPerformed(ActionEvent event) { 4615 _print(); 4616 } 4617 } 4618 4619 /////////////////////////////////////////////////////////////////// 4620 //// RedoAction 4621 4622 /** 4623 * Redo the last undone MoML change on the current current model. 4624 */ 4625 private class RedoAction extends AbstractAction { 4626 /** 4627 * Create a new action to paste the current contents of the clipboard 4628 * into the current model. 4629 */ 4630 public RedoAction() { 4631 super("Redo"); 4632 putValue("tooltip", "Redo the last change undone."); 4633 putValue(diva.gui.GUIUtilities.ACCELERATOR_KEY, 4634 KeyStroke.getKeyStroke(KeyEvent.VK_Y, Toolkit 4635 .getDefaultToolkit().getMenuShortcutKeyMask())); 4636 putValue(diva.gui.GUIUtilities.MNEMONIC_KEY, 4637 Integer.valueOf(KeyEvent.VK_R)); 4638 } 4639 4640 /** 4641 * Redo the last undone MoML change on the current current model. 4642 * 4643 * @param e The event for the action. 4644 */ 4645 @Override 4646 public void actionPerformed(ActionEvent e) { 4647 redo(); 4648 } 4649 } 4650 4651 /////////////////////////////////////////////////////////////////// 4652 //// SaveAction 4653 4654 /** 4655 * Save the current model. 4656 */ 4657 private class SaveAction extends AbstractAction { 4658 /** Construct a save action. 4659 * @param description A string that describes the action. Spaces are 4660 * permitted, each word is usually capitalized. 4661 */ 4662 public SaveAction(String description) { 4663 super(description); 4664 putValue("tooltip", description); 4665 // Load the image by using the absolute path to the gif. 4666 // Using a relative location should work, but it does not. 4667 // Use the resource locator of the class. 4668 // For more information, see 4669 // jdk1.3/docs/guide/resources/resources.html 4670 GUIUtilities.addIcons(this, 4671 new String[][] { 4672 { "/ptolemy/vergil/basic/img/save.gif", 4673 GUIUtilities.LARGE_ICON }, 4674 { "/ptolemy/vergil/basic/img/save_o.gif", 4675 GUIUtilities.ROLLOVER_ICON }, 4676 { "/ptolemy/vergil/basic/img/save_ov.gif", 4677 GUIUtilities.ROLLOVER_SELECTED_ICON }, 4678 { "/ptolemy/vergil/basic/img/save_on.gif", 4679 GUIUtilities.SELECTED_ICON } }); 4680 putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_Z)); 4681 } 4682 4683 /** Save the current layout. 4684 * @param e The action event, ignored by this method. 4685 */ 4686 @Override 4687 public void actionPerformed(ActionEvent e) { 4688 _save(); 4689 } 4690 } 4691 4692 /////////////////////////////////////////////////////////////////// 4693 //// UndoAction 4694 4695 /** 4696 * Undo the last undoable MoML change on the current current model. 4697 */ 4698 private class UndoAction extends AbstractAction { 4699 /** 4700 * Create a new action to paste the current contents of the clipboard 4701 * into the current model. 4702 */ 4703 public UndoAction() { 4704 super("Undo"); 4705 putValue("tooltip", "Undo the last change."); 4706 putValue(diva.gui.GUIUtilities.ACCELERATOR_KEY, 4707 KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit 4708 .getDefaultToolkit().getMenuShortcutKeyMask())); 4709 putValue(diva.gui.GUIUtilities.MNEMONIC_KEY, 4710 Integer.valueOf(KeyEvent.VK_U)); 4711 } 4712 4713 /** 4714 * Undo the last undoable MoML change on the current current model. 4715 * 4716 * @param e The event for the action. 4717 */ 4718 @Override 4719 public void actionPerformed(ActionEvent e) { 4720 undo(); 4721 } 4722 } 4723 4724 /////////////////////////////////////////////////////////////////// 4725 //// ZoomInAction 4726 /** An action to zoom in. */ 4727 private class ZoomInAction extends AbstractAction { 4728 /** Construct a zoom in action. 4729 * @param description A string that describes the action. Spaces are 4730 * permitted, each word is usually capitalized. 4731 */ 4732 public ZoomInAction(String description) { 4733 super(description); 4734 4735 // Load the image by using the absolute path to the gif. 4736 // Using a relative location should work, but it does not. 4737 // Use the resource locator of the class. 4738 // For more information, see 4739 // jdk1.3/docs/guide/resources/resources.html 4740 GUIUtilities.addIcons(this, 4741 new String[][] { 4742 { "/ptolemy/vergil/basic/img/zoomin.gif", 4743 GUIUtilities.LARGE_ICON }, 4744 { "/ptolemy/vergil/basic/img/zoomin_o.gif", 4745 GUIUtilities.ROLLOVER_ICON }, 4746 { "/ptolemy/vergil/basic/img/zoomin_ov.gif", 4747 GUIUtilities.ROLLOVER_SELECTED_ICON }, 4748 { "/ptolemy/vergil/basic/img/zoomin_on.gif", 4749 GUIUtilities.SELECTED_ICON } }); 4750 4751 putValue("tooltip", description + " (Ctrl+Shift+=)"); 4752 4753 // NOTE: The following assumes that the + key is the same 4754 // as the = key. Unfortunately, the VK_PLUS key event doesn't 4755 // work, so we have to do it this way. 4756 putValue(GUIUtilities.ACCELERATOR_KEY, 4757 KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, 4758 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() 4759 | Event.SHIFT_MASK)); 4760 putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_Z)); 4761 } 4762 4763 /** Zoom in by a factor of 1.25. 4764 * @param e The action event, ignored by this method. 4765 */ 4766 @Override 4767 public void actionPerformed(ActionEvent e) { 4768 zoom(1.25); 4769 } 4770 } 4771 4772 /////////////////////////////////////////////////////////////////// 4773 //// ZoomResetAction 4774 /** An action to reset zoom. */ 4775 private class ZoomResetAction extends AbstractAction { 4776 /** Construct a zoom reset action. 4777 * @param description A string that describes the action. Spaces are 4778 * permitted, each word is usually capitalized. 4779 */ 4780 public ZoomResetAction(String description) { 4781 super(description); 4782 4783 // Load the image by using the absolute path to the gif. 4784 // Using a relative location should work, but it does not. 4785 // Use the resource locator of the class. 4786 // For more information, see 4787 // jdk1.3/docs/guide/resources/resources.html 4788 GUIUtilities.addIcons(this, 4789 new String[][] { 4790 { "/ptolemy/vergil/basic/img/zoomreset.gif", 4791 GUIUtilities.LARGE_ICON }, 4792 { "/ptolemy/vergil/basic/img/zoomreset_o.gif", 4793 GUIUtilities.ROLLOVER_ICON }, 4794 { "/ptolemy/vergil/basic/img/zoomreset_ov.gif", 4795 GUIUtilities.ROLLOVER_SELECTED_ICON }, 4796 { "/ptolemy/vergil/basic/img/zoomreset_on.gif", 4797 GUIUtilities.SELECTED_ICON } }); 4798 4799 // Control-m is usually carriage return. In this case, we use 4800 // it to mean "return the zoom to the original state". 4801 putValue("tooltip", description + " (Ctrl+M)"); 4802 putValue(GUIUtilities.ACCELERATOR_KEY, KeyStroke.getKeyStroke( 4803 KeyEvent.VK_M, 4804 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); 4805 putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_M)); 4806 } 4807 4808 /** Reset the zoom. 4809 * @param e The action event, ignored by this method. 4810 */ 4811 @Override 4812 public void actionPerformed(ActionEvent e) { 4813 zoomReset(); 4814 } 4815 } 4816 4817 /////////////////////////////////////////////////////////////////// 4818 //// ZoomFitAction 4819 /** An action to zoom fit.*/ 4820 private class ZoomFitAction extends AbstractAction { 4821 /** Construct a zoom fit action. 4822 * @param description A string that describes the action. Spaces are 4823 * permitted, each word is usually capitalized. 4824 */ 4825 public ZoomFitAction(String description) { 4826 super(description); 4827 4828 // Load the image by using the absolute path to the gif. 4829 // Using a relative location should work, but it does not. 4830 // Use the resource locator of the class. 4831 // For more information, see 4832 // jdk1.3/docs/guide/resources/resources.html 4833 GUIUtilities.addIcons(this, 4834 new String[][] { 4835 { "/ptolemy/vergil/basic/img/zoomfit.gif", 4836 GUIUtilities.LARGE_ICON }, 4837 { "/ptolemy/vergil/basic/img/zoomfit_o.gif", 4838 GUIUtilities.ROLLOVER_ICON }, 4839 { "/ptolemy/vergil/basic/img/zoomfit_ov.gif", 4840 GUIUtilities.ROLLOVER_SELECTED_ICON }, 4841 { "/ptolemy/vergil/basic/img/zoomfit_on.gif", 4842 GUIUtilities.SELECTED_ICON } }); 4843 4844 putValue("tooltip", description + " (Ctrl+Shift+-)"); 4845 putValue(GUIUtilities.ACCELERATOR_KEY, 4846 KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 4847 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() 4848 | Event.SHIFT_MASK)); 4849 putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_F)); 4850 } 4851 4852 /** Zoom so that the entire graph is visible. 4853 * @param e The action event, ignored by this method. 4854 */ 4855 @Override 4856 public void actionPerformed(ActionEvent e) { 4857 zoomFit(); 4858 } 4859 } 4860 4861 /////////////////////////////////////////////////////////////////// 4862 //// ZoomOutAction 4863 /** An action to zoom out. */ 4864 private class ZoomOutAction extends AbstractAction { 4865 /** Construct a zoom fit action. 4866 * @param description A string that describes the action. Spaces are 4867 * permitted, each word is usually capitalized. 4868 */ 4869 public ZoomOutAction(String description) { 4870 super(description); 4871 4872 // Load the image by using the absolute path to the gif. 4873 // Using a relative location should work, but it does not. 4874 // Use the resource locator of the class. 4875 // For more information, see 4876 // jdk1.3/docs/guide/resources/resources.html 4877 GUIUtilities.addIcons(this, 4878 new String[][] { 4879 { "/ptolemy/vergil/basic/img/zoomout.gif", 4880 GUIUtilities.LARGE_ICON }, 4881 { "/ptolemy/vergil/basic/img/zoomout_o.gif", 4882 GUIUtilities.ROLLOVER_ICON }, 4883 { "/ptolemy/vergil/basic/img/zoomout_ov.gif", 4884 GUIUtilities.ROLLOVER_SELECTED_ICON }, 4885 { "/ptolemy/vergil/basic/img/zoomout_on.gif", 4886 GUIUtilities.SELECTED_ICON } }); 4887 4888 putValue("tooltip", description + " (Ctrl+-)"); 4889 putValue(GUIUtilities.ACCELERATOR_KEY, KeyStroke.getKeyStroke( 4890 KeyEvent.VK_MINUS, 4891 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); 4892 putValue(GUIUtilities.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_U)); 4893 } 4894 4895 /** Zoom out by a factor of 1/1.25. 4896 * @param e The action event, ignored by this method. 4897 */ 4898 @Override 4899 public void actionPerformed(ActionEvent e) { 4900 zoom(1.0 / 1.25); 4901 } 4902 } 4903 4904 /////////////////////////////////////////////////////////////////// 4905 //// LayoutConfigDialogAction 4906 4907 /** Action to display a dialog for setting layout options. */ 4908 private class LayoutConfigDialogAction extends AbstractAction { 4909 /** Create a new action to show the layout configuration dialog. */ 4910 public LayoutConfigDialogAction() { 4911 super("Configure Layout..."); 4912 putValue("tooltip", 4913 "Set parameters for controlling the layout algorithm"); 4914 } 4915 4916 /** Show the layout configuration dialog. */ 4917 @Override 4918 public void actionPerformed(ActionEvent e) { 4919 NamedObj model = getModel(); 4920 4921 Attribute attribute = model.getAttribute("_layoutConfiguration"); 4922 if (attribute == null) { 4923 4924 String layoutConfiguration = "ptolemy.vergil.basic.layout.ActorLayoutConfiguration"; 4925 if (_getGraphModel() instanceof FSMGraphModel) { 4926 layoutConfiguration = "ptolemy.vergil.basic.layout.ModalLayoutConfiguration"; 4927 } 4928 4929 String momlChange = "<property name=\"_layoutConfiguration\" class=\"" 4930 + layoutConfiguration + "\"/>"; 4931 model.requestChange( 4932 new MoMLChangeRequest(this, model, momlChange, false)); 4933 attribute = model.getAttribute("_layoutConfiguration"); 4934 if (attribute == null) { 4935 MessageHandler.error( 4936 "Could not create the layout configuration attribute."); 4937 return; 4938 } 4939 } 4940 new EditParametersDialog(BasicGraphFrame.this, attribute, 4941 "Configure Layout Parameters"); 4942 } 4943 } 4944 4945}