001/* 002 * Copyright (c) 2009-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2015-08-24 22:44:14 +0000 (Mon, 24 Aug 2015) $' 007 * '$Revision: 33630 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.gui; 031 032import java.awt.Adjustable; 033import java.awt.BorderLayout; 034import java.awt.Color; 035import java.awt.Dimension; 036import java.awt.KeyboardFocusManager; 037import java.awt.event.ActionEvent; 038import java.awt.event.ActionListener; 039import java.awt.event.AdjustmentEvent; 040import java.awt.event.AdjustmentListener; 041import java.awt.event.KeyEvent; 042import java.awt.geom.AffineTransform; 043import java.awt.geom.Point2D; 044import java.awt.geom.Rectangle2D; 045import java.io.File; 046import java.io.IOException; 047import java.net.URL; 048import java.util.Collections; 049import java.util.Iterator; 050import java.util.SortedSet; 051import java.util.TreeSet; 052import java.util.Vector; 053import java.util.regex.Matcher; 054 055import javax.swing.BoxLayout; 056import javax.swing.JButton; 057import javax.swing.JComponent; 058import javax.swing.JMenu; 059import javax.swing.JMenuBar; 060import javax.swing.JMenuItem; 061import javax.swing.JOptionPane; 062import javax.swing.JPanel; 063import javax.swing.JScrollBar; 064import javax.swing.JToolBar; 065import javax.swing.KeyStroke; 066 067import org.apache.commons.logging.Log; 068import org.apache.commons.logging.LogFactory; 069import org.kepler.gui.kar.ExportArchiveAction; 070import org.kepler.kar.KARFile; 071import org.kepler.kar.KARManager; 072import org.kepler.objectmanager.ObjectManager; 073import org.kepler.objectmanager.cache.LocalRepositoryManager; 074import org.kepler.objectmanager.library.LibraryManager; 075import org.kepler.util.FileUtil; 076import org.kepler.util.RenameUtil; 077import org.kepler.util.ShutdownNotifier; 078 079import diva.graph.GraphController; 080import diva.gui.toolbox.JCanvasPanner; 081import ptolemy.actor.gui.Configuration; 082import ptolemy.actor.gui.Effigy; 083import ptolemy.actor.gui.ModelDirectory; 084import ptolemy.actor.gui.PtolemyEffigy; 085import ptolemy.actor.gui.PtolemyPreferences; 086import ptolemy.actor.gui.SizeAttribute; 087import ptolemy.actor.gui.Tableau; 088import ptolemy.actor.gui.TableauFrame; 089import ptolemy.gui.MemoryCleaner; 090import ptolemy.kernel.ComponentEntity; 091import ptolemy.kernel.CompositeEntity; 092import ptolemy.kernel.util.IllegalActionException; 093import ptolemy.kernel.util.NameDuplicationException; 094import ptolemy.kernel.util.NamedObj; 095import ptolemy.moml.LibraryAttribute; 096import ptolemy.util.CancelException; 097import ptolemy.util.MessageHandler; 098import ptolemy.vergil.actor.ActorGraphFrame; 099import ptolemy.vergil.basic.BasicGraphFrame; 100import ptolemy.vergil.basic.BasicGraphFrameExtension; 101 102/** Kepler-specific overrides for the graph frame. 103 * 104 */ 105public class KeplerGraphFrame extends ActorGraphFrame { 106 107 /** 108 * Constructor. 109 * 110 * @param entity 111 * @param tableau 112 */ 113 public KeplerGraphFrame(CompositeEntity entity, Tableau tableau) { 114 this(entity, tableau, null); 115 } 116 117 /** 118 * Constructor 119 * 120 * @param entity 121 * @param tableau 122 * @param defaultLibrary 123 */ 124 public KeplerGraphFrame(CompositeEntity entity, Tableau tableau, 125 LibraryAttribute defaultLibrary) { 126 super(entity, tableau, defaultLibrary); 127 _initKeplerGraphFrame(); 128 129 // get the model file from the effigy and update the recent menu history 130 // and set the last directory for the File->Open and Save dialogs. 131 // this is performed here for opening XML or KAR files from the command 132 // line. when a model is opened via File->Open, the recent menu and 133 // last directory are updated elsewhere. 134 Effigy effigy = getEffigy(); 135 File modelFile = effigy.getWritableFile(); 136 if(modelFile != null) { 137 try { 138 updateHistory(modelFile.getAbsolutePath()); 139 } catch (IOException e) { 140 MessageHandler.error("Unable to update history menu.", e); 141 } 142 143 setLastDirectory(modelFile.getParentFile()); 144 } 145 } 146 147 /** 148 * Override the dispose method to unattach any listeners that may keep this 149 * model from getting garbage collected. Also remove KARManager's mapping of 150 * this JFrame to KARfile if necessary. 151 */ 152 @Override 153 public void dispose() { 154 155 if (_topPack instanceof KeplerMenuHandler) { 156 KeplerMenuHandler topPack = (KeplerMenuHandler) _topPack; 157 MenuMapper mm = topPack.menuMapper; 158 mm.clear(); 159 /* 160 * mm.printDebugInfo(); Map<String,Action> m = 161 * mm.getPTIIMenuActionsMap(); m.clear(); KeplerMenuHandler kmh = 162 * (KeplerMenuHandler) _topPack; kmh.clear(); _topPack = null; 163 */ 164 } 165 166 JMenuBar keplerMenuBar = getJMenuBar(); 167 /* int removed = */MemoryCleaner.removeActionListeners(keplerMenuBar); 168 // System.out.println("KeplerGraphFrame menubar action listeners removed: " 169 // + removed); 170 171 CanvasDropTargetListener listener = CanvasDropTargetListener 172 .getInstance(); 173 if (listener != null && _dropTarget != null) { 174 _dropTarget.deRegisterAdditionalListener(listener); 175 } 176 177 if (_horizontalScrollBarListener != null 178 && _horizontalScrollBar != null) { 179 _horizontalScrollBar 180 .removeAdjustmentListener(_horizontalScrollBarListener); 181 _horizontalScrollBarListener = null; 182 } 183 if (_verticalScrollBarListener != null 184 && _verticalScrollBarListener != null) { 185 _verticalScrollBar 186 .removeAdjustmentListener(_verticalScrollBarListener); 187 _verticalScrollBarListener = null; 188 } 189 190 // remove JFrame => KARFile mapping from KARManager 191 KARManager karManager = KARManager.getInstance(); 192 karManager.remove(this); 193 194 TabManager tabManager = TabManager.getInstance(); 195 tabManager.removeAllFrameTabs(this); 196 197 // this isn't safe. see: 198 // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5095#c14 199 // it is safe now after changes in r26444. 200 ObjectManager.getInstance().removeNamedObjs(this.getModel()); 201 ViewManager.getInstance().removeOpenFrame(this); 202 LibraryManager.getInstance().removeAllFrameTabs(this); 203 204 // now call dispose on updaters in order 205 synchronized (_updaterSet) { 206 Iterator<KeplerGraphFrameUpdater> itr = _updaterSet.iterator(); 207 while (itr.hasNext()) { 208 KeplerGraphFrameUpdater updater = itr.next(); 209 updater.dispose(this); 210 } 211 } 212 213 ModelToFrameManager m2fm = ModelToFrameManager.getInstance(); 214 m2fm.remove(this); 215 216 // see if this was the last window 217 if (m2fm.numberOfOpenFrames() == 0) { 218 ShutdownNotifier.shutdown(); 219 } 220 221 KeyboardFocusManager focusManager = KeyboardFocusManager 222 .getCurrentKeyboardFocusManager(); 223 focusManager.clearGlobalFocusOwner(); 224 focusManager.downFocusCycle(); 225 226 super.dispose(); 227 } 228 229 /** 230 * Go to full screen. 231 */ 232 @Override 233 public void fullScreen() { 234 235 // FIXME: do nothing since returning from full screen currently crashes 236 // kepler. this is due to cancelFullScreen() dereferencing _splitPane, 237 // which is never initialized. 238 } 239 240 // commented out since zoom() is commented out, and hides a super class _zoomFlag 241 //protected boolean _zoomFlag = false; 242 243 /** Get the graph controller. */ 244 public GraphController getGraphController() { 245 return _controller; 246 } 247 248 public JToolBar getToolBar() { 249 return this._toolbar; 250 } 251 252 /** Set the model for this frame. */ 253 @Override 254 public void setModel(NamedObj model) { 255 super.setModel(model); 256 257 // FIXME: ModelToFrameManager is probably unnecessary, this 258 // information is in the Configuration. 259 ModelToFrameManager m2fm = ModelToFrameManager.getInstance(); 260 m2fm.remove(this); 261 m2fm.add(getModel(), this); 262 } 263 264 /** Update the History menu. */ 265 public void updateHistory(String absolutePath) throws IOException { 266 _updateHistory(absolutePath, false); 267 } 268 269 /** 270 * Create the menus that are used by this frame. It is essential that 271 * _createGraphPane() be called before this. 272 */ 273 @Override 274 protected void _addMenus() { 275 super._addMenus(); 276 277 // remove Open File... O accelerator. 278 // Open... (for KARs) uses this accelerator now. 279 for (int i = 0; i < _fileMenuItems.length; i++) { 280 JMenuItem menuItem = _fileMenuItems[i]; 281 if (menuItem != null) { 282 String menuItemText = menuItem.getText(); 283 if (menuItemText != null && menuItemText.equals("Open File")) { 284 // int removed = 285 // MemoryCleaner.removeActionListeners(menuItem); 286 // System.out.println("KeplerGraphFrame _fileMenuItems["+i+"] action listeners removed: " 287 // + removed); 288 menuItem.setAccelerator(null); 289 } 290 } 291 } 292 293 // see if the effigy for the top level workflow is called "Unnamed" 294 // if so, renamed it to "Unnamed1" 295 296 NamedObj namedObj = getModel(); 297 if (namedObj.getContainer() == null && namedObj.getName().length() == 0) { 298 Effigy effigy = getEffigy(); 299 if (effigy != null) { 300 String name = effigy.identifier.getExpression(); 301 if (name.equals("Unnamed")) { 302 String newName = name + _nextUnnamed; 303 _nextUnnamed++; 304 try { 305 // set the identifier, which is what shows up at the top 306 // of the window 307 effigy.identifier.setExpression(newName); 308 } catch (Exception e) { 309 report("Error setting effigy name.", e); 310 } 311 312 try { 313 namedObj.setName(newName); 314 } catch (Exception e) { 315 report("Error setting workflow name to " + newName 316 + ".", e); 317 } 318 319 } 320 } 321 } 322 323 // let any KeplerGraphFrameUpdaters perform updates. 324 Components components = new Components(); 325 326 // now call updateFrameComponents on updaters in order 327 synchronized (_updaterSet) { 328 Iterator<KeplerGraphFrameUpdater> itr = _updaterSet.iterator(); 329 while (itr.hasNext()) { 330 KeplerGraphFrameUpdater updater = itr.next(); 331 updater.updateFrameComponents(components); 332 } 333 } 334 335 } 336 337 /** 338 * Get the name of this object. If the parent class returns "Unnamed", use 339 * the effigy's identifier as the name. 340 * 341 * @return The name. 342 */ 343 @Override 344 protected String _getName() { 345 String retval = super._getName(); 346 if (retval.equals("Unnamed")) { 347 Effigy effigy = getEffigy(); 348 if (effigy != null) { 349 retval = effigy.identifier.getExpression(); 350 } 351 } 352 return retval; 353 } 354 355 /** 356 * Get the component whose size is to be recorded in the model when it is 357 * saved into an output file. In this class, the return of this function is 358 * the same as that of {@link #_getRightComponent()}. A subclass may 359 * override this function to return a different component, such as a tab of 360 * a tabbed pane, whose size is to be recorded instead. 361 * 362 * @return The component whose size is to be recorded. 363 */ 364 /* TODO this is not used, but should be moved to and called from BasicGraphFrame. 365 protected JComponent _getSizeComponent() { 366 return _getRightComponent(); 367 } 368 */ 369 370 /** 371 * Override BasicGraphFrame._initBasicGraphFrame() 372 */ 373 @Override 374 protected void _initBasicGraphFrame() { 375 376 /** 377 * @todo - FIXME - Need to move this further up the hierarchy, so other 378 * types of frames use it too. Don't put it in a launcher class 379 * like KeplerApplication, because it then gets overridden later, 380 * elsewhere in PTII 381 */ 382 StaticGUIResources.setLookAndFeel(); 383 384 _initBasicGraphFrameInitialization(); 385 386 _dropTarget = BasicGraphFrameExtension.getDropTarget(_jgraph); 387 388 // add a CanvasDropTargetListener so that other classes can get 389 CanvasDropTargetListener listener = CanvasDropTargetListener 390 .getInstance(); 391 _dropTarget.registerAdditionalListener(listener); 392 393 ActionListener deletionListener = new DeletionListener(); 394 395 _rightComponent.registerKeyboardAction(deletionListener, "Delete", 396 KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), 397 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 398 _rightComponent.registerKeyboardAction(deletionListener, "BackSpace", 399 KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), 400 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 401 402 _initBasicGraphFrameRightComponent(); 403 404 _jgraph.setRequestFocusEnabled(true); 405 406 // Background color is parameterizable by preferences. 407 Configuration configuration = getConfiguration(); 408 409 if (configuration != null) { 410 try { 411 // Set the PtolemyPreference to the desired background. 412 // See 413 // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=2321#c14 414 PtolemyPreferences preferences = PtolemyPreferences 415 .getPtolemyPreferencesWithinConfiguration(configuration); 416 if (_isDebugging) { 417 _log.debug("bg: " + BACKGROUND_COLOR); 418 } 419 if (preferences != null) { 420 float[] components = new float[4]; 421 // Make sure we get only 4 elements in case the color space 422 // is bigger than 4 423 components = BACKGROUND_COLOR.getComponents(components); 424 preferences.backgroundColor.setExpression("{" 425 + components[0] + "," + components[1] + "," 426 + components[2] + "," + components[3] + "}"); 427 _rightComponent.setBackground(preferences.backgroundColor 428 .asColor()); 429 if (_isDebugging) { 430 _log.debug("desired background: " + BACKGROUND_COLOR 431 + " actual background: " 432 + preferences.backgroundColor.asColor()); 433 } 434 } 435 } catch (IllegalActionException e1) { 436 // Ignore the exception and use the default color. 437 } 438 } 439 440 _initBasicGraphFrameRightComponentMouseListeners(); 441 442 try { 443 // The SizeAttribute property is used to specify the size 444 // of the JGraph component. Unfortunately, with Swing's 445 // mysterious and undocumented handling of component sizes, 446 // there appears to be no way to control the size of the 447 // JGraph from the size of the Frame, which is specified 448 // by the WindowPropertiesAttribute. 449 SizeAttribute size = (SizeAttribute) getModel().getAttribute( 450 "_vergilSize", SizeAttribute.class); 451 if (size != null) { 452 size.setSize(_jgraph); 453 } else { 454 // Set the default size. 455 // Note that the location is of the frame, while the size 456 // is of the scrollpane. 457 _jgraph.setPreferredSize(new Dimension(600, 400)); 458 } 459 460 _initBasicGraphFrameSetZoomAndPan(); 461 } catch (Exception ex) { 462 // Ignore problems here. Errors simply result in a default 463 // size and location. 464 } 465 466 // Create the panner. 467 _graphPanner = new JCanvasPanner(_jgraph); 468 469 _horizontalScrollBar = new JScrollBar(Adjustable.HORIZONTAL); 470 _verticalScrollBar = new JScrollBar(Adjustable.VERTICAL); 471 472 // see if we want scrollbars on the canvas or not 473 // the answer defaults to 'no' 474 CanvasNavigationModifierFactory CNMfactory = (CanvasNavigationModifierFactory) getConfiguration() 475 .getAttribute("canvasNavigationModifier"); 476 if (CNMfactory != null) { // get the scrollbar flag from the factory if 477 // it exists in the config 478 ScrollBarModifier modifier = CNMfactory.createScrollBarModifier(); 479 _scrollBarFlag = modifier.getScrollBarModifier(); 480 } 481 482 _canvasPanel = new JPanel(); 483 484 _canvasPanel.setBorder(null); 485 _canvasPanel.setLayout(new BorderLayout()); 486 487 if (_scrollBarFlag) { 488 _canvasPanel.add(_horizontalScrollBar, BorderLayout.SOUTH); 489 _canvasPanel.add(_verticalScrollBar, BorderLayout.EAST); 490 _horizontalScrollBar.setModel(_jgraph.getGraphPane().getCanvas() 491 .getHorizontalRangeModel()); 492 _verticalScrollBar.setModel(_jgraph.getGraphPane().getCanvas() 493 .getVerticalRangeModel()); 494 _horizontalScrollBarListener = new ScrollBarListener( 495 _horizontalScrollBar); 496 _verticalScrollBarListener = new ScrollBarListener( 497 _verticalScrollBar); 498 _horizontalScrollBar 499 .addAdjustmentListener(_horizontalScrollBarListener); 500 _verticalScrollBar 501 .addAdjustmentListener(_verticalScrollBarListener); 502 } 503 504 // NOTE: add _rightComponent instead of _jgraph since _rightComponent 505 // may be sub-divided into tabbed panes. 506 // see http://bugzilla.ecoinformatics.org/show_bug.cgi?id=3708 507 _canvasPanel.add(_rightComponent, BorderLayout.CENTER); 508 509 TabManager tabman = TabManager.getInstance(); 510 tabman.initializeTabs(this); 511 512 ViewManager viewman = ViewManager.getInstance(); 513 viewman.initializeViews(this); 514 try { 515 viewman.addCanvasToLocation(_canvasPanel, this); 516 } catch (Exception e) { 517 throw new RuntimeException("Could not add canvas panel: " 518 + e.getMessage()); 519 } 520 521 // _jgraph.setMinimumSize(new Dimension(0, 0)); 522 523 getContentPane().add(viewman.getViewArea(this), BorderLayout.CENTER); 524 525 // The toolbar panel is the container that contains the main toolbar and 526 // any additional toolbars 527 JPanel toolbarPanel = new JPanel(); 528 toolbarPanel.setLayout(new BoxLayout(toolbarPanel, BoxLayout.Y_AXIS)); // They 529 // stack 530 _toolbar = new JToolBar(); // The main Kepler toolbar 531 toolbarPanel.add(_toolbar); 532 getContentPane().add(toolbarPanel, BorderLayout.NORTH); // Place the 533 // toolbar panel 534 _initBasicGraphFrameToolBarZoomButtons(); 535 536 _initBasicGraphFrameActions(); 537 538 // Add a weak reference to this to keep track of all 539 // the graph frames that have been created. 540 _openGraphFrames.add(this); 541 542 System.gc(); 543 } 544 545 /** 546 * KeplerGraphFrame Initializer method 547 */ 548 protected void _initKeplerGraphFrame() { 549 if (_isDebugging) { 550 _log.debug("_initKeplerGraphFrame()"); 551 } 552 ModelToFrameManager m2fm = ModelToFrameManager.getInstance(); 553 m2fm.add(getModel(), this); 554 } 555 556 /** 557 * Open a dialog to prompt the user to save a KAR. Return false if the user 558 * clicks "cancel", and otherwise return true. 559 * 560 * Overrides Top._queryForSave() 561 * 562 * @return _SAVED if the file is saved, _DISCARDED if the modifications are 563 * discarded, _CANCELED if the operation is canceled by the user, 564 * and _FAILED if the user selects save and the save fails. 565 */ 566 @Override 567 protected int _queryForSave() { 568 Object[] options = { "Save", "Discard changes", "Cancel" }; 569 570 // making more generic since other items to go in the KAR 571 // may be the reason for querying to save 572 // String query = "Save changes to " + StringUtilities.split(_getName()) 573 // + "?"; 574 String query = "Save changes to KAR?"; 575 576 // Show the MODAL dialog 577 int selected = JOptionPane.showOptionDialog(this, query, 578 "Save Changes?", JOptionPane.YES_NO_CANCEL_OPTION, 579 JOptionPane.QUESTION_MESSAGE, null, options, options[0]); 580 581 if (selected == JOptionPane.YES_OPTION) { 582 583 JButton saveButton = new JButton("Save Kar"); 584 ExportArchiveAction eaa = new ExportArchiveAction(this); 585 eaa.setRefreshFrameAfterSave(false); 586 // if the file already exists, call ExportArchiveAction.setSave() 587 // so that the save as file dialog does is not used. 588 KARFile karFile = KARManager.getInstance().get(this); 589 if (karFile != null) { 590 File file = karFile.getFileLocation(); 591 if (file.canWrite()) { 592 eaa.setSaveFile(file); 593 } 594 } 595 saveButton.addActionListener(eaa); 596 saveButton.doClick(); 597 598 if (eaa.saveSucceeded()) { 599 setModified(false); 600 return _SAVED; 601 } else { 602 return _FAILED; 603 } 604 605 } 606 607 if (selected == JOptionPane.NO_OPTION) { 608 return _DISCARDED; 609 } 610 611 return _CANCELED; 612 } 613 614 /** 615 * Query the user for a filename, save the model to that file, and open a 616 * new window to view the model. This overrides the base class to close the 617 * old effigy if its name matches "UnnamedN". It also assigns a new LSID to 618 * the new workflow unless it is unnamed. 619 * 620 * @param extension 621 * If non-null, then the extension that is appended to the file 622 * name if there is no extension. 623 * @return True if the save succeeds. 624 */ 625 @Override 626 protected boolean _saveAs(String extension) { 627 boolean isUnnamed = false; 628 629 // If the tableau was unnamed before, then we need 630 // to close this window after doing the save. 631 Effigy effigy = getEffigy(); 632 if (effigy != null) { 633 String id = effigy.identifier.getExpression(); 634 635 // see if effigy id matches unnamed regex. 636 Matcher matcher = RenameUtil.unnamedIdPattern.matcher(id); 637 if (matcher.matches()) { 638 isUnnamed = true; 639 } 640 } 641 642 NamedObj model = getModel(); 643 644 // When we do a File -> Save As, the new and original 645 // workflows need to have different LSIDs. This is taken 646 // care of by RenameUtil.renameComponentEntity below. 647 648 // NOTE: get the model directory before calling _saveAsHelper 649 // since _saveAsHelper may dispose the effigy and thereby close 650 // all associated tableaux 651 ModelDirectory directory = (ModelDirectory) getConfiguration() 652 .getEntity(Configuration._DIRECTORY_NAME); 653 654 // print a warning the first time this is called 655 if (!_printedWarningFirstTime) { 656 try { 657 MessageHandler 658 .warning("Exporting the workflow to XML does not save any associated workflow " 659 + "artifacts such as the report layout or module dependencies."); 660 } catch (CancelException e) { 661 // user clicked cancel button, so do not save. 662 return false; 663 } 664 _printedWarningFirstTime = true; 665 } 666 667 // perform the save 668 URL newModelURL = super._saveAsHelper(extension); 669 670 // see if we successfully saved 671 if (newModelURL != null) { 672 673 // get the new model 674 File newModelFile = new File(newModelURL.getFile()); 675 String newName = FileUtil.getFileNameWithoutExtension(newModelFile 676 .getName()); 677 PtolemyEffigy newEffigy = (PtolemyEffigy) directory 678 .getEffigy(newModelURL.toString()); 679 NamedObj newModel = newEffigy.getModel(); 680 681 // set newModel name back to the original name so we can 682 // send it through renameComponentEntity 683 try { 684 newModel.setName(model.getName()); 685 } catch (IllegalActionException e1) { 686 // TODO Auto-generated catch block 687 e1.printStackTrace(); 688 } catch (NameDuplicationException e1) { 689 // TODO Auto-generated catch block 690 e1.printStackTrace(); 691 } 692 693 if (isUnnamed) { 694 try { 695 // This will have the effect of closing all the 696 // tableaux associated with the unnamed model. 697 effigy.setContainer(null); 698 699 // remove the workflow from the object manager since 700 // its window went away. 701 ObjectManager objectManager = ObjectManager.getInstance(); 702 objectManager.removeNamedObj(model); 703 } catch (Exception e) { 704 report("Error closing effigy.", e); 705 return false; 706 } 707 } 708 709 try { 710 RenameUtil.renameComponentEntity((ComponentEntity) newModel, 711 newName); 712 } catch (Exception e) { 713 // TODO Auto-generated catch block 714 e.printStackTrace(); 715 } 716 717 // After the MoML has been saved to disk we add it to the library 718 // and refresh the JTrees if it is saved in a local repository update the library 719 LocalRepositoryManager lrm = LocalRepositoryManager.getInstance(); 720 if (lrm.isInLocalRepository(newModelFile)) { 721 722 //_savekar.saveToCache(); 723 724 LibraryManager lm = LibraryManager.getInstance(); 725 try { 726 lm.addXML(newModelFile); 727 } catch (Exception e2) { 728 MessageHandler.error("Error adding workflow to library.", 729 e2); 730 } 731 try { 732 lm.refreshJTrees(); 733 } catch (IllegalActionException e2) { 734 MessageHandler.error("Error updating tree.", e2); 735 } 736 } 737 738 } 739 740 return true; 741 } 742 743 /** 744 * Add the name of the last file open or set the name to the first position 745 * if already in the list 746 * 747 * @param file 748 * name of the file to add 749 * @param delete 750 * If true, remove from the history list, otherwise the file is 751 * added to the beginning. 752 */ 753 @Override 754 protected void _updateHistory(String file, boolean delete) 755 throws IOException { 756 super._updateHistory(file, delete); 757 // reload the menus in all open windows. 758 MenuMapper.reloadAllMenubars(); 759 } 760 761 /** A class to give KeplerGraphFrameUpdaters access to GUI components. */ 762 public class Components { 763 /** Construct a new Component. Only KeplerGraphFrame may do this. */ 764 private Components() { 765 } 766 767 /** Get the KeplerGraphFrame. */ 768 public KeplerGraphFrame getFrame() { 769 return KeplerGraphFrame.this; 770 } 771 772 /** Get the menu. */ 773 public JMenu getMenu() { 774 return _graphMenu; 775 } 776 777 /** Get the toolbar. */ 778 public JToolBar getToolBar() { 779 return _toolbar; 780 } 781 } 782 783 /** 784 * Listener for scrollbar events. 785 */ 786 public class ScrollBarListener implements AdjustmentListener { 787 /** 788 * constructor 789 * 790 * @param sb 791 * JScrollBar 792 */ 793 public ScrollBarListener(JScrollBar sb) { 794 _orientation = sb.getOrientation(); 795 } 796 797 /** 798 * translate the model when the scrollbars move 799 * 800 * @param e 801 * AdjustmentEvent 802 */ 803 @Override 804 public void adjustmentValueChanged(AdjustmentEvent e) { 805 806 // do not scroll the canvas if the canvas is being zoomed 807 // or we are already scrolling it. 808 if(!_zoomFlag && !_amUpdating) { 809 810 int val = e.getValue(); 811 812 Rectangle2D visibleRect = getVisibleCanvasRectangle(); 813 AffineTransform newTransform = getJGraph().getCanvasPane().getTransformContext() 814 .getTransform(); 815 816 if(_orientation == Adjustable.HORIZONTAL) { 817 Point2D newLeft = new Point2D.Double(val, 0); 818 newTransform.translate(visibleRect.getX() - newLeft.getX(), 0); 819 } else { 820 Point2D newTop = new Point2D.Double(0, val); 821 newTransform.translate(0, visibleRect.getY() - newTop.getY()); 822 } 823 824 // set the _amUpdating flag before scrolling the canvas to prevent 825 // recursive calls to this method. 826 _amUpdating = true; 827 try { 828 getJGraph().getCanvasPane() 829 .setTransform(newTransform); 830 } finally { 831 _amUpdating = false; 832 } 833 834 if (_graphPanner != null) { 835 _graphPanner.repaint(); 836 } 837 } 838 } 839 840 private int _orientation; 841 private boolean _amUpdating = false; 842 } 843 844 /** An ActionListener for handling deletion events. */ 845 private class DeletionListener implements ActionListener { 846 /** 847 * Delete any nodes or edges from the graph that are currently selected. 848 * In addition, delete any edges that are connected to any deleted 849 * nodes. 850 */ 851 @Override 852 public void actionPerformed(ActionEvent e) { 853 delete(); 854 } 855 } 856 857 /** Override the BasicGraphFrame Background color **/ 858 public static final Color BACKGROUND_COLOR = new Color(255, 255, 255); 859 860 private static final Log _log = LogFactory.getLog(KeplerGraphFrame.class 861 .getName()); 862 863 private static final boolean _isDebugging = _log.isDebugEnabled(); 864 865 /** The number of the next "Unnamed" effigy. */ 866 private static int _nextUnnamed = 1; 867 868 /** If true, we've printed the warning about exporting to MoML. */ 869 private static Boolean _printedWarningFirstTime = false; 870 871 /** A synchronizedSortedSet TreeSet of controller updaters. */ 872 private static SortedSet<KeplerGraphFrameUpdater> _updaterSet = Collections 873 .synchronizedSortedSet(new TreeSet<KeplerGraphFrameUpdater>()); 874 875 /** 876 * Add a KeplerGraphFrameUpdater to the set of updaters that can make 877 * changes to components, e.g., the toolbar or menus. The ordering of 878 * updates can be specified by implementing the Comparable interface. 879 */ 880 public static void addUpdater(KeplerGraphFrameUpdater updater) { 881 boolean addUpdater = _updaterSet.add(updater); 882 if (!addUpdater) { 883 // if this happens even when the set doesn't contain 884 // your updater, check your updater's compareTo value 885 _log.warn("KeplerGraphFrame addUpdater(" + updater.getClass() 886 + ") returned false."); 887 } 888 } 889 890 /** Clear the set of KeplerGraphFrameUpdaters. */ 891 public static void clearUpdaters() { 892 _updaterSet.clear(); 893 } 894 895 public static Vector<TableauFrame> getOpenFrames() { 896 Vector<TableauFrame> frames = new Vector<TableauFrame>( 897 _openGraphFrames.size()); 898 for (BasicGraphFrame bgf : _openGraphFrames) { 899 frames.add(bgf); 900 } 901 return frames; 902 } 903 904 /** Remove an KeplerGraphFrameUpdater from the set of updaters. */ 905 public static void removeUpdater(KeplerGraphFrameUpdater updater) { 906 // _updaterSet.remove doesn't move the 907 // org.kepler.reporting.gui.ReportViewerPanel object correctly 908 // when the first window is closed. Switching to use iterator remove. 909 // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5095#c19 910 // _updaterSet.remove(updater); 911 synchronized (_updaterSet) { 912 Iterator<KeplerGraphFrameUpdater> itr = _updaterSet.iterator(); 913 while (itr.hasNext()) { 914 KeplerGraphFrameUpdater _updater = itr.next(); 915 if (_updater == updater) { 916 itr.remove(); 917 } 918 } 919 } 920 } 921 922 // Listeners 923 924 /** A panel for the canvas. */ 925 protected JPanel _canvasPanel; 926 927 /** Horizontal scrollbar. */ 928 protected JScrollBar _horizontalScrollBar; 929 930 /** Listener for scrolling on the horizontal scroll bar. */ 931 private ScrollBarListener _horizontalScrollBarListener; 932 933 /** Flag to determine whether to render scrollbars on the canvas. */ 934 private boolean _scrollBarFlag = false; 935 936 /** Vertical scrollbar. */ 937 protected JScrollBar _verticalScrollBar; 938 939 /** Listener for scrolling on the vertical scroll bar. */ 940 private ScrollBarListener _verticalScrollBarListener; 941 942 943 // private class ExecutionMoMLChangeRequest extends OffsetMoMLChangeRequest { 944 // // FIXME: This was only called from within createHierarchy()? It may have been called within copy() as well. 945 // private String _name; 946 947 // public ExecutionMoMLChangeRequest(Object o, NamedObj n, String s) { 948 // super(o, n, s); 949 // _name = new String(); 950 // } 951 952 // public void setName(String name) { 953 // _name = name; 954 // } 955 956 // protected void _execute() throws Exception { 957 // super._execute(); 958 // CompositeEntity container = (CompositeEntity) getContext(); 959 // NamedObj newObject = container.getEntity(_name); 960 961 // // _setLocation(compositeActor, point); 962 963 // // Kepler wants a different icon. 964 // // FIXME: It seems a little strange to do this inside 965 // // a change request, but the change request inner class 966 // // was already here, and perhaps this helps undo work 967 // // better or something? 968 // IconLoader _iconLoader = MoMLParser.getIconLoader(); 969 // if (_iconLoader != null) { 970 // _iconLoader.loadIconForClass( 971 // "ptolemy.actor.TypedCompositeActor", newObject); 972 // } 973 // } 974 // } 975}