org.kepler.gui.KeplerGraphFrame.java Source code

Java tutorial

Introduction

Here is the source code for org.kepler.gui.KeplerGraphFrame.java

Source

/*
 * Copyright (c) 2009-2010 The Regents of the University of California.
 * All rights reserved.
 *
 * '$Author: crawl $'
 * '$Date: 2012-11-26 14:22:25 -0800 (Mon, 26 Nov 2012) $' 
 * '$Revision: 31122 $'
 * 
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the above
 * copyright notice and the following two paragraphs appear in all copies
 * of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
 * ENHANCEMENTS, OR MODIFICATIONS.
 *
 */

package org.kepler.gui;

import java.awt.Adjustable;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Vector;
import java.util.regex.Matcher;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.kepler.gui.kar.ExportArchiveAction;
import org.kepler.kar.KARFile;
import org.kepler.kar.KARManager;
import org.kepler.objectmanager.ObjectManager;
import org.kepler.objectmanager.cache.LocalRepositoryManager;
import org.kepler.objectmanager.library.LibraryManager;
import org.kepler.util.FileUtil;
import org.kepler.util.RenameUtil;
import org.kepler.util.ShutdownNotifier;

import ptolemy.actor.gui.Configuration;
import ptolemy.actor.gui.Effigy;
import ptolemy.actor.gui.ModelDirectory;
import ptolemy.actor.gui.PtolemyEffigy;
import ptolemy.actor.gui.PtolemyPreferences;
import ptolemy.actor.gui.SizeAttribute;
import ptolemy.actor.gui.Tableau;
import ptolemy.actor.gui.TableauFrame;
import ptolemy.gui.MemoryCleaner;
import ptolemy.kernel.ComponentEntity;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NameDuplicationException;
import ptolemy.kernel.util.NamedObj;
import ptolemy.moml.LibraryAttribute;
import ptolemy.util.CancelException;
import ptolemy.util.MessageHandler;
import ptolemy.vergil.actor.ActorGraphFrame;
import ptolemy.vergil.basic.BasicGraphFrame;
import ptolemy.vergil.basic.BasicGraphFrameExtension;
import diva.graph.GraphController;
import diva.gui.toolbox.JCanvasPanner;
import diva.util.java2d.ShapeUtilities;

/** Kepler-specific overrides for the graph frame.
 * 
 */
public class KeplerGraphFrame extends ActorGraphFrame {

    /**
     * Constructor.
     * 
     * @param entity
     * @param tableau
     */
    public KeplerGraphFrame(CompositeEntity entity, Tableau tableau) {
        this(entity, tableau, null);
    }

    /**
     * Constructor
     * 
     * @param entity
     * @param tableau
     * @param defaultLibrary
     */
    public KeplerGraphFrame(CompositeEntity entity, Tableau tableau, LibraryAttribute defaultLibrary) {
        super(entity, tableau, defaultLibrary);
        _initKeplerGraphFrame();
    }

    /**
     * Override the dispose method to unattach any listeners that may keep this
     * model from getting garbage collected. Also remove KARManager's mapping of
     * this JFrame to KARfile if necessary.
     */
    @Override
    public void dispose() {

        if (_topPack instanceof KeplerMenuHandler) {
            KeplerMenuHandler topPack = (KeplerMenuHandler) _topPack;
            MenuMapper mm = topPack.menuMapper;
            mm.clear();
            /*
             * mm.printDebugInfo(); Map<String,Action> m =
             * mm.getPTIIMenuActionsMap(); m.clear(); KeplerMenuHandler kmh =
             * (KeplerMenuHandler) _topPack; kmh.clear(); _topPack = null;
             */
        }

        JMenuBar keplerMenuBar = getJMenuBar();
        /* int removed = */MemoryCleaner.removeActionListeners(keplerMenuBar);
        // System.out.println("KeplerGraphFrame menubar action listeners removed: "
        // + removed);

        CanvasDropTargetListener listener = CanvasDropTargetListener.getInstance();
        if (listener != null && _dropTarget != null) {
            _dropTarget.deRegisterAdditionalListener(listener);
        }

        if (_horizontalScrollBarListener != null && _horizontalScrollBar != null) {
            _horizontalScrollBar.removeAdjustmentListener(_horizontalScrollBarListener);
            _horizontalScrollBarListener = null;
        }
        if (_verticalScrollBarListener != null && _verticalScrollBarListener != null) {
            _verticalScrollBar.removeAdjustmentListener(_verticalScrollBarListener);
            _verticalScrollBarListener = null;
        }

        // remove JFrame => KARFile mapping from KARManager
        KARManager karManager = KARManager.getInstance();
        karManager.remove(this);

        TabManager tabManager = TabManager.getInstance();
        tabManager.removeAllFrameTabs(this);

        // this isn't safe. see:
        // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5095#c14
        // it is safe now after changes in r26444.
        ObjectManager.getInstance().removeNamedObjs(this.getModel());
        ViewManager.getInstance().removeOpenFrame(this);
        LibraryManager.getInstance().removeAllFrameTabs(this);

        // now call dispose on updaters in order
        synchronized (_updaterSet) {
            Iterator<KeplerGraphFrameUpdater> itr = _updaterSet.iterator();
            while (itr.hasNext()) {
                KeplerGraphFrameUpdater updater = itr.next();
                updater.dispose(this);
            }
        }

        ModelToFrameManager m2fm = ModelToFrameManager.getInstance();
        m2fm.remove(this);

        // see if this was the last window
        if (m2fm.numberOfOpenFrames() == 0) {
            ShutdownNotifier.shutdown();
        }

        KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        focusManager.clearGlobalFocusOwner();
        focusManager.downFocusCycle();

        super.dispose();
    }

    /**
     * Go to full screen.
     */
    @Override
    public void fullScreen() {

        // FIXME: do nothing since returning from full screen currently crashes
        // kepler. this is due to cancelFullScreen() dereferencing _splitPane,
        // which is never initialized.
    }

    // commented out since zoom() is commented out, and hides a super class _zoomFlag
    //protected boolean _zoomFlag = false;

    /** Get the graph controller. */
    public GraphController getGraphController() {
        return _controller;
    }

    public JToolBar getToolBar() {
        return this._toolbar;
    }

    /**
     * Return the size of the visible part of the canvas, in canvas coordinates.
     * 
     * @return Rectangle2D
     */
    public Rectangle2D getVisibleSize() {
        AffineTransform current = _jgraph.getGraphPane().getCanvas().getCanvasPane().getTransformContext()
                .getTransform();
        AffineTransform inverse;
        try {
            inverse = current.createInverse();
        } catch (NoninvertibleTransformException e) {
            throw new RuntimeException(e.toString());
        }
        Dimension size = _jgraph.getGraphPane().getCanvas().getSize();
        Rectangle2D visibleRect = new Rectangle2D.Double(0, 0, size.getWidth(), size.getHeight());
        return ShapeUtilities.transformBounds(visibleRect, inverse);
    }

    /** Set the model for this frame. */
    @Override
    public void setModel(NamedObj model) {
        super.setModel(model);

        // FIXME: ModelToFrameManager is probably unnecessary, this
        // information is in the Configuration.
        ModelToFrameManager m2fm = ModelToFrameManager.getInstance();
        m2fm.remove(this);
        m2fm.add(getModel(), this);
    }

    /** Update the History menu. */
    public void updateHistory(String absolutePath) throws IOException {
        _updateHistory(absolutePath, false);
    }

    /**
     * Create the menus that are used by this frame. It is essential that
     * _createGraphPane() be called before this.
     */
    @Override
    protected void _addMenus() {
        super._addMenus();

        // remove Open File... O accelerator.
        // Open... (for KARs) uses this accelerator now.
        for (int i = 0; i < _fileMenuItems.length; i++) {
            JMenuItem menuItem = _fileMenuItems[i];
            if (menuItem != null) {
                String menuItemText = menuItem.getText();
                if (menuItemText != null && menuItemText.equals("Open File")) {
                    // int removed =
                    // MemoryCleaner.removeActionListeners(menuItem);
                    // System.out.println("KeplerGraphFrame _fileMenuItems["+i+"] action listeners removed: "
                    // + removed);
                    menuItem.setAccelerator(null);
                }
            }
        }

        // see if the effigy for the top level workflow is called "Unnamed"
        // if so, renamed it to "Unnamed1"

        NamedObj namedObj = getModel();
        if (namedObj.getContainer() == null && namedObj.getName().length() == 0) {
            Effigy effigy = getEffigy();
            if (effigy != null) {
                String name = effigy.identifier.getExpression();
                if (name.equals("Unnamed")) {
                    String newName = name + _nextUnnamed;
                    _nextUnnamed++;
                    try {
                        // set the identifier, which is what shows up at the top
                        // of the window
                        effigy.identifier.setExpression(newName);
                    } catch (Exception e) {
                        report("Error setting effigy name.", e);
                    }

                    try {
                        namedObj.setName(newName);
                    } catch (Exception e) {
                        report("Error setting workflow name to " + newName + ".", e);
                    }

                }
            }
        }

        // let any KeplerGraphFrameUpdaters perform updates.
        Components components = new Components();

        // now call updateFrameComponents on updaters in order
        synchronized (_updaterSet) {
            Iterator<KeplerGraphFrameUpdater> itr = _updaterSet.iterator();
            while (itr.hasNext()) {
                KeplerGraphFrameUpdater updater = itr.next();
                updater.updateFrameComponents(components);
            }
        }

    }

    /**
     * Get the name of this object. If the parent class returns "Unnamed", use
     * the effigy's identifier as the name.
     * 
     * @return The name.
     */
    @Override
    protected String _getName() {
        String retval = super._getName();
        if (retval.equals("Unnamed")) {
            Effigy effigy = getEffigy();
            if (effigy != null) {
                retval = effigy.identifier.getExpression();
            }
        }
        return retval;
    }

    /**
     * Get the component whose size is to be recorded in the model when it is
     * saved into an output file. In this class, the return of this function is
     * the same as that of {@link #_getRightComponent()}. A subclass may
     * override this function to return a different component, such as a tab of
     * a tabbed pane, whose size is to be recorded instead.
     * 
     * @return The component whose size is to be recorded.
     */
    protected JComponent _getSizeComponent() {
        return _getRightComponent();
    }

    /**
     * Override BasicGraphFrame._initBasicGraphFrame()
     */
    @Override
    protected void _initBasicGraphFrame() {

        /**
         * @todo - FIXME - Need to move this further up the hierarchy, so other
         *       types of frames use it too. Don't put it in a launcher class
         *       like KeplerApplication, because it then gets overridden later,
         *       elsewhere in PTII
         */
        StaticGUIResources.setLookAndFeel();

        _initBasicGraphFrameInitialization();

        _dropTarget = BasicGraphFrameExtension.getDropTarget(_jgraph);

        // add a CanvasDropTargetListener so that other classes can get
        CanvasDropTargetListener listener = CanvasDropTargetListener.getInstance();
        _dropTarget.registerAdditionalListener(listener);

        ActionListener deletionListener = new DeletionListener();

        _rightComponent.registerKeyboardAction(deletionListener, "Delete",
                KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        _rightComponent.registerKeyboardAction(deletionListener, "BackSpace",
                KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

        _initBasicGraphFrameRightComponent();

        _jgraph.setRequestFocusEnabled(true);

        // Background color is parameterizable by preferences.
        Configuration configuration = getConfiguration();

        if (configuration != null) {
            try {
                // Set the PtolemyPreference to the desired background.
                // See
                // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=2321#c14
                PtolemyPreferences preferences = PtolemyPreferences
                        .getPtolemyPreferencesWithinConfiguration(configuration);
                if (_isDebugging) {
                    _log.debug("bg: " + BACKGROUND_COLOR);
                }
                if (preferences != null) {
                    float[] components = new float[4];
                    // Make sure we get only 4 elements in case the color space
                    // is bigger than 4
                    components = BACKGROUND_COLOR.getComponents(components);
                    preferences.backgroundColor.setExpression("{" + components[0] + "," + components[1] + ","
                            + components[2] + "," + components[3] + "}");
                    _rightComponent.setBackground(preferences.backgroundColor.asColor());
                    if (_isDebugging) {
                        _log.debug("desired background: " + BACKGROUND_COLOR + " actual background:  "
                                + preferences.backgroundColor.asColor());
                    }
                }
            } catch (IllegalActionException e1) {
                // Ignore the exception and use the default color.
            }
        }

        _initBasicGraphFrameRightComponentMouseListeners();

        try {
            // The SizeAttribute property is used to specify the size
            // of the JGraph component. Unfortunately, with Swing's
            // mysterious and undocumented handling of component sizes,
            // there appears to be no way to control the size of the
            // JGraph from the size of the Frame, which is specified
            // by the WindowPropertiesAttribute.
            SizeAttribute size = (SizeAttribute) getModel().getAttribute("_vergilSize", SizeAttribute.class);
            if (size != null) {
                size.setSize(_jgraph);
            } else {
                // Set the default size.
                // Note that the location is of the frame, while the size
                // is of the scrollpane.
                _jgraph.setPreferredSize(new Dimension(600, 400));
            }

            _initBasicGraphFrameSetZoomAndPan();
        } catch (Exception ex) {
            // Ignore problems here. Errors simply result in a default
            // size and location.
        }

        // Create the panner.
        _graphPanner = new JCanvasPanner(_jgraph);

        _horizontalScrollBar = new JScrollBar(Adjustable.HORIZONTAL);
        _verticalScrollBar = new JScrollBar(Adjustable.VERTICAL);

        // see if we want scrollbars on the canvas or not
        // the answer defaults to 'no'
        CanvasNavigationModifierFactory CNMfactory = (CanvasNavigationModifierFactory) getConfiguration()
                .getAttribute("canvasNavigationModifier");
        if (CNMfactory != null) { // get the scrollbar flag from the factory if
            // it exists in the config
            ScrollBarModifier modifier = CNMfactory.createScrollBarModifier();
            _scrollBarFlag = modifier.getScrollBarModifier();
        }

        _canvasPanel = new JPanel();

        _canvasPanel.setBorder(null);
        _canvasPanel.setLayout(new BorderLayout());

        if (_scrollBarFlag) {
            _canvasPanel.add(_horizontalScrollBar, BorderLayout.SOUTH);
            _canvasPanel.add(_verticalScrollBar, BorderLayout.EAST);
            _horizontalScrollBar.setModel(_jgraph.getGraphPane().getCanvas().getHorizontalRangeModel());
            _verticalScrollBar.setModel(_jgraph.getGraphPane().getCanvas().getVerticalRangeModel());
            _horizontalScrollBarListener = new ScrollBarListener(_horizontalScrollBar);
            _verticalScrollBarListener = new ScrollBarListener(_verticalScrollBar);
            _horizontalScrollBar.addAdjustmentListener(_horizontalScrollBarListener);
            _verticalScrollBar.addAdjustmentListener(_verticalScrollBarListener);
        }

        // NOTE: add _rightComponent instead of _jgraph since _rightComponent
        // may be sub-divided into tabbed panes.
        // see http://bugzilla.ecoinformatics.org/show_bug.cgi?id=3708
        _canvasPanel.add(_rightComponent, BorderLayout.CENTER);

        TabManager tabman = TabManager.getInstance();
        tabman.initializeTabs(this);

        ViewManager viewman = ViewManager.getInstance();
        viewman.initializeViews(this);
        try {
            viewman.addCanvasToLocation(_canvasPanel, this);
        } catch (Exception e) {
            throw new RuntimeException("Could not add canvas panel: " + e.getMessage());
        }

        // _jgraph.setMinimumSize(new Dimension(0, 0));

        getContentPane().add(viewman.getViewArea(this), BorderLayout.CENTER);

        // The toolbar panel is the container that contains the main toolbar and
        // any additional toolbars
        JPanel toolbarPanel = new JPanel();
        toolbarPanel.setLayout(new BoxLayout(toolbarPanel, BoxLayout.Y_AXIS)); // They
        // stack
        _toolbar = new JToolBar(); // The main Kepler toolbar
        toolbarPanel.add(_toolbar);
        getContentPane().add(toolbarPanel, BorderLayout.NORTH); // Place the
        // toolbar panel
        _initBasicGraphFrameToolBarZoomButtons();

        _initBasicGraphFrameActions();

        // Add a weak reference to this to keep track of all
        // the graph frames that have been created.
        _openGraphFrames.add(this);

        System.gc();
    }

    /**
     * KeplerGraphFrame Initializer method
     */
    protected void _initKeplerGraphFrame() {
        if (_isDebugging) {
            _log.debug("_initKeplerGraphFrame()");
        }
        ModelToFrameManager m2fm = ModelToFrameManager.getInstance();
        m2fm.add(getModel(), this);
    }

    /**
     * Open a dialog to prompt the user to save a KAR. Return false if the user
     * clicks "cancel", and otherwise return true.
     * 
     * Overrides Top._queryForSave()
     * 
     * @return _SAVED if the file is saved, _DISCARDED if the modifications are
     *         discarded, _CANCELED if the operation is canceled by the user,
     *         and _FAILED if the user selects save and the save fails.
     */
    @Override
    protected int _queryForSave() {
        Object[] options = { "Save", "Discard changes", "Cancel" };

        // making more generic since other items to go in the KAR
        // may be the reason for querying to save
        // String query = "Save changes to " + StringUtilities.split(_getName())
        // + "?";
        String query = "Save changes to KAR?";

        // Show the MODAL dialog
        int selected = JOptionPane.showOptionDialog(this, query, "Save Changes?", JOptionPane.YES_NO_CANCEL_OPTION,
                JOptionPane.QUESTION_MESSAGE, null, options, options[0]);

        if (selected == JOptionPane.YES_OPTION) {

            JButton saveButton = new JButton("Save Kar");
            ExportArchiveAction eaa = new ExportArchiveAction(this);
            eaa.setRefreshFrameAfterSave(false);
            // if the file already exists, call ExportArchiveAction.setSave()
            // so that the save as file dialog does is not used.
            KARFile karFile = KARManager.getInstance().get(this);
            if (karFile != null) {
                File file = karFile.getFileLocation();
                if (file.canWrite()) {
                    eaa.setSaveFile(file);
                }
            }
            saveButton.addActionListener(eaa);
            saveButton.doClick();

            if (eaa.saveSucceeded()) {
                setModified(false);
                return _SAVED;
            } else {
                return _FAILED;
            }

        }

        if (selected == JOptionPane.NO_OPTION) {
            return _DISCARDED;
        }

        return _CANCELED;
    }

    /**
     * Query the user for a filename, save the model to that file, and open a
     * new window to view the model. This overrides the base class to close the
     * old effigy if its name matches "UnnamedN". It also assigns a new LSID to
     * the new workflow unless it is unnamed.
     * 
     * @param extension
     *            If non-null, then the extension that is appended to the file
     *            name if there is no extension.
     * @return True if the save succeeds.
     */
    @Override
    protected boolean _saveAs(String extension) {
        boolean isUnnamed = false;

        // If the tableau was unnamed before, then we need
        // to close this window after doing the save.
        Effigy effigy = getEffigy();
        if (effigy != null) {
            String id = effigy.identifier.getExpression();

            // see if effigy id matches unnamed regex.
            Matcher matcher = RenameUtil.unnamedIdPattern.matcher(id);
            if (matcher.matches()) {
                isUnnamed = true;
            }
        }

        NamedObj model = getModel();

        // When we do a File -> Save As, the new and original
        // workflows need to have different LSIDs. This is taken
        // care of by RenameUtil.renameComponentEntity below.

        // NOTE: get the model directory before calling _saveAsHelper
        // since _saveAsHelper may dispose the effigy and thereby close
        // all associated tableaux
        ModelDirectory directory = (ModelDirectory) getConfiguration().getEntity(Configuration._DIRECTORY_NAME);

        // print a warning the first time this is called
        if (!_printedWarningFirstTime) {
            try {
                MessageHandler.warning("Exporting the workflow to XML does not save any associated workflow "
                        + "artifacts such as the report layout or module dependencies.");
            } catch (CancelException e) {
                // user clicked cancel button, so do not save.
                return false;
            }
            _printedWarningFirstTime = true;
        }

        // perform the save
        URL newModelURL = super._saveAsHelper(extension);

        // see if we successfully saved
        if (newModelURL != null) {

            // get the new model
            File newModelFile = new File(newModelURL.getFile());
            String newName = FileUtil.getFileNameWithoutExtension(newModelFile.getName());
            PtolemyEffigy newEffigy = (PtolemyEffigy) directory.getEffigy(newModelURL.toString());
            NamedObj newModel = newEffigy.getModel();

            // set newModel name back to the original name so we can
            // send it through renameComponentEntity
            try {
                newModel.setName(model.getName());
            } catch (IllegalActionException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (NameDuplicationException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }

            if (isUnnamed) {
                try {
                    // This will have the effect of closing all the
                    // tableaux associated with the unnamed model.
                    effigy.setContainer(null);

                    // remove the workflow from the object manager since
                    // its window went away.
                    ObjectManager objectManager = ObjectManager.getInstance();
                    objectManager.removeNamedObj(model);
                } catch (Exception e) {
                    report("Error closing effigy.", e);
                    return false;
                }
            }

            try {
                RenameUtil.renameComponentEntity((ComponentEntity) newModel, newName);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            // After the MoML has been saved to disk we add it to the library
            // and refresh the JTrees if it is saved in a local repository update the library
            LocalRepositoryManager lrm = LocalRepositoryManager.getInstance();
            if (lrm.isInLocalRepository(newModelFile)) {

                //_savekar.saveToCache();

                LibraryManager lm = LibraryManager.getInstance();
                try {
                    lm.addXML(newModelFile);
                } catch (Exception e2) {
                    MessageHandler.error("Error adding workflow to library.", e2);
                }
                try {
                    lm.refreshJTrees();
                } catch (IllegalActionException e2) {
                    MessageHandler.error("Error updating tree.", e2);
                }
            }

        }

        return true;
    }

    /**
     * Add the name of the last file open or set the name to the first position
     * if already in the list
     * 
     * @param file
     *            name of the file to add
     * @param delete
     *            If true, remove from the history list, otherwise the file is
     *            added to the beginning.
     */
    @Override
    protected void _updateHistory(String file, boolean delete) throws IOException {
        super._updateHistory(file, delete);
        // reload the menus in all open windows.
        MenuMapper.reloadAllMenubars();
    }

    /** A class to give KeplerGraphFrameUpdaters access to GUI components. */
    public class Components {
        /** Construct a new Component. Only KeplerGraphFrame may do this. */
        private Components() {
        }

        /** Get the KeplerGraphFrame. */
        public KeplerGraphFrame getFrame() {
            return KeplerGraphFrame.this;
        }

        /** Get the menu. */
        public JMenu getMenu() {
            return _graphMenu;
        }

        /** Get the toolbar. */
        public JToolBar getToolBar() {
            return _toolbar;
        }
    }

    /**
     * Listener for scrollbar events.
     */
    public class ScrollBarListener implements java.awt.event.AdjustmentListener {
        /**
         * constructor
         * 
         * @param sb
         *            JScrollBar
         */
        public ScrollBarListener(JScrollBar sb) {
            if (sb.getOrientation() == Adjustable.HORIZONTAL) {
                orientation = "h";
            } else {
                orientation = "v";
            }
        }

        /**
         * translate the model when the scrollbars move
         * 
         * @param e
         *            AdjustmentEvent
         */
        @Override
        public void adjustmentValueChanged(java.awt.event.AdjustmentEvent e) {
            int val = e.getValue();

            if (orientation.equals("h")) {
                if (_zoomFlag) {
                    return;
                }
                Rectangle2D visibleRect = getVisibleSize();
                Point2D newLeft = new Point2D.Double(val, 0);
                AffineTransform newTransform = _jgraph.getGraphPane().getCanvas().getCanvasPane()
                        .getTransformContext().getTransform();

                newTransform.translate(visibleRect.getX() - newLeft.getX(), 0);

                _jgraph.getGraphPane().getCanvas().getCanvasPane().setTransform(newTransform);

                if (_graphPanner != null) {
                    _graphPanner.repaint();
                }
            } else {
                if (_zoomFlag) {
                    return;
                }
                Rectangle2D visibleRect = getVisibleSize();
                Point2D newTop = new Point2D.Double(0, val);
                AffineTransform newTransform = _jgraph.getGraphPane().getCanvas().getCanvasPane()
                        .getTransformContext().getTransform();

                newTransform.translate(0, visibleRect.getY() - newTop.getY());

                _jgraph.getGraphPane().getCanvas().getCanvasPane().setTransform(newTransform);

                if (_graphPanner != null) {
                    _graphPanner.repaint();
                }
            }
        }

        private String orientation = "";
    }

    /** An ActionListener for handling deletion events. */
    private class DeletionListener implements ActionListener {
        /**
         * Delete any nodes or edges from the graph that are currently selected.
         * In addition, delete any edges that are connected to any deleted
         * nodes.
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            delete();
        }
    }

    /** Override the BasicGraphFrame Background color **/
    public static final Color BACKGROUND_COLOR = new Color(255, 255, 255);

    private static final Log _log = LogFactory.getLog(KeplerGraphFrame.class.getName());

    private static final boolean _isDebugging = _log.isDebugEnabled();

    /** The number of the next "Unnamed" effigy. */
    private static int _nextUnnamed = 1;

    /** If true, we've printed the warning about exporting to MoML. */
    private static Boolean _printedWarningFirstTime = false;

    /** A synchronizedSortedSet TreeSet of controller updaters. */
    private static SortedSet<KeplerGraphFrameUpdater> _updaterSet = Collections
            .synchronizedSortedSet(new TreeSet<KeplerGraphFrameUpdater>());

    /**
     * Add a KeplerGraphFrameUpdater to the set of updaters that can make
     * changes to components, e.g., the toolbar or menus. The ordering of
     * updates can be specified by implementing the Comparable interface.
     */
    public static void addUpdater(KeplerGraphFrameUpdater updater) {
        boolean addUpdater = _updaterSet.add(updater);
        if (!addUpdater) {
            // if this happens even when the set doesn't contain
            // your updater, check your updater's compareTo value
            _log.warn("KeplerGraphFrame addUpdater(" + updater.getClass() + ") returned false.");
        }
    }

    /** Clear the set of KeplerGraphFrameUpdaters. */
    public static void clearUpdaters() {
        _updaterSet.clear();
    }

    public static Vector<TableauFrame> getOpenFrames() {
        Vector<TableauFrame> frames = new Vector<TableauFrame>(_openGraphFrames.size());
        for (BasicGraphFrame bgf : _openGraphFrames) {
            frames.add(bgf);
        }
        return frames;
    }

    /** Remove an KeplerGraphFrameUpdater from the set of updaters. */
    public static void removeUpdater(KeplerGraphFrameUpdater updater) {
        // _updaterSet.remove doesn't move the
        // org.kepler.reporting.gui.ReportViewerPanel object correctly
        // when the first window is closed. Switching to use iterator remove.
        // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5095#c19
        // _updaterSet.remove(updater);
        synchronized (_updaterSet) {
            Iterator<KeplerGraphFrameUpdater> itr = _updaterSet.iterator();
            while (itr.hasNext()) {
                KeplerGraphFrameUpdater _updater = itr.next();
                if (_updater == updater) {
                    itr.remove();
                }
            }
        }
    }

    // Listeners

    /** A panel for the canvas. */
    protected JPanel _canvasPanel;

    /** Horizontal scrollbar. */
    protected JScrollBar _horizontalScrollBar;

    private ScrollBarListener _horizontalScrollBarListener;

    /** Flag to determine whether to render scrollbars on the canvas. */
    private boolean _scrollBarFlag = false;

    /** Vertical scrollbar. */
    protected JScrollBar _verticalScrollBar;

    private ScrollBarListener _verticalScrollBarListener;

    //    private class ExecutionMoMLChangeRequest extends OffsetMoMLChangeRequest {
    // // FIXME: This was only called from within createHierarchy()?  It may have been called within copy() as well.
    //       private String _name;

    //       public ExecutionMoMLChangeRequest(Object o, NamedObj n, String s) {
    //          super(o, n, s);
    //          _name = new String();
    //       }

    //       public void setName(String name) {
    //          _name = name;
    //       }

    //       protected void _execute() throws Exception {
    //          super._execute();
    //          CompositeEntity container = (CompositeEntity) getContext();
    //          NamedObj newObject = container.getEntity(_name);

    //          // _setLocation(compositeActor, point);

    //          // Kepler wants a different icon.
    //          // FIXME: It seems a little strange to do this inside
    //          // a change request, but the change request inner class
    //          // was already here, and perhaps this helps undo work
    //          // better or something?
    //          IconLoader _iconLoader = MoMLParser.getIconLoader();
    //          if (_iconLoader != null) {
    //             _iconLoader.loadIconForClass(
    //                   "ptolemy.actor.TypedCompositeActor", newObject);
    //          }
    //       }
    //    }
}