ffx.ui.MainPanel.java Source code

Java tutorial

Introduction

Here is the source code for ffx.ui.MainPanel.java

Source

/**
 * Title: Force Field X.
 *
 * Description: Force Field X - Software for Molecular Biophysics.
 *
 * Copyright: Copyright (c) Michael J. Schnieders 2001-2017.
 *
 * This file is part of Force Field X.
 *
 * Force Field X is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 3 as published by
 * the Free Software Foundation.
 *
 * Force Field X is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * Linking this library statically or dynamically with other modules is making a
 * combined work based on this library. Thus, the terms and conditions of the
 * GNU General Public License cover the whole combination.
 *
 * As a special exception, the copyright holders of this library give you
 * permission to link this library with independent modules to produce an
 * executable, regardless of the license terms of these independent modules, and
 * to copy and distribute the resulting executable under terms of your choice,
 * provided that you also meet, for each linked independent module, the terms
 * and conditions of the license of that module. An independent module is a
 * module which is not derived from or based on this library. If you modify this
 * library, you may extend this exception to your version of the library, but
 * you are not obligated to do so. If you do not wish to do so, delete this
 * exception statement from your version.
 */
package ffx.ui;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GraphicsConfigTemplate;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Month;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;

import javax.help.HelpSet;
import javax.help.JHelp;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.WindowConstants;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileSystemView;
import javax.vecmath.Vector3d;

import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.SystemUtils;
import org.biojava.bio.structure.Structure;

import ffx.crystal.Crystal;
import ffx.potential.MolecularAssembly;
import ffx.potential.bonded.Atom;
import ffx.potential.bonded.Bond;
import ffx.potential.bonded.MSNode;
import ffx.potential.bonded.MSRoot;
import ffx.potential.bonded.ROLS;
import ffx.potential.bonded.RendererCache;
import ffx.potential.bonded.RotamerLibrary;
import ffx.potential.parameters.ForceField;
import ffx.potential.parameters.ForceField.ForceFieldDouble;
import ffx.potential.parameters.ForceField.ForceFieldString;
import ffx.potential.parsers.ARCFileFilter;
import ffx.potential.parsers.BiojavaDataFilter;
import ffx.potential.parsers.BiojavaFilter;
import ffx.potential.parsers.ConversionFilter;
import ffx.potential.parsers.DYNFileFilter;
import ffx.potential.parsers.FFXFileFilter;
import ffx.potential.parsers.ForceFieldFileFilter;
import ffx.potential.parsers.ForceFieldFilter;
import ffx.potential.parsers.INTFileFilter;
import ffx.potential.parsers.INTFilter;
import ffx.potential.parsers.InducedFileFilter;
import ffx.potential.parsers.InducedFilter;
import ffx.potential.parsers.KeyFileFilter;
import ffx.potential.parsers.KeyFilter;
import ffx.potential.parsers.MergeFilter;
import ffx.potential.parsers.PDBFileFilter;
import ffx.potential.parsers.PDBFilter;
import ffx.potential.parsers.SystemFilter;
import ffx.potential.parsers.XYZFileFilter;
import ffx.potential.parsers.XYZFilter;
import ffx.potential.utils.PotentialsDataConverter;
import ffx.ui.properties.FFXLocale;
import ffx.utilities.Keyword;
import ffx.utilities.StringUtils;

import static ffx.utilities.StringUtils.pdbForID;

/**
 * The MainPanel class is the main container for Force Field X, handles file
 * input/output and is used to pass references among the various sub-Panels.
 *
 * @author Michael J. Schnieders
 *
 */
public final class MainPanel extends JPanel implements ActionListener, ChangeListener {

    // Static Variables
    private static final Logger logger = Logger.getLogger(MainPanel.class.getName());
    // Panel Order in the TabbedPane
    /**
     * Constant <code>GRAPHICS=0</code>
     */
    public static final int GRAPHICS = 0;
    /**
     * Constant <code>KEYWORDS=1</code>
     */
    public static final int KEYWORDS = 1;
    /**
     * Constant <code>MODELING=2</code>
     */
    public static final int MODELING = 2;
    /**
     * Constant <code>classpath=""</code>
     */
    public static String classpath;
    /**
     * Constant <code>ffxDir</code>
     */
    public static File ffxDir;
    private static File pwd;
    // FileFilters for filtering file selection in the JFileChooser
    private static JFileChooser fileChooser = null;
    /**
     * Constant <code>xyzFileFilter</code>
     */
    public static final XYZFileFilter xyzFileFilter = new XYZFileFilter();
    /**
     * Constant <code>arcFileFilter</code>
     */
    public static final ARCFileFilter arcFileFilter = new ARCFileFilter();
    /**
     * Constant <code>intFileFilter</code>
     */
    public static final INTFileFilter intFileFilter = new INTFileFilter();
    /**
     * Constant <code>dynFileFilter</code>
     */
    public static final DYNFileFilter dynFileFilter = new DYNFileFilter();
    /**
     * Constant <code>biojavaDataFilter</code>
     */
    public static final BiojavaDataFilter biojavaDataFilter = new BiojavaDataFilter();
    /**
     * Constant <code>indFileFilter</code>
     */
    public static final InducedFileFilter indFileFilter = new InducedFileFilter();
    /**
     * Constant <code>forceFieldFileFilter</code>
     */
    public static final ForceFieldFileFilter forceFieldFileFilter = new ForceFieldFileFilter();
    /**
     * Constant <code>pdbFileFilter</code>
     */
    public static final PDBFileFilter pdbFileFilter = new PDBFileFilter();
    /**
     * Constant <code>keyFileFilter</code>
     */
    public static final KeyFileFilter keyFileFilter = new KeyFileFilter();
    /**
     * Constant <code>ffxFileFilter</code>
     */
    public static final FFXFileFilter ffxFileFilter = new FFXFileFilter();
    private int fileOpenerThreads = -1;

    static {
        try {
            String ffxString = System.getProperty("ffx.dir", ".");
            ffxDir = new File(ffxString);
            classpath = System.getProperty("java.class.path");
            pwd = MainPanel.getPWD();
        } catch (Exception e) {
            logger.severe(" FFX directory could not be found.\n" + e);
        }
    }

    /**
     * <p>
     * getPWD</p>
     *
     * @return a {@link java.io.File} object.
     */
    public static File getPWD() {
        if (pwd == null) {
            pwd = new File(System.getProperty("user.dir",
                    FileSystemView.getFileSystemView().getDefaultDirectory().getAbsolutePath()));
        }
        return pwd;
    }

    /**
     * JFileChooser
     *
     * @return a {@link javax.swing.JFileChooser} object.
     */
    public static JFileChooser resetFileChooser() {
        if (fileChooser == null) {
            fileChooser = new JFileChooser();
        }
        fileChooser.resetChoosableFileFilters();
        fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        fileChooser.setFileHidingEnabled(false);
        fileChooser.setAcceptAllFileFilterUsed(true);
        fileChooser.setCurrentDirectory(getPWD());
        fileChooser.setMultiSelectionEnabled(false);
        fileChooser.setSelectedFile(null);
        return fileChooser;
    }

    // Force Field X Panels and Components
    private JFrame frame;
    private MSRoot dataRoot;
    private Hierarchy hierarchy;
    private MainMenu mainMenu;
    private GraphicsPanel graphicsPanel;
    private ModelingPanel modelingPanel;
    private KeywordPanel keywordPanel;
    // private LogPanel logPanel;
    private GraphicsCanvas graphicsCanvas;
    // Misc. Components
    // The SplitPane holds the Hierarchy and JTabbedPane
    private JSplitPane splitPane;
    private int splitPaneDivider;
    // Holds 3D Graphics, Keyword Editor, Modeling Commands and Log Panels
    private JTabbedPane tabbedPane;
    private JLabel statusLabel;
    private ForceFieldFilter forceFieldFilter;
    private FFXLocale locale = null;
    private JDialog aboutDialog = null;
    private JTextArea aboutTextArea = null;
    private Thread openThread = null;
    private SystemFilter activeFilter = null;
    private ConversionFilter activeConvFilter = null;
    private boolean oscillate = false;
    // TINKER Simulation Variables
    private TinkerSimulation simulation;
    private String ip = new String("");
    private int port = 2000;
    private InetAddress address = null;
    private InetSocketAddress socketAddress = new InetSocketAddress(port);
    private ModelingShell modelingShell = null;
    /**
     * Initialize all the sub-Panels and put them together
     */
    private boolean init = false;
    // Indicates how FFX is terminating.
    private ExitStatus exitType = ExitStatus.NORMAL;

    /**
     * MainPanel Constructor
     *
     * @param f Application Frame
     */
    public MainPanel(JFrame f) {
        frame = f;
    }

    /**
     * <p>
     * Constructor for MainPanel.</p>
     */
    public MainPanel() {
        frame = null;
    }

    /**
     * <p>
     * help</p>
     */
    public void help() {
        String helpHS = "ffx/help/jhelpset.hs";
        ClassLoader cl = getClass().getClassLoader();
        HelpSet hs = null;
        try {
            URL hsURL = HelpSet.findHelpSet(cl, helpHS);
            hs = new HelpSet(null, hsURL);
        } catch (Exception e) {
            logger.warning("HelpSet not found: " + e);
            return;
        }
        JHelp jhelp = new JHelp(hs);
        JFrame helpFrame = new JFrame();
        JFrame.setDefaultLookAndFeelDecorated(true);
        helpFrame.add(jhelp);
        helpFrame.setTitle("Force Field X Help");
        helpFrame.setSize(jhelp.getPreferredSize());
        helpFrame.setVisible(true);
        jhelp.setCurrentID("ForceFieldXBook");
        helpFrame.toFront();
    }

    /**
     * <p>
     * about</p>
     */
    public void about() {
        if (aboutDialog == null) {
            aboutDialog = new JDialog(frame, "About... ", true);
            URL ffxURL = getClass().getClassLoader().getResource("ffx/ui/icons/splash.png");
            ImageIcon logoIcon = new ImageIcon(ffxURL);
            JLabel logoLabel = new JLabel(logoIcon);
            logoLabel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
            Container contentpane = aboutDialog.getContentPane();
            contentpane.setLayout(new BorderLayout());
            initAbout();
            contentpane.add(aboutTextArea, BorderLayout.SOUTH);
            contentpane.add(logoLabel, BorderLayout.CENTER);
            aboutDialog.pack();
            Dimension dim = getToolkit().getScreenSize();
            Dimension ddim = aboutDialog.getSize();
            aboutDialog.setLocation((dim.width - ddim.width) / 2, (dim.height - ddim.height) / 2);
            aboutDialog.setResizable(false);
        }
        aboutDialog.setVisible(true);
    }

    /**
     * {@inheritDoc}
     *
     * Handle most File, Selection, Trajectory, Simulation, Window and Help Menu
     * Commands This should probably be partitioned between a few different
     * handlers
     */
    @Override
    public void actionPerformed(ActionEvent evt) {
        String arg = evt.getActionCommand();
        if (logger.isLoggable(Level.FINEST)) {
            logger.finest(" Action: " + arg);
        }
        // File Commands
        if (arg.equals("Open")) {
            open();
        } else if (arg.equals("DownloadFromPDB")) {
            openFromPDB();
        } else if (arg.equals("SaveAs")) {
            saveAsXYZ(null);
        } else if (arg.equals("Close")) {
            close();
        } else if (arg.equals("CloseAll")) {
            closeAll();
        } else if (arg.equals("ChooseKeyFile")) {
            chooseKey();
        } else if (arg.equals("ChooseLogFile")) {
            chooseLog();
        } else if (arg.equals("LoadInducedData")) {
            openInduced();
            // Selection Commands
        } else if (arg.equals("SelectAll")) {
            selectAll();
        } else if (arg.equals("MergeSelections")) {
            merge();
        } else if (arg.equals("HighlightSelections")) {
            highlightSelections(evt);
            // Trajectory
        } else if (arg.equals("Play")) {
            play();
        } else if (arg.equals("Stop")) {
            stop();
        } else if (arg.equals("StepForward")) {
            stepForward();
        } else if (arg.equals("StepBack")) {
            stepBack();
        } else if (arg.equals("Reset")) {
            reset();
        } else if (arg.equals("Oscillate")) {
            oscillate(evt);
        } else if (arg.equals("Frame")) {
            frame();
        } else if (arg.equals("Speed")) {
            speed();
        } else if (arg.equals("Skip")) {
            skip();
            // Simulation
        } else if (arg.equals("ConnectToLocalJob")) {
            connectToTINKER(null, null);
        } else if (arg.equals("ConnectToRemoteJob")) {
            connect();
        } else if (arg.equals("ReleaseJob")) {
            release();
        } else if (arg.equals("SetPort")) {
            setPort();
        } else if (arg.equals("SetRemoteJobAddress")) {
            setRemoteJobAddress();
            // Window
        } else if (arg.equals("ShowToolBar")) {
            showToolBar(evt);
        } else if (arg.equals("ShowTree")) {
            showTree(evt);
        } else if (arg.equals("ShowGlobalAxes")) {
            showGlobalAxes(evt);
        } else if (arg.equals("ResetPanes")) {
            resetPanes();
        } else if (arg.equals("ResetConsole")) {
            resetShell();
        } else if (arg.equals("OceanLookAndFeel")) {
            oceanLookAndFeel();
        } else if (arg.equals("WindowsLookAndFeel") || arg.equals("MacOSXLookAndFeel")
                || arg.equals("MotifLookAndFeel")) {
            platformLookAndFeel();
        } else if (arg.equals("ShrinkGraphicsWindow")) {
            resizePanes(20);
        } else if (arg.equals("ExpandGraphicsWindow")) {
            resizePanes(-20);
            // Help
        } else if (arg.equals("HelpContents")) {
            help();
        } else if (arg.equals("About")) {
            about();
            // Others
        } else if (arg.equals("GarbageCollect")) {
            Runtime.getRuntime().runFinalization();
            Runtime.getRuntime().gc();
        } else if (arg.equals("Exit")) {
            exit();
        } else {
            try {
                ClassLoader cl = MainPanel.class.getClassLoader();
                URL url = cl.getResource(arg);
                logger.info(url.toString());
                File structureFile = new File(url.getFile());
                logger.info(structureFile.toString());
                String tempFile = StringUtils.copyInputStreamToTmpFile(url.openStream(), structureFile.getName(),
                        "pdb");
                open(tempFile);
            } catch (Exception e) {
                System.err.println("MainPanel - Menu command not found: " + arg);
            }

        }
    }

    /**
     * Prompt the user to select an alternate key file.
     */
    private void chooseKey() {
        JFileChooser d = MainPanel.resetFileChooser();
        d.setFileFilter(new KeyFileFilter());
        d.setAcceptAllFileFilterUsed(false);
        FFXSystem sys = getHierarchy().getActive();
        if (sys != null) {
            File newCWD = sys.getFile();
            if (newCWD != null && newCWD.getParentFile() != null) {
                d.setCurrentDirectory(newCWD.getParentFile());
            }
        } else {
            return;
        }
        int result = d.showOpenDialog(this);
        if (result == JFileChooser.APPROVE_OPTION) {
            File f = d.getSelectedFile();
            sys.setKeyFile(f);
            sys.setKeywords(KeyFilter.open(f));
            getKeywordPanel().loadActive(sys);
        }
    }

    /**
     * Prompt the user to select an alternate log file
     */
    private void chooseLog() {
        JFileChooser d = resetFileChooser();
        FFXSystem sys = getHierarchy().getActive();
        if (sys != null) {
            File newCWD = sys.getFile();
            if (newCWD != null && newCWD.getParentFile() != null) {
                d.setCurrentDirectory(newCWD.getParentFile());
            }
        } else {
            return;
        }
        d.setDialogTitle("Select a log file");
        d.setAcceptAllFileFilterUsed(true);
        int result = d.showOpenDialog(this);
        if (result == JFileChooser.APPROVE_OPTION) {
            File f = d.getSelectedFile();
            if (f != null) {
                sys.setLogFile(f);
                setCWD(d.getCurrentDirectory());
                //getModelingPanel().selected();
            }
        }
    }

    /**
     * Detach the active FSystem's BranchGroup from the Scene and clear that
     * FSystem's data
     *
     * @return a {@link java.lang.Thread} object.
     */
    public Thread close() {
        FFXSystem m = hierarchy.getActive();
        return close(m);
    }

    /**
     * <p>
     * closeWait</p>
     */
    public synchronized void closeWait() {
        FFXSystem active = hierarchy.getActive();
        if (active == null) {
            logger.log(Level.INFO, " No active system to close.");
            return;
        }
        Thread thread = close(active);
        while (thread != null && thread.isAlive()) {
            try {
                wait(1);
            } catch (InterruptedException e) {
                String message = "Exception waiting for " + active + " to close.";
                logger.log(Level.WARNING, message, e);
            }
        }
    }

    /**
     * <p>
     * close</p>
     *
     * @param closedModel a {@link ffx.ui.FFXSystem} object.
     * @return a {@link java.lang.Thread} object.
     */
    public Thread close(FFXSystem closedModel) {
        if (closedModel != null && closedModel.getParent() != null) {
            Trajectory traj = closedModel.getTrajectory();
            if (traj != null) {
                traj.stop();
            }
            if (simulation != null && simulation.getFSystem() == closedModel) {
                release();
            }
            hierarchy.removeTreeNode(closedModel);
            closedModel.setView(RendererCache.ViewModel.DESTROY, null);
            Thread thread = new Thread(new UIFileCloser(closedModel));
            thread.start();
            return thread;
        }
        return null;
    }

    /**
     * Close all open systems.
     */
    public synchronized void closeAll() {
        while (hierarchy.getActive() != null) {
            closeWait();
        }
    }

    /**
     * Attempt to connect to a TINKER Simulation
     */
    private void connect() {
        if (simulation == null || simulation.isFinished()) {
            if (simulation != null) {
                simulation.release();
            }
            simulation = new TinkerSimulation(null, null, this, socketAddress);
            simulation.connect();
            mainMenu.setConnect(false);
            setPanel(GRAPHICS);
        }
    }

    /**
     * <p>
     * connectToTINKER</p>
     *
     * @param system a {@link ffx.ui.FFXSystem} object.
     * @param modelingThread a {@link java.lang.Thread} object.
     */
    public void connectToTINKER(FFXSystem system, Thread modelingThread) {
        if (simulation == null || simulation.isFinished()) {
            if (simulation != null) {
                simulation.release();
            }
            InetSocketAddress tempAddress = null;
            try {
                tempAddress = new InetSocketAddress(InetAddress.getLocalHost(), port);
            } catch (Exception e) {
                try {
                    tempAddress = new InetSocketAddress(InetAddress.getByName(null), port);
                } catch (Exception ex) {
                    System.err.println("Could not determine Local Host: " + ex);
                    return;
                }
            }
            simulation = new TinkerSimulation(system, modelingThread, this, tempAddress);
            if (modelingThread != null) {
                modelingThread.start();
            }
            simulation.connect();
            mainMenu.setConnect(false);
            setPanel(GRAPHICS);
        }
    }

    /**
     * <p>
     * createKeyFile</p>
     *
     * @param system a {@link ffx.ui.FFXSystem} object.
     * @return a boolean.
     */
    public boolean createKeyFile(FFXSystem system) {
        String message = new String("Please select a parameter file " + "and a TINKER Key file will be created.");
        String params = (String) JOptionPane.showInputDialog(this, message, "Parameter File",
                JOptionPane.QUESTION_MESSAGE, null, keywordPanel.getParamFiles(), null);
        if (params != null) {
            if (params.equalsIgnoreCase("Use an existing TINKER Key file")) {
                JFileChooser fc = resetFileChooser();
                fc.setDialogTitle("Choose a KEY File");
                fc.setCurrentDirectory(pwd);
                fc.setSelectedFile(null);
                fc.setFileFilter(keyFileFilter);
                int result = fc.showOpenDialog(this);
                if (result == JFileChooser.APPROVE_OPTION) {
                    File keyfile = fc.getSelectedFile();
                    if (keyfile.exists()) {
                        Hashtable<String, Keyword> keywordHash = KeyFilter.open(keyfile);
                        if (keywordHash != null) {
                            system.setKeywords(keywordHash);
                        } else {
                            return false;
                        }
                        system.setKeyFile(keyfile);
                        system.setForceField(null);
                        return true;
                    }
                }
            } else {
                File tempFile = system.getFile();
                if (tempFile.getParentFile().canWrite()) {
                    String path = system.getFile().getParent() + File.separatorChar;
                    String keyFileName = system.getName() + ".key";
                    File keyfile = new File(path + keyFileName);
                    try {
                        FileWriter fw = new FileWriter(keyfile);
                        BufferedWriter bw = new BufferedWriter(fw);
                        bw.write("\n");
                        bw.write("# Force Field Selection\n");
                        String tempParm = keywordPanel.getParamPath(params);
                        if (tempParm.indexOf(" ") > 0) {
                            tempParm = "\"" + keywordPanel.getParamPath(params) + "\"";
                        }
                        bw.write("PARAMETERS        " + tempParm + "\n");
                        bw.close();
                        fw.close();
                        Hashtable<String, Keyword> keywordHash = KeyFilter.open(keyfile);
                        if (keywordHash != null) {
                            system.setKeywords(keywordHash);
                        } else {
                            return false;
                        }
                        system.setKeyFile(keyfile);
                        system.setForceField(null);
                        return true;
                    } catch (Exception e) {
                        logger.warning("" + e);
                        message = new String("There was an error creating " + keyfile.getAbsolutePath());
                        JOptionPane.showMessageDialog(this, message);
                    }
                } else {
                    message = new String(
                            "Could not create a Key file because " + pwd.getAbsolutePath() + " is not writable");
                    JOptionPane.showMessageDialog(this, message);
                }
            }
        }
        return false;
    }

    /**
     * <p>
     * exit with current exit code (default: 0 (ExitStatus.NORMAL))</p>
     */
    public void exit() {
        exit(exitType);
    }

    /**
     * <p>
     * exit with a target ExitStatus</p>
     *
     * @param exitStatus How FFX has closed.
     */
    void exit(ExitStatus exitStatus) {
        // Package-private out of conservatism; may be safe to make public.
        savePrefs();
        System.exit(exitStatus.getExitCode());
    }

    /**
     * Set the current exit code.
     *
     * @param exitType Enumerated type for exit codes.
     */
    void setExitType(ExitStatus exitType) {
        // Package-private out of conservatism; may be safe to make public.
        this.exitType = exitType;
    }

    /**
     * <p>
     * frame</p>
     */
    public void frame() {
        Trajectory trajectory = getTrajectory();
        if (trajectory == null) {
            return;
        }
        String frameNumber = new String("" + trajectory.getFrame());
        frameNumber = JOptionPane.showInputDialog("Enter the Frame Number", frameNumber);
        try {
            int f = Integer.parseInt(frameNumber);
            trajectory.setFrame(f);
        } catch (NumberFormatException e) {
            return;
        }
    }

    /**
     * <p>
     * Getter for the field <code>dataRoot</code>.</p>
     *
     * @return a {@link ffx.potential.bonded.MSRoot} object.
     */
    public MSRoot getDataRoot() {
        return dataRoot;
    }

    /**
     * <p>
     * getFFXLocale</p>
     *
     * @return a {@link ffx.ui.properties.FFXLocale} object.
     */
    public FFXLocale getFFXLocale() {
        return locale;
    }

    /**
     * <p>
     * getGraphics3D</p>
     *
     * @return a {@link ffx.ui.GraphicsCanvas} object.
     */
    public GraphicsCanvas getGraphics3D() {
        return graphicsCanvas;
    }

    /**
     * <p>
     * Getter for the field <code>hierarchy</code>.</p>
     *
     * @return a {@link ffx.ui.Hierarchy} object.
     */
    public Hierarchy getHierarchy() {
        return hierarchy;
    }

    /**
     * <p>
     * Getter for the field <code>keywordPanel</code>.</p>
     *
     * @return a {@link ffx.ui.KeywordPanel} object.
     */
    public KeywordPanel getKeywordPanel() {
        return keywordPanel;
    }

    /**
     * <p>
     * Getter for the field <code>mainMenu</code>.</p>
     *
     * @return a {@link ffx.ui.MainMenu} object.
     */
    public MainMenu getMainMenu() {
        return mainMenu;
    }

    /**
     * <p>
     * Getter for the field <code>frame</code>.</p>
     *
     * @return a {@link java.awt.Frame} object.
     */
    public Frame getFrame() {
        return frame;
    }

    public ModelingPanel getModelingPanel() {
        return modelingPanel;
    }

    /**
     * <p>
     * Getter for the field <code>modelingShell</code>.</p>
     *
     * @return a {@link ffx.ui.ModelingShell} object.
     */
    public ModelingShell getModelingShell() {
        if (modelingShell == null) {
            modelingShell = new ModelingShell(this);
            modelingShell.run();
        }
        return modelingShell;
    }

    /**
     * <p>
     * getStatusBar</p>
     *
     * @return a {@link javax.swing.JLabel} object.
     */
    public JLabel getStatusBar() {
        return statusLabel;
    }

    /**
     * Get the Trajectory wrapper for the active system
     *
     * @return trajectory
     */
    public Trajectory getTrajectory() {
        FFXSystem system = hierarchy.getActive();
        if (system == null) {
            return null;
        }
        Trajectory trajectory = system.getTrajectory();
        if (trajectory != null) {
            return trajectory;
        }
        trajectory = new Trajectory(system, this);
        trajectory.setOscillate(oscillate);
        system.setTrajectory(trajectory);
        return trajectory;
    }

    /**
     * Return the active SystemFilter.
     *
     * @return
     */
    public SystemFilter getFilter() {
        return activeFilter;
    }

    /**
     * <p>
     * highlightSelections</p>
     *
     * @param evt a {@link java.awt.event.ActionEvent} object.
     */
    public void highlightSelections(ActionEvent evt) {
        if (evt.getSource() instanceof JCheckBoxMenuItem) {
            JCheckBoxMenuItem jcb = (JCheckBoxMenuItem) evt.getSource();
            if (jcb.isSelected()) {
                hierarchy.setHighlighting(true);
            } else {
                hierarchy.setHighlighting(false);
            }
        } else {
            boolean highlighting = RendererCache.highlightSelections;
            if (highlighting) {
                hierarchy.setHighlighting(false);
                mainMenu.setHighlighting(false);
            } else {
                hierarchy.setHighlighting(true);
                mainMenu.setHighlighting(true);
            }
        }
    }

    /**
     * Constant <code>version="1.0.0-BETA"</code>
     */
    public static final String version = "1.0.0-BETA";
    /**
     * Constant <code>date="January 2017"</code>
     */
    public static final String date = "January 2017";

    private static final String commitVersion;
    private static final String commitDate;
    private static final String commitSCM; // Presently using Git.

    /**
     * Attempts to initialize version, date, and SCM versioning from
     * target/ffx-mvn.properties. Fallback is to use defaults from above.
     */
    static {
        /* Dynamically set date.
        YearMonth now = YearMonth.now();
        Month month = now.getMonth();
        int year = now.getYear();
        String newDate = String.format("%s %d", month.toString(), year);*/

        String basedir = System.getProperty("basedir");
        File mvnProps = new File(basedir + "/target/ffx-mvn.properties");
        String cVers = version + "-unknown";
        String cDate = date;
        String cSCM = "";
        if (mvnProps.exists()) {
            try (BufferedReader br = new BufferedReader(new FileReader(mvnProps))) {
                String ffxVers = "1.0.0-BETA";
                String ffxvProp = "ffx.version=";

                /*String gitTag = "unknown";
                String gtProp = "git.tag=";*/
                String commitsCount = "";
                String ccProp = "git.commitsCount=";

                String timeProp = "timestamp=";

                String scmProp = "git.revision=";

                String line = br.readLine();
                while (line != null) {
                    line = line.trim();
                    if (line.startsWith(ffxvProp)) {
                        ffxVers = line.replaceFirst(ffxvProp, "");
                    } /* else if (line.startsWith(gtProp)) {
                      gitTag = line.replaceFirst(gtProp, "");
                      } */ else if (line.startsWith(ccProp)) {
                        commitsCount = line.replaceFirst(ccProp, "");
                    } else if (line.startsWith(timeProp)) {
                        String timeStr = line.replaceFirst(timeProp, "");
                        // Expected to be MM-dd-yyyy
                        String[] timeToks = timeStr.split("-");
                        try {
                            String year = timeToks[2];
                            int mon = Integer.parseInt(timeToks[0]);
                            Month month = Month.of(mon);
                            String mstr = month.toString();
                            cDate = String.format("%c%s %s", mstr.charAt(0), mstr.substring(1).toLowerCase(), year);
                        } catch (Exception ex) {
                            cDate = date;
                        }
                    } else if (line.startsWith(scmProp) && !line.contains("UNKNOWN_REVISION")) {
                        String scm = line.replaceFirst(scmProp, "");
                        cSCM = String.format("        %s %s \n", "SCM version ", scm);
                    }
                    line = br.readLine();
                }

                StringBuilder sb = new StringBuilder(ffxVers).append("-");
                /*if (gitTag.isEmpty()) {
                sb.append("unknown");
                } else {
                sb.append(gitTag);
                }*/

                if (!commitsCount.isEmpty()) {
                    sb.append(commitsCount);
                } else {
                    sb.append("unknown");
                }
                cVers = sb.toString();
            } catch (Exception ex) {
            }
        }
        commitVersion = cVers;
        commitDate = cDate;
        commitSCM = cSCM;
    }
    /**
     * Constant
     */
    public static final String border = " ______________________________________________________________________________\n";
    /**
     * Constant
     */
    public static final String title = "        FORCE FIELD X - Software for Molecular Biophysics \n";
    /**
     * Constant
     */
    public static final String aboutString = "        Version " + commitVersion + "  " + commitDate + " \n"
            + commitSCM // Will contain its own spacing/newline, or be empty.
            + "\n        Copyright (c)  Michael J. Schnieders    2001-2017 \n"
            + "        Portions Copyright (c)  Timothy D. Fenn 2009-2017 \n" + "\n"
            + "        All Rights Reserved \n" + "\n"
            + "        Force Field X is distributed under the GPL v. 3 license and \n"
            + "        hosted by the Schnieders Lab at The University of Iowa. \n" + "\n"
            + "        For publications please see http://ffx.eng.uiowa.edu/publications.html \n"
            + "        For the GPL v.3 license see http://ffx.eng.uiowa.edu/license.html \n";

    private void initAbout() {
        aboutTextArea = new JTextArea();
        Font font = Font.decode(Font.MONOSPACED);
        aboutTextArea.setFont(font);
        aboutTextArea.setText(aboutString);
        aboutTextArea.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
        aboutTextArea.setEditable(false);
    }

    /**
     * <p>
     * initialize</p>
     */
    public void initialize() {
        if (init) {
            return;
        }
        init = true;
        String dir = System.getProperty("user.dir",
                FileSystemView.getFileSystemView().getDefaultDirectory().getAbsolutePath());
        setCWD(new File(dir));
        locale = new FFXLocale("en", "US");
        JDialog splashScreen = null;
        ClassLoader loader = getClass().getClassLoader();
        if (!GraphicsEnvironment.isHeadless()) {
            // Splash Screen
            JFrame.setDefaultLookAndFeelDecorated(true);
            splashScreen = new JDialog(frame, false);
            ImageIcon logo = new ImageIcon(loader.getResource("ffx/ui/icons/splash.png"));
            JLabel ffxLabel = new JLabel(logo);
            ffxLabel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
            Container contentpane = splashScreen.getContentPane();
            contentpane.setLayout(new BorderLayout());
            contentpane.add(ffxLabel, BorderLayout.CENTER);
            splashScreen.setUndecorated(true);
            splashScreen.pack();
            Dimension screenDimension = getToolkit().getScreenSize();
            Dimension splashDimension = splashScreen.getSize();
            splashScreen.setLocation((screenDimension.width - splashDimension.width) / 2,
                    (screenDimension.height - splashDimension.height) / 2);
            splashScreen.setResizable(false);
            splashScreen.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            splashScreen.setVisible(true);
            // Make all pop-up Menus Heavyweight so they play nicely with Java3D
            JPopupMenu.setDefaultLightWeightPopupEnabled(false);
        }
        // Create the Root Node
        dataRoot = new MSRoot();
        Border bb = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
        statusLabel = new JLabel("  ");
        JLabel stepLabel = new JLabel("  ");
        stepLabel.setHorizontalAlignment(JLabel.RIGHT);
        JLabel energyLabel = new JLabel("  ");
        energyLabel.setHorizontalAlignment(JLabel.RIGHT);
        JPanel statusPanel = new JPanel(new GridLayout(1, 3));
        statusPanel.setBorder(bb);
        statusPanel.add(statusLabel);
        statusPanel.add(stepLabel);
        statusPanel.add(energyLabel);
        if (!GraphicsEnvironment.isHeadless()) {
            GraphicsConfigTemplate3D template3D = new GraphicsConfigTemplate3D();
            template3D.setDoubleBuffer(GraphicsConfigTemplate.PREFERRED);
            GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
                    .getBestConfiguration(template3D);
            graphicsCanvas = new GraphicsCanvas(gc, this);
            graphicsPanel = new GraphicsPanel(graphicsCanvas, statusPanel);
        }
        // Initialize various Panels
        hierarchy = new Hierarchy(this);
        hierarchy.setStatus(statusLabel, stepLabel, energyLabel);
        keywordPanel = new KeywordPanel(this);
        modelingPanel = new ModelingPanel(this);
        JPanel treePane = new JPanel(new BorderLayout());
        JScrollPane scrollPane = new JScrollPane(hierarchy, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        treePane.add(scrollPane, BorderLayout.CENTER);
        tabbedPane = new JTabbedPane();

        ImageIcon graphicsIcon = new ImageIcon(loader.getResource("ffx/ui/icons/monitor.png"));
        ImageIcon keywordIcon = new ImageIcon(loader.getResource("ffx/ui/icons/key.png"));
        ImageIcon modelingIcon = new ImageIcon(loader.getResource("ffx/ui/icons/cog.png"));
        tabbedPane.addTab(locale.getValue("Graphics"), graphicsIcon, graphicsPanel);
        tabbedPane.addTab(locale.getValue("KeywordEditor"), keywordIcon, keywordPanel);
        tabbedPane.addTab(locale.getValue("ModelingCommands"), modelingIcon, modelingPanel);
        tabbedPane.addChangeListener(this);
        splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, false, treePane, tabbedPane);

        /* splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, false,
         treePane, graphicsPanel); */
        splitPane.setResizeWeight(0.25);
        splitPane.setOneTouchExpandable(true);
        setLayout(new BorderLayout());
        add(splitPane, BorderLayout.CENTER);
        if (!GraphicsEnvironment.isHeadless()) {
            mainMenu = new MainMenu(this);
            add(mainMenu.getToolBar(), BorderLayout.NORTH);
            getModelingShell();
            loadPrefs();
            SwingUtilities.updateComponentTreeUI(SwingUtilities.getRoot(this));
            splashScreen.dispose();
        }
    }

    /**
     * <p>
     * isOpening</p>
     *
     * @return a boolean.
     */
    public boolean isOpening() {
        return (openThread != null && openThread.isAlive());
    }

    /**
     * Load preferences from the user node
     */
    public void loadPrefs() {
        String c = MainPanel.class.getName();
        JFrame frame1 = (JFrame) SwingUtilities.getRoot(this);
        Toolkit toolkit = getToolkit();
        Dimension screenSize = toolkit.getScreenSize();
        int x = preferences.getInt(c + ".x", screenSize.width / 8);
        int y = preferences.getInt(c + ".y", screenSize.height / 8);
        int width = preferences.getInt(c + ".width", screenSize.width * 3 / 4);
        int height = preferences.getInt(c + ".height", screenSize.height * 3 / 4);
        if (width > screenSize.width * 0.4 && width < screenSize.width * 0.8 && height > screenSize.height * 0.4
                && height < screenSize.height * 0.8) {
            frame1.setSize(width, height);
        } else {
            frame1.setSize(screenSize.width * 4 / 5, screenSize.height * 4 / 5);
        }
        if (x > 0 && x < screenSize.width / 2 && y > 0 && y < screenSize.height / 2) {
            frame1.setLocation(x, y);
        } else {
            frame1.setLocation(screenSize.width / 8, screenSize.height / 8);
        }
        splitPaneDivider = preferences.getInt(c + ".divider", 200);
        if (splitPaneDivider < frame1.getWidth() * (1.0f / 4.0f)) {
            splitPaneDivider = (int) (frame1.getWidth() * (1.0f / 4.0f));
        }
        splitPane.setDividerLocation(splitPaneDivider);
        if (!preferences.getBoolean(c + ".system", true)) {
            mainMenu.setSystemShowing(false);
            splitPane.setDividerLocation(0);
        } else {
            mainMenu.setSystemShowing(true);
        }
        if (!preferences.getBoolean(c + ".menu", true)) {
            remove(mainMenu.getToolBar());
            mainMenu.setMenuShowing(false);
            validate();
        } else {
            mainMenu.setMenuShowing(true);
        }
        try {
            port = preferences.getInt(c + ".port", 2000);
            ip = preferences.get(c + ".ip", InetAddress.getLocalHost().getHostAddress());
            if (ip != null) {
                address = InetAddress.getByName(ip);
                socketAddress = new InetSocketAddress(address, port);
            } else {
                socketAddress = new InetSocketAddress(port);
            }
        } catch (Exception e) {
            logger.log(Level.WARNING, e.toString());
        }
        if (graphicsCanvas != null) {
            graphicsCanvas.loadPrefs();
        }
    }

    /**
     * <p>
     * merge</p>
     */
    public void merge() {
        ArrayList<MSNode> activeNodes = hierarchy.getActiveNodes();
        if (activeNodes.size() >= 2) {
            merge(activeNodes);
        }
    }

    /**
     * Merge two or more selected FSystem Nodes into one FSystem node. There are
     * a few gotchas that need to be fixed
     *
     * @param nodesToMerge a {@link java.util.ArrayList} object.
     */
    public void merge(ArrayList<MSNode> nodesToMerge) {
        ArrayList<MSNode> activeNodes = new ArrayList<MSNode>();
        for (MSNode node : nodesToMerge) {
            if (node != null && !(node instanceof MSRoot)) {
                activeNodes.add(node);
            }
        }
        if (activeNodes.size() <= 1) {
            return;
        }
        // Set up a structure to hold the new system
        FFXSystem active = hierarchy.getActive();
        File file = SystemFilter.version(hierarchy.getActive().getFile());
        FFXSystem system = new FFXSystem(file, "Merge Result", active.getProperties());
        system.setKeyFile(active.getKeyFile());
        system.setKeywords(KeyFilter.open(active.getKeyFile()));
        // Fill arrays with the atoms and bonds from the systems to be combined
        ArrayList<Atom> mergedAtoms = new ArrayList<Atom>();
        ArrayList<Bond> mergedBonds = new ArrayList<Bond>();
        ArrayList<FFXSystem> systems = new ArrayList<FFXSystem>();
        TransformGroup parentTransformGroup = null;
        FFXSystem parentSystem;
        Transform3D parentTransform3D = new Transform3D();
        Vector3d parentPosition = new Vector3d();
        Vector3d atomPosition = new Vector3d();
        // TINKER Atom Numbers start at 1
        int atomNum = 1;
        Vector3d zero = new Vector3d(0.0, 0.0, 0.0);
        for (MSNode m : activeNodes) {
            parentSystem = (FFXSystem) m.getMSNode(FFXSystem.class);
            if (parentSystem == null) {
                return;
            }
            if (!systems.contains(parentSystem)) {
                graphicsCanvas.updateSceneWait(parentSystem, false, true, RendererCache.ViewModel.WIREFRAME, false,
                        null);
                systems.add(parentSystem);
            }
            // Move each atom into the global frame by applying the System
            // Transform to
            // relative atomic position
            parentTransformGroup = parentSystem.getOriginToRot();
            parentTransformGroup.getTransform(parentTransform3D);
            parentTransform3D.get(parentPosition);
            parentTransform3D.setTranslation(zero);
            // parentTransform3D.setScale(1.0d);
            ArrayList<Atom> atoms = m.getAtomList();
            ArrayList<ROLS> bonds = m.getBondList();
            for (Atom atom : atoms) {
                atom.removeFromParent();
                atom.setXYZIndex(atomNum++);
                mergedAtoms.add(atom);
                atom.getV3D(atomPosition);
                parentTransform3D.transform(atomPosition);
                atomPosition.add(parentPosition);
                atom.moveTo(atomPosition);
            }
            for (ROLS msm : bonds) {
                Bond bond = (Bond) msm;
                bond.removeFromParent();
                mergedBonds.add((Bond) msm);
            }
        }
        for (FFXSystem sys : systems) {
            close(sys);
        }
        MergeFilter mergeFilter = new MergeFilter(system, mergedAtoms, mergedBonds);
        UIFileOpener fileOpener = new UIFileOpener(mergeFilter, this);
        if (fileOpenerThreads > 0) {
            fileOpener.setNThreads(fileOpenerThreads);
        }
        Thread thread = new Thread(fileOpener);
        thread.start();
    }

    /**
     * <p>
     * merge</p>
     *
     * @param nodesToMerge an array of {@link ffx.potential.bonded.MSNode}
     * objects.
     */
    public void merge(MSNode[] nodesToMerge) {
        ArrayList<MSNode> activeNodes = new ArrayList<MSNode>();
        for (MSNode node : nodesToMerge) {
            if (node != null) {
                activeNodes.add(node);
            }
        }
        if (activeNodes.size() > 1) {
            merge(activeNodes);
        }
    }

    /**
     * <p>
     * oceanLookAndFeel</p>
     */
    public void oceanLookAndFeel() {
        try {
            UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
            SwingUtilities.updateComponentTreeUI(SwingUtilities.getRoot(this));
        } catch (Exception e) {
            return;
        }
    }

    /**
     * Trys to convert a file picked from a JFileChooser
     */
    private Thread open() {
        if (openThread != null && openThread.isAlive()) {
            return null;
        }
        JFileChooser fc = resetFileChooser();
        fc.setDialogTitle("Choose FFX, PDB, XYZ or ARC");
        fc.addChoosableFileFilter(xyzFileFilter);
        fc.addChoosableFileFilter(pdbFileFilter);
        fc.addChoosableFileFilter(intFileFilter);
        fc.addChoosableFileFilter(arcFileFilter);
        fc.addChoosableFileFilter(ffxFileFilter);
        fc.setAcceptAllFileFilterUsed(true);
        int result = fc.showOpenDialog(this);
        if (result == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();
            return open(file, null);
        }
        return null;
    }

    private UIFileOpener openFromUtils(File file, String commandDescription) {
        UIFileOpener opener = openInit(file, commandDescription);
        openThread = new Thread(opener);
        openThread.start();
        setPanel(GRAPHICS);
        return opener;
    }

    public Thread open(File file, String commandDescription) {
        UIFileOpener opener = openInit(file, commandDescription);
        openThread = new Thread(opener);
        openThread.start();
        setPanel(GRAPHICS);
        return openThread;
    }

    public Thread convert(Object data, File file, String commandDescription) {
        UIDataConverter converter = convertInit(data, file, commandDescription);
        openThread = new Thread(converter);
        openThread.start();
        setPanel(GRAPHICS);
        return openThread;
    }

    /**
     * Attempts to load the supplied file
     *
     * @param file File to open
     * @param commandDescription Description of the command that created this
     * file.
     * @return a {@link java.lang.Thread} object.
     */
    private UIFileOpener openInit(File file, String commandDescription) {
        if (file == null || !file.isFile() || !file.canRead()) {
            return null;
        }
        file = new File(FilenameUtils.normalize(file.getAbsolutePath()));
        // Set the Current Working Directory based on this file.
        setCWD(file.getParentFile());

        // Get "filename" from "filename.extension".
        String name = file.getName();
        String extension = FilenameUtils.getExtension(name);

        /**
         * Run a Force Field X script.
         */
        if (extension.equalsIgnoreCase("ffx") || extension.equalsIgnoreCase("groovy")) {
            ModelingShell shell = getModelingShell();
            shell.runFFXScript(file);
            boolean shutDown = Boolean.parseBoolean(System.getProperty("ffx.shutDown", "true"));
            if (java.awt.GraphicsEnvironment.isHeadless() && shutDown) {
                exit();
            } else {
                return null;
            }
        }

        // Create the CompositeConfiguration properties.
        CompositeConfiguration properties = Keyword.loadProperties(file);
        // Create an FFXSystem for this file.
        FFXSystem newSystem = new FFXSystem(file, commandDescription, properties);
        // Create a Force Field.
        forceFieldFilter = new ForceFieldFilter(properties);
        ForceField forceField = forceFieldFilter.parse();
        String patches[] = properties.getStringArray("patch");
        for (String patch : patches) {
            logger.info(" Attempting to read force field patch from " + patch + ".");
            CompositeConfiguration patchConfiguration = new CompositeConfiguration();
            patchConfiguration.addProperty("parameters", patch);
            forceFieldFilter = new ForceFieldFilter(patchConfiguration);
            ForceField patchForceField = forceFieldFilter.parse();
            forceField.append(patchForceField);
            if (RotamerLibrary.addRotPatch(patch)) {
                logger.info(String.format(" Loaded rotamer definitions from patch %s.", patch));
            }
        }
        newSystem.setForceField(forceField);
        SystemFilter systemFilter = null;

        // Decide what parser to use.
        if (xyzFileFilter.acceptDeep(file)) {
            // Use the TINKER Cartesian Coordinate File Parser.
            systemFilter = new XYZFilter(file, newSystem, forceField, properties);
        } else if (intFileFilter.acceptDeep(file)) {
            // Use the TINKER Internal Coordinate File Parser.
            systemFilter = new INTFilter(file, newSystem, forceField, properties);
        } else {
            // Use the PDB File Parser.
            systemFilter = new PDBFilter(file, newSystem, forceField, properties);
        }

        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        activeFilter = systemFilter;
        UIFileOpener fileOpener = new UIFileOpener(systemFilter, this);
        if (fileOpenerThreads > 0) {
            fileOpener.setNThreads(fileOpenerThreads);
        }
        return fileOpener;
        //return new UIFileOpener(systemFilter, this);
    }

    /**
     * Attempts to load from the supplied data structure
     *
     * @param data Data structure to load from
     * @param file Source file
     * @param commandDescription Description of the command that created this
     * file.
     * @return A thread-based UIDataConverter
     */
    private UIDataConverter convertInit(Object data, File file, String commandDescription) {
        if (data == null) {
            return null;
        }

        // Create the CompositeConfiguration properties.
        CompositeConfiguration properties = Keyword.loadProperties(file);
        // Create an FFXSystem for this file.
        FFXSystem newSystem = new FFXSystem(file, commandDescription, properties);
        // Create a Force Field.
        forceFieldFilter = new ForceFieldFilter(properties);
        ForceField forceField = forceFieldFilter.parse();
        String patches[] = properties.getStringArray("patch");
        for (String patch : patches) {
            logger.info(" Attempting to read force field patch from " + patch + ".");
            CompositeConfiguration patchConfiguration = new CompositeConfiguration();
            patchConfiguration.addProperty("parameters", patch);
            forceFieldFilter = new ForceFieldFilter(patchConfiguration);
            ForceField patchForceField = forceFieldFilter.parse();
            forceField.append(patchForceField);
            if (RotamerLibrary.addRotPatch(patch)) {
                logger.info(String.format(" Loaded rotamer definitions from patch %s.", patch));
            }
        }
        newSystem.setForceField(forceField);
        ConversionFilter convFilter = null;

        // Decide what parser to use.
        if (biojavaDataFilter.accept(data)) {
            convFilter = new BiojavaFilter((Structure) data, newSystem, forceField, properties);
        } else {
            throw new IllegalArgumentException("Not a recognized data structure.");
        }

        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        activeConvFilter = convFilter;
        return new UIDataConverter(data, file, convFilter, this);
    }

    private UIFileOpener openFromUtils(List<File> files, String commandDescription) {
        UIFileOpener openFile = openInit(files, commandDescription);
        openThread = new Thread(openFile);
        openThread.start();
        setPanel(GRAPHICS);
        return openFile;
    }

    public Thread open(List<File> files, String commandDescription) {
        UIFileOpener openFile = openInit(files, commandDescription);
        openThread = new Thread(openFile);
        openThread.start();
        setPanel(GRAPHICS);
        return openThread;
    }

    /**
     * Attempts to load the supplied file
     *
     * @param files Files to open
     * @param commandDescription Description of the command that created this
     * file.
     * @return a {@link java.lang.Thread} object.
     */
    private UIFileOpener openInit(List<File> files, String commandDescription) {
        if (files == null) {
            return null;
        }
        File file = new File(FilenameUtils.normalize(files.get(0).getAbsolutePath()));
        // Set the Current Working Directory based on this file.
        setCWD(file.getParentFile());

        // Get "filename" from "filename.extension".
        String name = file.getName();
        String extension = FilenameUtils.getExtension(name);

        // Create the CompositeConfiguration properties.
        CompositeConfiguration properties = Keyword.loadProperties(file);
        forceFieldFilter = new ForceFieldFilter(properties);
        ForceField forceField = forceFieldFilter.parse();

        // Create an FFXSystem for this file.
        FFXSystem newSystem = new FFXSystem(file, commandDescription, properties);
        String patches[] = properties.getStringArray("patch");
        for (String patch : patches) {
            logger.info(" Attempting to read force field patch from " + patch + ".");
            CompositeConfiguration patchConfiguration = new CompositeConfiguration();
            patchConfiguration.addProperty("parameters", patch);
            forceFieldFilter = new ForceFieldFilter(patchConfiguration);
            ForceField patchForceField = forceFieldFilter.parse();
            forceField.append(patchForceField);
            if (RotamerLibrary.addRotPatch(patch)) {
                logger.info(String.format(" Loaded rotamer definitions from patch %s.", patch));
            }
        }
        newSystem.setForceField(forceField);
        // Decide what parser to use.
        SystemFilter systemFilter = null;
        if (xyzFileFilter.acceptDeep(file)) {
            // Use the TINKER Cartesian Coordinate File Parser.
            systemFilter = new XYZFilter(files, newSystem, forceField, properties);
        } else if (intFileFilter.acceptDeep(file)) {
            // Use the TINKER Internal Coordinate File Parser.
            systemFilter = new INTFilter(files, newSystem, forceField, properties);
        } else {
            // Use the PDB File Parser.
            systemFilter = new PDBFilter(files, newSystem, forceField, properties);
        }

        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        activeFilter = systemFilter;
        //return new UIFileOpener(systemFilter, this);
        UIFileOpener fileOpener = new UIFileOpener(systemFilter, this);
        if (fileOpenerThreads > 0) {
            fileOpener.setNThreads(fileOpenerThreads);
        }
        return fileOpener;
    }

    public synchronized MolecularAssembly[] openWaitUtils(String file) {
        UIFileOpener opener = openFromUtils(file);
        Thread thread = new Thread(opener);
        while (thread != null && thread.isAlive()) {
            try {
                wait(1);
            } catch (InterruptedException e) {
                String message = "Exception waiting for " + file + " to open.";
                logger.log(Level.WARNING, message, e);
                return null;
            }
        }

        MolecularAssembly systems[] = activeFilter.getMolecularAssemblys();
        if (systems != null) {
            int n = systems.length;
            FFXSystem ffxSystems[] = new FFXSystem[n];
            FFXSystem allSystems[] = getHierarchy().getSystems();
            int total = allSystems.length;
            for (int i = 0; i < n; i++) {
                ffxSystems[i] = allSystems[total - n + i];
            }
            return ffxSystems;
        } else {
            return null;
        }
    }

    /**
     * <p>
     * openWait</p>
     *
     * @param file a {@link java.lang.String} object.
     * @return an array of {@link ffx.ui.FFXSystem} objects.
     */
    public synchronized FFXSystem[] openWait(String file) {
        Thread thread = open(file);
        while (thread != null && thread.isAlive()) {
            try {
                wait(1);
            } catch (InterruptedException e) {
                String message = "Exception waiting for " + file + " to open.";
                logger.log(Level.WARNING, message, e);
                return null;
            }

        }

        MolecularAssembly systems[] = activeFilter.getMolecularAssemblys();
        if (systems != null) {
            int n = systems.length;
            FFXSystem ffxSystems[] = new FFXSystem[n];
            FFXSystem allSystems[] = getHierarchy().getSystems();
            int total = allSystems.length;
            for (int i = 0; i < n; i++) {
                ffxSystems[i] = allSystems[total - n + i];
            }
            return ffxSystems;
        } else {
            return null;
        }
    }

    /**
     * <p>
     * openWait</p>
     *
     * @param file a {@link java.lang.String} object.
     * @param nThreads
     * @return an array of {@link ffx.ui.FFXSystem} objects.
     */
    public synchronized FFXSystem[] openWait(String file, int nThreads) {
        fileOpenerThreads = nThreads;
        FFXSystem[] systs = openWait(file);
        fileOpenerThreads = -1;
        return systs;
    }

    /**
     * <p>
     * openWait</p>
     *
     * @param files an array of {@link java.lang.String} objects.
     * @return an array of {@link ffx.ui.FFXSystem} objects.
     */
    public synchronized FFXSystem[] openWait(String files[]) {
        Thread thread = open(files);
        while (thread != null && thread.isAlive()) {
            try {
                wait(1);
            } catch (InterruptedException e) {
                String message = "Exception waiting for " + files[0] + " to open.";
                logger.log(Level.WARNING, message, e);
                return null;
            }
        }

        MolecularAssembly systems[] = activeFilter.getMolecularAssemblys();
        if (systems != null) {
            int n = systems.length;
            FFXSystem ffxSystems[] = new FFXSystem[n];
            FFXSystem allSystems[] = getHierarchy().getSystems();
            int total = allSystems.length;
            for (int i = 0; i < n; i++) {
                ffxSystems[i] = allSystems[total - n + i];
            }
            return ffxSystems;
        } else {
            return null;
        }
    }

    /**
     * <p>
     * openWait</p>
     *
     * @param files an array of {@link java.lang.String} objects.
     * @param nThreads
     * @return an array of {@link ffx.ui.FFXSystem} objects.
     */
    public synchronized FFXSystem[] openWait(String files[], int nThreads) {
        fileOpenerThreads = nThreads;
        FFXSystem[] systs = openWait(files);
        fileOpenerThreads = -1;
        return systs;
    }

    /**
     * Converts a non-Force Field X data structure into an array of FFXSystem[].
     * Presently does not yet have support for array- or list-based data
     * structures, only singular objects.
     *
     * @param data Outside data structure
     * @param file Source file
     * @return An array of FFXSystem
     */
    public synchronized FFXSystem[] convertWait(Object data, File file) {
        if (file == null) {
            try {
                file = PotentialsDataConverter.getDefaultFile(data);
            } catch (FileNotFoundException | IllegalArgumentException ex) {
                logger.warning(String.format(" Exception in finding file for data structure: %s", ex.toString()));
                return null;
            }
        }
        String name = file.getName();

        Thread thread = convert(data, file, null);
        while (thread != null && thread.isAlive()) {
            try {
                wait(1);
            } catch (InterruptedException e) {
                String message = "Exception waiting for " + name + " to open.";
                logger.log(Level.WARNING, message, e);
                return null;
            }
        }

        MolecularAssembly systems[] = activeConvFilter.getMolecularAssemblys();
        if (systems != null) {
            int n = systems.length;
            FFXSystem ffxSystems[] = new FFXSystem[n];
            FFXSystem allSystems[] = getHierarchy().getSystems();
            int total = allSystems.length;
            for (int i = 0; i < n; i++) {
                ffxSystems[i] = allSystems[total - n + i];
            }
            return ffxSystems;
        } else {
            return null;
        }
    }

    /**
     * <p>
     * open</p>
     *
     * @param name a {@link java.lang.String} object.
     * @return a {@link java.lang.Thread} object.
     */
    public Thread open(String name) {
        File file = resolveName(name);
        if (file == null) {
            logger.log(Level.WARNING, "{0}: could not be found.", name);
            return null;
        }
        return open(file, null);
    }

    private UIFileOpener openFromUtils(String name) {
        File file = resolveName(name);
        if (file == null) {
            logger.log(Level.WARNING, "{0}: could not be found.", name);
            return null;
        }
        return openFromUtils(file, null);
    }

    private File resolveName(String name) {
        // Return null if name == null.
        if (name == null) {
            return null;
        }
        File file = new File(name);
        // If the file exists, return it.
        if (file.exists()) {
            return file;
        }
        // Check for a file in the CWD.
        file = new File(pwd + File.separator + name);
        if (file.exists()) {
            return file;
        }
        // Check for an HTTP address
        if (name.startsWith("http://")) {
            String fileName = FilenameUtils.getName(name);
            if (fileName == null) {
                return null;
            }
            return downloadURL(name);
        }
        // Check for a PDB ID.
        if (name.length() == 4) {
            String fileName = name + ".pdb";
            String path = getPWD().getAbsolutePath();
            File pdbFile = new File(path + File.separatorChar + fileName);
            if (!pdbFile.exists()) {
                String fromURL = pdbForID(name);
                return downloadURL(fromURL);
            } else {
                return pdbFile;
            }
        }
        return null;
    }

    /**
     * <p>
     * open</p>
     *
     * @param names an array of {@link java.lang.String} objects.
     * @return a {@link java.lang.Thread} object.
     */
    public Thread open(String names[]) {
        if (names == null) {
            return null;
        }
        int n = names.length;
        List<File> files = new ArrayList<>();
        // Resolve all file names.
        for (int i = 0; i < n; i++) {
            File file = resolveName(names[i]);
            if (file == null || !file.exists()) {
                return null;
            }
            files.add(file);
        }
        return open(files, null);
    }

    private UIFileOpener openFromUtils(String[] names) {
        if (names == null) {
            return null;
        }
        int n = names.length;
        List<File> files = new ArrayList<>();
        // Resolve all file names.
        for (int i = 0; i < n; i++) {
            File file = resolveName(names[i]);
            if (file == null || !file.exists()) {
                return null;
            }
            files.add(file);
        }
        return openFromUtils(files, null);
    }

    /**
     * Opens a file from the PDB
     */
    public void openFromPDB() {
        if (openThread != null && openThread.isAlive()) {
            return;
        }
        String code = JOptionPane.showInputDialog("Enter the PDB Identifier (4 characters)", "");
        if (code == null) {
            return;
        }
        code = code.toLowerCase().trim();
        if (code == null || code.length() != 4) {
            return;
        }
        String fileName = code + ".pdb";
        String path = getPWD().getAbsolutePath();
        File pdbFile = new File(path + File.separatorChar + fileName);
        CompositeConfiguration properties = Keyword.loadProperties(pdbFile);
        forceFieldFilter = new ForceFieldFilter(properties);
        ForceField forceField = forceFieldFilter.parse();
        FFXSystem newSystem = new FFXSystem(pdbFile, "PDB", properties);
        newSystem.setForceField(forceField);
        if (!pdbFile.exists()) {
            String fromURL = pdbForID(code);
            pdbFile = downloadURL(fromURL);
            if (pdbFile == null || !pdbFile.exists()) {
                return;
            }
        } else {
            String message = String.format(" Reading the local copy of the PDB file %s.", pdbFile);
            logger.info(message);
        }
        PDBFilter pdbFilter = new PDBFilter(pdbFile, newSystem, forceField, properties);
        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        UIFileOpener openFile = new UIFileOpener(pdbFilter, this);
        if (fileOpenerThreads > 0) {
            openFile.setNThreads(fileOpenerThreads);
        }
        openThread = new Thread(openFile);
        openThread.start();
        setPanel(GRAPHICS);
    }

    private File downloadURL(String fromString) {
        /**
         * Check for null input.
         */
        if (fromString == null) {
            return null;
        }

        /**
         * Convert the string to a URL instance.
         */
        URL fromURL = null;
        try {
            fromURL = new URL(fromString);
        } catch (MalformedURLException e) {
            String message = String.format(" URL incorrectly formatted %s.", fromString);
            logger.log(Level.INFO, message, e);
            return null;
        }

        /**
         * Download the URL to a local file.
         */
        logger.info(String.format(" Downloading %s", fromString));
        try {
            File toFile = new File(FilenameUtils.getName(fromURL.getPath()));
            FileUtils.copyURLToFile(fromURL, toFile, 1000, 1000);
            logger.info(String.format(" Saved to %s\n", toFile.getPath()));
            return toFile;
        } catch (IOException ex) {
            logger.log(Level.INFO, " Failed to read URL " + fromURL.getPath(), ex);
            return null;
        }
    }

    private void openInduced() {
        FFXSystem active = hierarchy.getActive();
        resetFileChooser();
        fileChooser.setCurrentDirectory(pwd);
        fileChooser.setSelectedFile(active.getFile());
        fileChooser.setDialogTitle("Choose Induced Dipole File");
        fileChooser.addChoosableFileFilter(indFileFilter);
        fileChooser.setAcceptAllFileFilterUsed(true);
        fileChooser.setFileFilter(indFileFilter);
        int result = fileChooser.showOpenDialog(this);
        if (result == JFileChooser.APPROVE_OPTION) {
            File f = fileChooser.getSelectedFile();
            InducedFilter indFilter = new InducedFilter(active, f);
            indFilter.read();
        }
    }

    /**
     * Attempt to convert a TINKER *.key file
     *
     * @param newSystem FFXSystem that needs an associated Key File
     * @param createKey flag to create a key file be created
     * @return Key file that was found, or null if nothing could be found
     */
    public boolean openKey(FFXSystem newSystem, boolean createKey) {
        String keyFileName = null;
        String temp = newSystem.getFile().getName();
        int dot = temp.lastIndexOf(".");
        if (dot > 0) {
            keyFileName = temp.substring(0, dot) + ".key";
        } else {
            keyFileName = temp + ".key";
        }
        String path = newSystem.getFile().getParent() + File.separator;
        File keyfile = new File(path + keyFileName);
        // System.out.println("" + keyfile);
        if (keyfile.exists()) {
            Hashtable<String, Keyword> keywordHash = KeyFilter.open(keyfile);
            if (keywordHash != null) {
                newSystem.setKeywords(keywordHash);
            } else {
                return false;
            }
            newSystem.setKeyFile(keyfile);
            newSystem.setForceField(null);
            return true;
        }
        keyfile = new File(path + "tinker.key");
        if (keyfile.exists()) {
            logger.info("Using tinker.key: " + keyfile);
            Hashtable<String, Keyword> keywordHash = KeyFilter.open(keyfile);
            if (keywordHash != null) {
                newSystem.setKeywords(keywordHash);
            } else {
                return false;
            }
            newSystem.setKeyFile(keyfile);
            newSystem.setForceField(null);
            return true;
        }
        if (createKey) {
            return createKeyFile(newSystem);
        }
        return false;
    }

    /**
     * <p>
     * openOn</p>
     *
     * @param f a {@link java.io.File} object.
     * @param oldSystem a {@link ffx.ui.FFXSystem} object.
     * @param command a {@link java.lang.String} object.
     */
    public void openOn(File f, FFXSystem oldSystem, String command) {
        XYZFilter.readOnto(f, oldSystem);
        oldSystem.setCommandDescription(command);
        graphicsCanvas.updateScene(oldSystem, true, false, null, false, null);
        getHierarchy().updateStatus();
        getHierarchy().repaint();
    }

    /**
     * <p>
     * oscillate</p>
     *
     * @param evt a {@link java.awt.event.ActionEvent} object.
     */
    public void oscillate(ActionEvent evt) {
        oscillate = ((JCheckBoxMenuItem) evt.getSource()).isSelected();
        FFXSystem[] systems = getHierarchy().getSystems();

        if (systems == null) {
            return;
        }

        for (int i = 0; i < systems.length; i++) {
            Trajectory trajectory = systems[i].getTrajectory();
            if (trajectory != null) {
                trajectory.setOscillate(oscillate);
            }
        }
    }

    /**
     * <p>
     * platformLookAndFeel</p>
     */
    public void platformLookAndFeel() {
        try {
            if (SystemUtils.IS_OS_LINUX) {
                UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");
            } else {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            }
            SwingUtilities.updateComponentTreeUI(SwingUtilities.getRoot(this));
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
                | UnsupportedLookAndFeelException e) {
            logger.log(Level.WARNING, "Can''t set look and feel: {0}", e);
        }
    }

    /**
     * <p>
     * play</p>
     */
    public void play() {
        Trajectory trajectory = getTrajectory();
        if (trajectory == null) {
            return;
        }
        trajectory.start();
    }

    /**
     * Close the connection to a running simulation
     */
    private void release() {
        if (simulation != null) {
            simulation.release();
            simulation = null;
            mainMenu.setConnect(true);
        }
    }

    /**
     * <p>
     * reset</p>
     */
    public void reset() {
        Trajectory trajectory = getTrajectory();
        if (trajectory == null) {
            return;
        }
        trajectory.stop();
        trajectory.rewind();
    }

    /**
     * <p>
     * resetPanes</p>
     */
    public void resetPanes() {
        resizePanes(0);
    }

    /**
     * <p>
     * resetShell</p>
     */
    public void resetShell() {
        if (!GraphicsEnvironment.isHeadless()) {
            modelingShell = getModelingShell();
            modelingShell.savePrefs();
            modelingShell.exit();
            modelingShell = new ModelingShell(this);
        }
    }

    /**
     * Set the split panes to their default proportions
     *
     * @param move a int.
     */
    public void resizePanes(int move) {
        if (move == 0) {
            splitPaneDivider = 0;
            mainMenu.setMenuShowing(false);
            mainMenu.toggleToolBarShowing();
            mainMenu.setSystemShowing(false);
            mainMenu.systemClick();
        } else {
            splitPane.setDividerLocation(splitPane.getDividerLocation() + move);
        }
    }

    /**
     * Save the currently selected FFXSystem to disk.
     *
     * @param file File to save the system to.
     * @since 1.0
     */
    public void saveAsXYZ(File file) {
        FFXSystem system = hierarchy.getActive();
        if (system != null && !system.isClosing()) {
            File saveFile = file;
            if (saveFile == null) {
                resetFileChooser();
                fileChooser.setCurrentDirectory(pwd);
                fileChooser.setFileFilter(xyzFileFilter);
                fileChooser.setAcceptAllFileFilterUsed(false);
                int result = fileChooser.showSaveDialog(this);
                if (result == JFileChooser.APPROVE_OPTION) {
                    saveFile = fileChooser.getSelectedFile();
                    pwd = saveFile.getParentFile();
                }
            }
            if (saveFile != null) {
                SystemFilter filter = new XYZFilter(saveFile, system, null, null);
                if (filter.writeFile(saveFile, false)) {
                    // Refresh Panels with the new System name
                    hierarchy.setActive(system);
                }
            }
        }
    }

    /**
     * Save the currently selected FFXSystem to disk.
     *
     * @param file File to save the system to.
     * @since 1.0
     */
    public void saveAsP1(File file) {
        FFXSystem system = hierarchy.getActive();
        if (system != null && !system.isClosing()) {
            File saveFile = file;
            if (saveFile == null) {
                resetFileChooser();
                fileChooser.setCurrentDirectory(pwd);
                fileChooser.setFileFilter(xyzFileFilter);
                fileChooser.setAcceptAllFileFilterUsed(false);
                int result = fileChooser.showSaveDialog(this);
                if (result == JFileChooser.APPROVE_OPTION) {
                    saveFile = fileChooser.getSelectedFile();
                    pwd = saveFile.getParentFile();
                }
            }
            if (saveFile != null) {
                XYZFilter filter = new XYZFilter(saveFile, system, null, null);
                ForceField forceField = system.getForceField();
                final double a = forceField.getDouble(ForceFieldDouble.A_AXIS, 10.0);
                final double b = forceField.getDouble(ForceFieldDouble.B_AXIS, a);
                final double c = forceField.getDouble(ForceFieldDouble.C_AXIS, a);
                final double alpha = forceField.getDouble(ForceFieldDouble.ALPHA, 90.0);
                final double beta = forceField.getDouble(ForceFieldDouble.BETA, 90.0);
                final double gamma = forceField.getDouble(ForceFieldDouble.GAMMA, 90.0);
                final String spacegroup = forceField.getString(ForceFieldString.SPACEGROUP, "P1");
                Crystal crystal = new Crystal(a, b, c, alpha, beta, gamma, spacegroup);
                if (filter.writeFileAsP1(saveFile, false, crystal)) {
                    // Refresh Panels with the new System name
                    hierarchy.setActive(system);
                }
            }
        }
    }

    /**
     * Save the currently selected FFXSystem to a PDB file.
     *
     * @param file File to save the system to.
     * @since 1.0
     */
    public void saveAsPDB(File file) {
        FFXSystem system = hierarchy.getActive();
        if (system == null) {
            logger.log(Level.INFO, " No active system to save.");
            return;
        }
        if (system.isClosing()) {
            logger.log(Level.INFO, " {0} is being closed and can no longer be saved.", system);
            return;
        }
        File saveFile = file;
        if (saveFile == null) {
            resetFileChooser();
            fileChooser.setCurrentDirectory(pwd);
            fileChooser.setFileFilter(pdbFileFilter);
            fileChooser.setAcceptAllFileFilterUsed(false);
            int result = fileChooser.showSaveDialog(this);
            if (result == JFileChooser.APPROVE_OPTION) {
                saveFile = fileChooser.getSelectedFile();
                pwd = saveFile.getParentFile();
            }
        }
        if (saveFile == null) {
            logger.log(Level.INFO, " No filename is defined for {0}.", system);
            return;
        }
        PDBFilter pdbFilter = new PDBFilter(saveFile, system, null, null);
        if (pdbFilter.writeFile(saveFile, false)) {
            // Refresh Panels with the new System name
            hierarchy.setActive(system);
        } else {
            logger.log(Level.INFO, " Save failed for: {0}", system);
        }

    }

    public void savePDBSymMates(File file, String suffix) {
        FFXSystem system = hierarchy.getActive();
        if (system == null) {
            logger.log(Level.INFO, " No active system to save.");
            return;
        }
        if (system.isClosing()) {
            logger.log(Level.INFO, " {0} is being closed and can no longer be saved.", system);
            return;
        }
        File saveFile = file;
        if (saveFile == null) {
            resetFileChooser();
            fileChooser.setCurrentDirectory(pwd);
            fileChooser.setFileFilter(pdbFileFilter);
            fileChooser.setAcceptAllFileFilterUsed(false);
            int result = fileChooser.showSaveDialog(this);
            if (result == JFileChooser.APPROVE_OPTION) {
                saveFile = fileChooser.getSelectedFile();
                pwd = saveFile.getParentFile();
            }
        }
        if (saveFile == null) {
            logger.log(Level.INFO, " No filename is defined for {0}.", system);
            return;
        }
        String filename = FilenameUtils.removeExtension(file.getName());
        PDBFilter pdbFilter = new PDBFilter(saveFile, system, null, null);
        if (pdbFilter.writeFile(saveFile, false)) {
            // Refresh Panels with the new System name
            hierarchy.setActive(system);
        } else {
            logger.log(Level.INFO, " Save failed for: {0}", system);
        }

        Crystal crystal = system.getCrystal();
        int nSymOps = crystal.spaceGroup.getNumberOfSymOps();
        logger.info(String.format(" Writing %d symmetry mates for %s", nSymOps, system.toString()));
        for (int i = 1; i < nSymOps; i++) {
            pdbFilter.setSymOp(i);
            String saveFileName = filename + suffix + "_" + i + ".pdb";
            saveFile = new File(saveFileName);
            for (int j = 1; j < 1000; j++) {
                if (!saveFile.exists()) {
                    break;
                }
                saveFile = new File(saveFileName + "_" + j);
            }

            StringBuilder symSb = new StringBuilder();
            String[] symopLines = crystal.spaceGroup.getSymOp(i).toString().split("\\r?\\n");
            int nLines = symopLines.length;
            symSb.append("REMARK 350\nREMARK 350 SYMMETRY OPERATORS");
            for (int j = 0; j < nLines; j++) {
                symSb.append("\nREMARK 350 ").append(symopLines[j]);
            }

            symopLines = crystal.spaceGroup.getSymOp(i).toXYZString().split("\\r?\\n");
            nLines = symopLines.length;
            symSb.append("\nREMARK 350\nREMARK 350 SYMMETRY OPERATORS XYZ FORM");
            for (int j = 0; j < nLines; j++) {
                symSb.append("\nREMARK 350 ").append(symopLines[j]);
            }

            if (saveFile.exists()) {
                logger.warning(String.format(" Could not successfully version file " + "%s: appending to file %s",
                        saveFileName, saveFile.getName()));
                if (!pdbFilter.writeFileWithHeader(saveFile, symSb, true)) {
                    logger.log(Level.INFO, " Save failed for: {0}", system);
                }
            } else {
                if (!pdbFilter.writeFileWithHeader(saveFile, symSb, false)) {
                    logger.log(Level.INFO, " Save failed for: {0}", system);
                }
            }
        }
    }

    /**
     * <p>
     * saveAsPDB</p>
     *
     * @param activeSystems an array of {@link ffx.potential.MolecularAssembly}
     * objects.
     * @param file a {@link java.io.File} object.
     */
    public void saveAsPDB(MolecularAssembly activeSystems[], File file) {
        File saveFile = file;
        if (saveFile == null) {
            resetFileChooser();
            fileChooser.setCurrentDirectory(pwd);
            fileChooser.setFileFilter(pdbFileFilter);
            fileChooser.setAcceptAllFileFilterUsed(false);
            int result = fileChooser.showSaveDialog(this);
            if (result == JFileChooser.APPROVE_OPTION) {
                saveFile = fileChooser.getSelectedFile();
                pwd = saveFile.getParentFile();
            }
        }
        if (saveFile != null) {
            PDBFilter pdbFilter = new PDBFilter(saveFile, Arrays.asList(activeSystems), null, null);
            pdbFilter.writeFile(saveFile, false);
        }
    }

    static final Preferences preferences = Preferences.userNodeForPackage(MainPanel.class);

    /**
     * Save preferences to the user node
     */
    private void savePrefs() {
        String c = MainPanel.class.getName();
        if (!GraphicsEnvironment.isHeadless()) {
            preferences.putInt(c + ".x", frame.getLocation().x);
            preferences.putInt(c + ".y", frame.getLocation().y);
            preferences.putInt(c + ".width", frame.getWidth());
            preferences.putInt(c + ".height", frame.getHeight());
            preferences.putBoolean(c + ".system", mainMenu.isSystemShowing());
            preferences.putInt(c + ".divider", splitPane.getDividerLocation());
            preferences.putBoolean(c + ".menu", mainMenu.isMenuShowing());
            preferences.putBoolean(c + ".axis", mainMenu.isAxisShowing());
        }
        if (ip == null) {
            ip = new String("");
        }
        if (address != null) {
            String s = address.getHostAddress();
            if (s != null) {
                preferences.put(c + ".ip", s);
            }
            preferences.putInt(c + ".port", socketAddress.getPort());
        }
        preferences.put(c + ".cwd", pwd.toString());

        if (modelingPanel != null) {
            modelingPanel.savePrefs();
        }
        if (keywordPanel != null) {
            keywordPanel.savePrefs();
        }
        if (modelingShell != null) {
            modelingShell.savePrefs();
        }
        if (graphicsCanvas != null) {
            graphicsCanvas.savePrefs();
        }
    }

    /**
     * <p>
     * selectAll</p>
     */
    public void selectAll() {
        if (dataRoot.getChildCount() == 0) {
            return;
        }
        hierarchy.selectAll();
    }

    /**
     * <p>
     * setCWD</p>
     *
     * @param file a {@link java.io.File} object.
     */
    public void setCWD(File file) {
        if ((file == null) || (!file.exists())) {
            return;
        }
        pwd = file;
    }

    /**
     * <p>
     * setPanel</p>
     *
     * @param panel a int.
     */
    public void setPanel(int panel) {
        // tabbedPane.setSelectedIndex(panel);
    }

    /**
     * <p>
     * Setter for the field <code>port</code>.</p>
     */
    public void setPort() {
        String s = new String("" + port);
        s = JOptionPane.showInputDialog("Enter a port number", s);
        if (s == null) {
            return;
        }
        int temp;
        try {
            temp = Integer.parseInt(s);
        } catch (NumberFormatException e) {
            return;
        }
        port = temp;
        socketAddress = new InetSocketAddress(address, port);
    }

    /**
     * <p>
     * setRemoteJobAddress</p>
     */
    public void setRemoteJobAddress() {
        if (address == null) {
            try {
                address = InetAddress.getLocalHost();
            } catch (Exception e) {
                try {
                    address = InetAddress.getByName(null);
                } catch (Exception ex) {
                    return;
                }
            }
        }
        String s = new String("" + address.getHostAddress());
        s = JOptionPane.showInputDialog("Enter an IP Address (XXX.XXX.XXX.XXX)", s);
        if (s == null) {
            return;
        }
        InetAddress newAddress;
        InetSocketAddress newSocketAddress;
        try {
            newAddress = InetAddress.getByName(s);
            newSocketAddress = new InetSocketAddress(newAddress, port);
        } catch (NumberFormatException e) {
            return;
        } catch (Exception e) {
            return;
        }
        address = newAddress;
        socketAddress = newSocketAddress;
    }

    /**
     * <p>
     * showGlobalAxes</p>
     *
     * @param evt a {@link java.awt.event.ActionEvent} object.
     */
    public void showGlobalAxes(ActionEvent evt) {
        JCheckBoxMenuItem showAxesCheckBox = (JCheckBoxMenuItem) evt.getSource();
        graphicsCanvas.setAxisShowing(showAxesCheckBox.isSelected());
    }

    /**
     * <p>
     * showToolBar</p>
     *
     * @param evt a {@link java.awt.event.ActionEvent} object.
     */
    public void showToolBar(ActionEvent evt) {
        JCheckBoxMenuItem toolBarCheckBox = (JCheckBoxMenuItem) evt.getSource();
        if (toolBarCheckBox.isSelected()) {
            add(mainMenu.getToolBar(), BorderLayout.NORTH);
            frame.validate();
        } else {
            remove(mainMenu.getToolBar());
            frame.validate();
        }
    }

    /**
     * <p>
     * showTree</p>
     *
     * @param evt a {@link java.awt.event.ActionEvent} object.
     */
    public void showTree(ActionEvent evt) {
        JCheckBoxMenuItem treeCheckBox = (JCheckBoxMenuItem) evt.getSource();
        if (treeCheckBox.isSelected()) {
            if (splitPaneDivider < frame.getWidth() * (1.0f / 4.0f)) {
                splitPaneDivider = (int) (frame.getWidth() * (1.0f / 4.0f));
            }
            splitPane.setDividerLocation(splitPaneDivider);
        } else {
            splitPaneDivider = splitPane.getDividerLocation();
            splitPane.setDividerLocation(0.0);
        }
    }

    /**
     * <p>
     * skip</p>
     */
    public void skip() {
        Trajectory trajectory = getTrajectory();
        if (trajectory == null) {
            return;
        }
        String skip = new String("" + trajectory.getSkip());
        skip = JOptionPane.showInputDialog("Enter the Number of Frames to Skip", skip);
        try {
            int f = Integer.parseInt(skip);
            trajectory.setSkip(f);
        } catch (NumberFormatException e) {
            return;
        }
    }

    /**
     * <p>
     * speed</p>
     */
    public void speed() {
        Trajectory trajectory = getTrajectory();
        if (trajectory == null) {
            return;
        }
        String rate = new String("" + trajectory.getRate());
        rate = JOptionPane.showInputDialog("Enter the Frame Rate (1-100)", rate);
        try {
            int f = Integer.parseInt(rate);
            trajectory.setRate(f);
        } catch (NumberFormatException e) {
            return;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void stateChanged(ChangeEvent evt) {
        JTabbedPane jtp = (JTabbedPane) evt.getSource();
        int index = jtp.getSelectedIndex();
        if (index == 0) {
            graphicsCanvas.selected();
        } else if (index == 1) {
            keywordPanel.selected();
        } else if (index == 2) {
            modelingPanel.selected();
        }
    }

    /**
     * <p>
     * stepBack</p>
     */
    public void stepBack() {
        Trajectory trajectory = getTrajectory();
        if (trajectory == null) {
            return;
        }
        trajectory.stop();
        trajectory.back();
    }

    /**
     * <p>
     * stepForward</p>
     */
    public void stepForward() {
        Trajectory trajectory = getTrajectory();
        if (trajectory == null) {
            return;
        }
        trajectory.stop();
        trajectory.forward();
    }

    /**
     * <p>
     * stop</p>
     */
    public void stop() {
        Trajectory trajectory = getTrajectory();
        if (trajectory == null) {
            return;
        }
        trajectory.stop();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return "Program Control";
    }

    /**
     * Enumerates the exit status codes FFX may terminate with; 0 will indicate
     * normal execution, 1-99 will indicate fatal errors, 100-199 non-fatal
     * errors, and 200-254 other exit statuses. Presently, only 0, 1, 3, 100,
     * and 200 have been defined for FFX.
     *
     * When adding to this enumeration, avoid the ranges 2, 64-78, 126-128, 130,
     * 137, and 255 or greater (see http://tldp.org/LDP/abs/html/exitcodes.html
     * and the C/C++ standard /usr/include/sysexits.h).
     */
    enum ExitStatus {
        // Normal termination.
        NORMAL(0),
        // Indicates some uncaught Exception, Error, or Throwable. As of now,
        // this enum value is unused, and we rely on the JVM automatically exiting
        // with a system code of 1 under these circumstances.
        EXCEPTION(1),
        // A call to logger.severe() resulted in program termination.
        SEVERE(3),
        // Algorithm did not complete properly (a minimization had a bad
        // interpolation, etc).
        ALGORITHM_FAILURE(100),
        // Some issue not listed here.
        OTHER(200);

        // This gets sent to System.exit().
        private final int exitCode;

        ExitStatus(int exitCode) {
            this.exitCode = exitCode;
        }

        /**
         * Gets the exit code associated with this exit status.
         *
         * @return JVM exit code.
         */
        int getExitCode() {
            return exitCode;
        }
    }
}