View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2021.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.ui;
39  
40  import static ffx.utilities.FileUtils.copyInputStreamToTmpFile;
41  import static java.lang.String.format;
42  import static java.util.Arrays.copyOfRange;
43  
44  import ffx.potential.Utilities.FileType;
45  import ffx.potential.bonded.AminoAcidUtils;
46  import ffx.potential.bonded.AminoAcidUtils.AminoAcid3;
47  import ffx.potential.bonded.NucleicAcidUtils;
48  import ffx.potential.bonded.NucleicAcidUtils.NucleicAcid3;
49  import ffx.potential.bonded.Residue;
50  import ffx.potential.parsers.SystemFilter;
51  import ffx.ui.commands.DTDResolver;
52  import ffx.utilities.Keyword;
53  import java.awt.BorderLayout;
54  import java.awt.Button;
55  import java.awt.Component;
56  import java.awt.Dimension;
57  import java.awt.FlowLayout;
58  import java.awt.Font;
59  import java.awt.GridLayout;
60  import java.awt.Insets;
61  import java.awt.event.ActionEvent;
62  import java.awt.event.ActionListener;
63  import java.awt.event.MouseEvent;
64  import java.awt.event.MouseListener;
65  import java.io.File;
66  import java.io.FileWriter;
67  import java.io.IOException;
68  import java.net.URL;
69  import java.util.ArrayList;
70  import java.util.Arrays;
71  import java.util.Hashtable;
72  import java.util.List;
73  import java.util.logging.Level;
74  import java.util.logging.Logger;
75  import java.util.prefs.Preferences;
76  import javax.swing.BorderFactory;
77  import javax.swing.Box;
78  import javax.swing.BoxLayout;
79  import javax.swing.ButtonGroup;
80  import javax.swing.ImageIcon;
81  import javax.swing.JButton;
82  import javax.swing.JCheckBox;
83  import javax.swing.JCheckBoxMenuItem;
84  import javax.swing.JComboBox;
85  import javax.swing.JLabel;
86  import javax.swing.JOptionPane;
87  import javax.swing.JPanel;
88  import javax.swing.JRadioButton;
89  import javax.swing.JScrollPane;
90  import javax.swing.JSplitPane;
91  import javax.swing.JTabbedPane;
92  import javax.swing.JTextArea;
93  import javax.swing.JTextField;
94  import javax.swing.JToggleButton;
95  import javax.swing.JToolBar;
96  import javax.swing.border.Border;
97  import javax.swing.border.EtchedBorder;
98  import javax.xml.parsers.DocumentBuilder;
99  import javax.xml.parsers.DocumentBuilderFactory;
100 import javax.xml.parsers.ParserConfigurationException;
101 import org.apache.commons.io.FilenameUtils;
102 import org.w3c.dom.Document;
103 import org.w3c.dom.Element;
104 import org.w3c.dom.Node;
105 import org.w3c.dom.NodeList;
106 import org.xml.sax.SAXException;
107 
108 /**
109  * The ModelingPanel class encapsulates functionality needed to run FFX Modeling Commands.
110  *
111  * @author Michael J. Schnieders
112  */
113 public class ModelingPanel extends JPanel implements ActionListener, MouseListener {
114 
115   private static final Logger logger = Logger.getLogger(ModelingPanel.class.getName());
116   private static final Preferences prefs = Preferences.userNodeForPackage(ModelingPanel.class);
117 
118   private final MainPanel mainPanel;
119   /** File Types for this Command */
120   private final ArrayList<FileType> commandFileTypes = new ArrayList<>();
121   /** Executing Commands */
122   private final ArrayList<Thread> executingCommands = new ArrayList<>();
123   /** Log Settings */
124   private final JComboBox<String> logSettings = new JComboBox<>();
125 
126   private final ArrayList<JLabel> conditionals = new ArrayList<>();
127   /** Nucleic Acid and Protein builder components. */
128   private final JComboBox<String> acidComboBox = new JComboBox<>();
129 
130   private final JTextField acidTextField = new JTextField();
131   private final JTextArea acidTextArea = new JTextArea();
132   /** Reused FlowLayout. */
133   private final FlowLayout flowLayout = new FlowLayout(FlowLayout.LEFT, 5, 5);
134   /** Reused BorderLayout. */
135   private final BorderLayout borderLayout = new BorderLayout();
136   /** Reused EtchedBorder. */
137   private final Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
138 
139   private final JTextField sizer = new JTextField(20);
140   /** Active System */
141   private FFXSystem activeSystem = null;
142   /** File Type for the Active System */
143   private FileType activeFileType = null;
144   /** Currently Selected Command */
145   private String activeCommand = null;
146   /** Actions to take for this Command when it finishes */
147   private String commandActions = "NONE";
148   /** Commands for supported file types */
149   private NodeList commandList;
150 
151   private JComboBox<String> xyzCommands;
152   private JComboBox<String> intCommands;
153   private JComboBox<String> arcCommands;
154   private JComboBox<String> pdbCommands;
155   private JComboBox<String> anyCommands;
156   private JComboBox<String> currentCommandBox;
157   /** The CommandPanel holds the toolBar (north), splitPane (center) and statusLabel (south). */
158   private JPanel commandPanel;
159 
160   private JToolBar toolBar;
161   /** The splitPane holds the optionsTabbedPane (top) and descriptScrollPane (bottom). */
162   private JSplitPane splitPane;
163 
164   private JLabel statusLabel;
165   private JTabbedPane optionsTabbedPane;
166   private JScrollPane descriptScrollPane;
167   private JTextArea descriptTextArea;
168   private JCheckBoxMenuItem descriptCheckBox;
169   /** Command input is formed in the commandTextArea, then exported to an input file. */
170   private JTextArea commandTextArea;
171 
172   private JComboBox<String> conformationComboBox;
173   private JScrollPane acidScrollPane = null;
174   private JPanel aminoPanel = null;
175   private JPanel nucleicPanel = null;
176   private JButton jbLaunch;
177   private JButton jbStop;
178   private Thread ffxThread = null;
179 
180   /**
181    * Constructor
182    *
183    * @param mainPanel a {@link ffx.ui.MainPanel} object.
184    */
185   public ModelingPanel(MainPanel mainPanel) {
186     super();
187     this.mainPanel = mainPanel;
188     initialize();
189   }
190 
191   /** {@inheritDoc} */
192   @Override
193   public void actionPerformed(ActionEvent evt) {
194     synchronized (this) {
195       String actionCommand = evt.getActionCommand();
196       // A change to the selected TINKER Command
197       switch (actionCommand) {
198         case "FFXCommand":
199           JComboBox jcb = (JComboBox) toolBar.getComponentAtIndex(2);
200           String com = jcb.getSelectedItem().toString();
201           if (!com.equals(activeCommand)) {
202             activeCommand = com.toLowerCase();
203             loadCommand();
204           }
205           break;
206         case "LogSettings":
207           // A change to the Log Settings.
208           loadLogSettings();
209           statusLabel.setText("  " + createCommandInput());
210           break;
211         case "Launch":
212           // Launch the selected Force Field X command.
213           runScript();
214           break;
215         case "NUCLEIC":
216         case "PROTEIN":
217           // Editor functions for the Protein and Nucleic Acid Builders
218           builderCommandEvent(evt);
219           break;
220         case "Conditional":
221           // Some command options are conditional on other input.
222           conditionalCommandEvent(evt);
223           break;
224         case "End":
225           // End the currently executing command.
226           setEnd();
227           break;
228         case "Delete":
229           // Delete log files.
230           deleteLogs();
231           break;
232         case "Description":
233           // Allow command descriptions to be hidden.
234           JCheckBoxMenuItem box = (JCheckBoxMenuItem) evt.getSource();
235           setDivider(box.isSelected());
236           break;
237         default:
238           logger.log(Level.WARNING, "ModelingPanel ActionCommand not recognized: {0}", evt);
239           break;
240       }
241     }
242   }
243 
244   /**
245    * Launch the TINKER command specified by the ModelingPanel
246    *
247    * @return a {@link ffx.ui.FFXExec} object.
248    */
249   public FFXExec executeCommand() {
250     FFXSystem s = mainPanel.getHierarchy().getActive();
251     String dir = MainPanel.getPWD().getAbsolutePath();
252     if (s != null) {
253       File f = s.getFile();
254       if (f != null) {
255         dir = f.getParent();
256       }
257     }
258     return launch(statusLabel.getText(), dir);
259   }
260 
261   /**
262    * getAvailableCommands
263    *
264    * @return a {@link java.util.ArrayList} object.
265    */
266   public ArrayList<String> getAvailableCommands() {
267     ArrayList<String> availableCommands = new ArrayList<>();
268     for (int i = 0; i < currentCommandBox.getItemCount(); i++) {
269       availableCommands.add(currentCommandBox.getItemAt(i));
270     }
271     return availableCommands;
272   }
273 
274   /** @return a {@link java.lang.String} object. */
275   public String getCommand() {
276     return activeCommand;
277   }
278 
279   /**
280    * Get an ArrayList of executing jobs
281    *
282    * @return an ArrayList containing Thread objects
283    */
284   public ArrayList<Thread> getModelingJobs() {
285     return executingCommands;
286   }
287 
288   /**
289    * Mouse events are used to trigger status bar updates.
290    *
291    * <p>{@inheritDoc}
292    */
293   @Override
294   public void mouseClicked(MouseEvent evt) {
295     statusLabel.setText("  " + createCommandInput());
296   }
297 
298   /** {@inheritDoc} */
299   @Override
300   public void mouseEntered(MouseEvent evt) {
301     mouseClicked(evt);
302   }
303 
304   // *********************************************************************
305   // Modeling Command Configuration
306 
307   /** {@inheritDoc} */
308   @Override
309   public void mouseExited(MouseEvent evt) {
310     mouseClicked(evt);
311   }
312 
313   /** {@inheritDoc} */
314   @Override
315   public void mousePressed(MouseEvent evt) {
316     mouseClicked(evt);
317   }
318 
319   /** {@inheritDoc} */
320   @Override
321   public void mouseReleased(MouseEvent evt) {
322     mouseClicked(evt);
323   }
324 
325   /** Selected. */
326   public void selected() {
327     loadLogSettings();
328     setDivider(descriptCheckBox.isSelected());
329     validate();
330     repaint();
331   }
332 
333   /**
334    * setCommand
335    *
336    * @param command a {@link java.lang.String} object.
337    * @return a boolean.
338    */
339   public boolean setCommand(String command) {
340     if (command == null) {
341       return false;
342     }
343     command = command.toLowerCase();
344     command = command.replaceFirst(command.substring(0, 1), command.toUpperCase().substring(0, 1));
345     currentCommandBox.setSelectedItem(command);
346     mainPanel.setPanel(MainPanel.MODELING);
347     return currentCommandBox.getSelectedItem().equals(command);
348   }
349 
350   /** @param mode a {@link java.lang.String} object. */
351   public void setLogMode(String mode) {
352     mode = mode.toUpperCase();
353     for (int i = 0; i < logSettings.getItemCount(); i++) {
354       String logType = (String) logSettings.getItemAt(i);
355       if (logType.toUpperCase().startsWith(mode)) {
356         logSettings.setSelectedIndex(i);
357         break;
358       }
359     }
360     loadLogSettings();
361   }
362 
363   /**
364    * toString
365    *
366    * @return a {@link java.lang.String} object.
367    */
368   @Override
369   public String toString() {
370     return "Modeling Panel";
371   }
372 
373   /*
374    * This handles Protein and Nucleic Acid builder events.
375    */
376   private void builderCommandEvent(ActionEvent evt) {
377     JButton button = (JButton) evt.getSource();
378     String arg = evt.getActionCommand();
379     int index = acidComboBox.getSelectedIndex();
380     String selected = (String) acidComboBox.getItemAt(index);
381     if ("Remove".equals(button.getText())) {
382       // Remove one entry
383       if (acidComboBox.getItemCount() > 0) {
384         acidComboBox.removeItemAt(index);
385         index--;
386       }
387     } else if ("Edit".equals(button.getText())) {
388       String entry = new String(acidTextField.getText());
389       // Allow editing - should add more input validation here
390       if (!entry.equals("")) {
391         String[] s = entry.trim().split(" +");
392         String newResidue = s[0].toUpperCase();
393         if ("NUCLEIC".equals(arg)) {
394           // Residue.NA3Set.contains(newResidue);
395           try {
396             NucleicAcidUtils.NucleicAcid3.valueOf(newResidue);
397             acidComboBox.removeItemAt(index);
398             acidComboBox.insertItemAt("" + index + " " + entry, index);
399           } catch (Exception e) {
400             //
401           }
402         } else {
403           try {
404             AminoAcidUtils.AminoAcid3.valueOf(newResidue);
405             acidComboBox.removeItemAt(index);
406             acidComboBox.insertItemAt("" + index + " " + entry, index);
407           } catch (Exception e) {
408             //
409           }
410         }
411       }
412     } else if ("Reset".equals(button.getText())) {
413       // Remove all entries
414       acidComboBox.removeAllItems();
415       acidTextArea.setText("");
416     } else {
417       // A base/residue button was selected
418       String newResidue = button.getText();
419       if ("PROTEIN".equals(arg)) {
420         String c = (String) conformationComboBox.getSelectedItem();
421         if (!c.toUpperCase().startsWith("DEFAULT")) {
422           c = c.substring(c.indexOf("[") + 1, c.indexOf("]"));
423           newResidue = newResidue + " " + c;
424         }
425         acidComboBox.insertItemAt("" + index + " " + newResidue, index + 1);
426         index++;
427       } else {
428         if (!newResidue.equalsIgnoreCase("MOL")) {
429           acidComboBox.insertItemAt("" + index + " " + newResidue, index + 1);
430           index++;
431         } else if (!selected.equalsIgnoreCase("MOL")) {
432           acidComboBox.insertItemAt("" + index + " " + newResidue, index + 1);
433           index++;
434         }
435       }
436     }
437     // Create the condensed sequence view.
438     StringBuilder sequence = new StringBuilder();
439     for (int i = 0; i < acidComboBox.getItemCount(); i++) {
440       String[] s = (acidComboBox.getItemAt(i)).trim().toUpperCase().split(" +");
441       if (s.length > 1) {
442         if (s[1].equalsIgnoreCase("MOL")) {
443           sequence.append(s[1]).append("\n");
444         } else {
445           sequence.append(s[1]).append(" ");
446         }
447       }
448     }
449     // Renumber the sequence.
450     acidTextArea.setText(sequence.toString());
451     for (int i = 0; i < acidComboBox.getItemCount(); i++) {
452       String s = (String) acidComboBox.getItemAt(i);
453       s = s.substring(s.indexOf(" ")).trim();
454       acidComboBox.removeItemAt(i);
455       acidComboBox.insertItemAt("" + (i + 1) + " " + s, i);
456     }
457     // Set the selected entry and fill in the edit textField.
458     if (index < 0) {
459       index = 0;
460     }
461     if (index > acidComboBox.getItemCount() - 1) {
462       index = acidComboBox.getItemCount() - 1;
463     }
464     acidComboBox.setSelectedIndex(index);
465     String s = (String) acidComboBox.getItemAt(index);
466     if (s != null) {
467       acidTextField.setText(s.substring(s.indexOf(" ")).trim());
468     } else {
469       acidTextField.setText("");
470     }
471   }
472 
473   /**
474    * This handles conditional command option input.
475    *
476    * @param evt ActionEvent
477    */
478   private void conditionalCommandEvent(ActionEvent evt) {
479     Object source = evt.getSource();
480     if (source instanceof JRadioButton) {
481       JRadioButton jrb = (JRadioButton) source;
482       String selection = jrb.getText().toLowerCase();
483       for (JLabel label : conditionals) {
484         JTextField jtf = (JTextField) label.getLabelFor();
485         String cupon = label.getName().toLowerCase();
486         if (cupon.contains(selection) && jrb.isSelected()) {
487           label.setEnabled(true);
488           jtf.setEnabled(true);
489         } else {
490           label.setEnabled(false);
491           jtf.setEnabled(false);
492         }
493       }
494     } else if (source instanceof JCheckBox) {
495       JCheckBox jcb = (JCheckBox) source;
496       String selection = jcb.getText().toLowerCase();
497       for (JLabel label : conditionals) {
498         String cupon = label.getName().toLowerCase();
499         JTextField jtf = (JTextField) label.getLabelFor();
500         if (cupon.contains(selection) && jcb.isSelected()) {
501           label.setEnabled(true);
502           jtf.setEnabled(true);
503         } else {
504           label.setEnabled(false);
505           jtf.setEnabled(false);
506         }
507       }
508     }
509     statusLabel.setText("  " + createCommandInput());
510   }
511 
512   /**
513    * Create a string representing the modeling command to execute.
514    *
515    * @return the modeling command string.
516    */
517   private String createCommandInput() {
518     StringBuilder commandLineParams = new StringBuilder(activeCommand + " ");
519     // Now append command line input to a TextArea, one option per line.
520     // This TextArea gets dumped to an input file.
521     commandTextArea.setText("");
522     int numparams = optionsTabbedPane.getTabCount();
523     for (int i = 0; i < numparams; i++) {
524       // A few cases require that a newLine not be generated between
525       // options.
526       boolean newLine = true;
527       // The optionString will collect the parameters for this Option,
528       // then append them to the CommandTextArea.
529       StringBuilder optionString = new StringBuilder();
530       JPanel optionPanel = (JPanel) optionsTabbedPane.getComponentAt(i);
531       int numOptions = optionPanel.getComponentCount();
532       String title = optionsTabbedPane.getTitleAt(i);
533       if (title.equalsIgnoreCase("Sequence")) {
534         for (int k = 0; k < acidComboBox.getItemCount(); k++) {
535           if (k != 0) {
536             optionString.append("\n");
537           }
538           String s = acidComboBox.getItemAt(k);
539           s = s.substring(s.indexOf(" ")).trim();
540           optionString.append(s);
541         }
542         // Need an extra newline for Nucleic
543         if (activeCommand.equalsIgnoreCase("NUCLEIC")) {
544           optionString.append("\n");
545         }
546       } else {
547         JPanel valuePanel = (JPanel) optionPanel.getComponent(numOptions - 1);
548         int numValues = valuePanel.getComponentCount();
549         for (int j = 0; j < numValues; j++) {
550           Component value = valuePanel.getComponent(j);
551           if (value instanceof JCheckBox) {
552             JCheckBox jcbox = (JCheckBox) value;
553             if (jcbox.isSelected()) {
554               optionString.append("-");
555               optionString.append(jcbox.getName());
556               optionString.append(" ");
557               optionString.append(jcbox.getText());
558             }
559           } else if (value instanceof JTextField) {
560             JTextField jtfield = (JTextField) value;
561             optionString.append("-");
562             optionString.append(jtfield.getName());
563             optionString.append(" ");
564             optionString.append(jtfield.getText());
565           } else if (value instanceof JComboBox) {
566             JComboBox jcb = (JComboBox) value;
567             Object object = jcb.getSelectedItem();
568             if (object instanceof FFXSystem) {
569               FFXSystem system = (FFXSystem) object;
570               File file = system.getFile();
571               if (file != null) {
572                 String absolutePath = file.getAbsolutePath();
573                 if (absolutePath.endsWith("xyz")) {
574                   absolutePath = absolutePath + "_1";
575                 }
576                 optionString.append(absolutePath);
577               }
578             }
579           } else if (value instanceof JRadioButton) {
580             JRadioButton jrbutton = (JRadioButton) value;
581             if (jrbutton.isSelected()) {
582               if (!jrbutton.getText().equalsIgnoreCase("NONE")) {
583                 optionString.append("-");
584                 optionString.append(jrbutton.getName());
585                 optionString.append(" ");
586                 optionString.append(jrbutton.getText());
587               }
588               if (title.equalsIgnoreCase("C-CAP")) {
589                 optionString.append("\n");
590               }
591             }
592           }
593         }
594         // Handle Conditional Options
595         if (optionPanel.getComponentCount() == 3) {
596           valuePanel = (JPanel) optionPanel.getComponent(1);
597           // JLabel conditionalLabel = (JLabel)
598           // valuePanel.getComponent(0);
599           JTextField jtf = (JTextField) valuePanel.getComponent(1);
600           if (jtf.isEnabled()) {
601             String conditionalInput = jtf.getText();
602             // Post-Process the Input into Atom Pairs
603             String postProcess = jtf.getName();
604             if (postProcess != null && postProcess.equalsIgnoreCase("ATOMPAIRS")) {
605               String[] tokens = conditionalInput.split(" +");
606               StringBuilder atomPairs = new StringBuilder();
607               int atomNumber = 0;
608               for (String token : tokens) {
609                 atomPairs.append(token);
610                 if (atomNumber++ % 2 == 0) {
611                   atomPairs.append(" ");
612                 } else {
613                   atomPairs.append("\n");
614                 }
615               }
616               conditionalInput = atomPairs.toString();
617             }
618             // Append a newline to "enter" the option string.
619             // Append "conditional" input.
620             optionString.append("\n").append(conditionalInput);
621           }
622         }
623       }
624       if (optionString.length() > 0) {
625         commandTextArea.append(optionString.toString());
626         commandTextArea.append("\n");
627       }
628     }
629     String commandInput = commandTextArea.getText();
630     if (commandInput != null && !commandInput.trim().equalsIgnoreCase("")) {
631       commandLineParams.append(commandInput);
632     }
633 
634     // The final token on the command line is the structure file name, except
635     // for protein and nucleic.
636     if (!activeCommand.equalsIgnoreCase("Protein") && !activeCommand.equalsIgnoreCase("Nucleic")) {
637       File file = activeSystem.getFile();
638       if (file != null) {
639         String name = file.getName();
640         commandLineParams.append(name);
641         commandLineParams.append(" ");
642       } else {
643         return null;
644       }
645     }
646 
647     return commandLineParams.toString();
648   }
649 
650   private JPanel getAminoAcidPanel() {
651     if (aminoPanel != null) {
652       return aminoPanel;
653     }
654     JPanel buttonPanel = new JPanel(new GridLayout(4, 5, 2, 2));
655     AminoAcid3[] a = AminoAcidUtils.AminoAcid3.values();
656     for (int i = 0; i < 20; i++) {
657       JButton button = new JButton(a[i].name());
658       button.setActionCommand("PROTEIN");
659       button.addActionListener(this);
660       buttonPanel.add(button);
661     }
662     buttonPanel.setMaximumSize(buttonPanel.getPreferredSize());
663     aminoPanel = new JPanel();
664     aminoPanel.setLayout(new BoxLayout(aminoPanel, BoxLayout.Y_AXIS));
665     aminoPanel.add(buttonPanel);
666     conformationComboBox = new JComboBox<>(Residue.Ramachandran);
667     conformationComboBox.setFont(Font.decode("Monospaced"));
668     conformationComboBox.setMaximumSize(buttonPanel.getPreferredSize());
669     aminoPanel.add(conformationComboBox);
670     return aminoPanel;
671   }
672 
673   private String getLogString(File currentLog) {
674     String currentMode = (String) logSettings.getSelectedItem();
675     if (currentMode.startsWith("Create")) {
676       currentLog = SystemFilter.version(currentLog);
677       return " > \"" + currentLog.getAbsolutePath() + "\"";
678     } else if (currentMode.startsWith("Append")) {
679       return " >> \"" + currentLog.getAbsolutePath() + "\"";
680     } else {
681       return " > \"" + currentLog.getAbsolutePath() + "\"";
682     }
683   }
684 
685   private JPanel getNucleicAcidPanel() {
686     if (nucleicPanel != null) {
687       return nucleicPanel;
688     }
689     nucleicPanel = new JPanel(new GridLayout(3, 4, 2, 2));
690     NucleicAcid3[] a = NucleicAcidUtils.NucleicAcid3.values();
691     for (int i = 0; i < 8; i++) {
692       JButton button = new JButton(a[i].name());
693       button.setActionCommand("NUCLEIC");
694       button.addActionListener(this);
695       nucleicPanel.add(button);
696     }
697     JButton button = new JButton("MOL");
698     button.setActionCommand("NUCLEIC");
699     button.addActionListener(this);
700     nucleicPanel.add(button);
701     nucleicPanel.add(Box.createHorizontalBox());
702     nucleicPanel.add(Box.createHorizontalBox());
703     nucleicPanel.add(Box.createHorizontalBox());
704     nucleicPanel.setMaximumSize(nucleicPanel.getPreferredSize());
705     return nucleicPanel;
706   }
707 
708   private void initCommandComboBox(JComboBox commands) {
709     commands.setActionCommand("FFXCommand");
710     commands.setMaximumSize(xyzCommands.getPreferredSize());
711     commands.setEditable(false);
712     commands.setToolTipText("Select a Modeling Command");
713     commands.setSelectedIndex(0);
714     commands.addActionListener(this);
715   }
716 
717   private void initialize() {
718     // Command Description
719     descriptTextArea = new JTextArea();
720     descriptTextArea.setEditable(false);
721     descriptTextArea.setLineWrap(true);
722     descriptTextArea.setWrapStyleWord(true);
723     descriptTextArea.setDoubleBuffered(true);
724     Insets insets = descriptTextArea.getInsets();
725     insets.set(5, 5, 5, 5);
726     descriptTextArea.setMargin(insets);
727     descriptScrollPane =
728         new JScrollPane(
729             descriptTextArea,
730             JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
731             JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
732     descriptScrollPane.setBorder(etchedBorder);
733     // Command Input
734     commandTextArea = new JTextArea();
735     commandTextArea.setEditable(false);
736     commandTextArea.setLineWrap(true);
737     commandTextArea.setWrapStyleWord(true);
738     commandTextArea.setDoubleBuffered(true);
739     commandTextArea.setMargin(insets);
740     // Command Options
741     optionsTabbedPane = new JTabbedPane();
742     statusLabel = new JLabel();
743     statusLabel.setBorder(etchedBorder);
744     statusLabel.setToolTipText("  Modeling command that will be executed");
745     commandPanel = new JPanel(flowLayout);
746     commandPanel.add(optionsTabbedPane);
747     splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, commandPanel, descriptScrollPane);
748     splitPane.setContinuousLayout(true);
749     splitPane.setResizeWeight(1.0d);
750     splitPane.setOneTouchExpandable(true);
751     setLayout(new BorderLayout());
752     add(splitPane, BorderLayout.CENTER);
753     add(statusLabel, BorderLayout.SOUTH);
754     // Initialize the Amino/Nucleic Acid ComboBox.
755     acidComboBox.setEditable(false);
756     acidComboBox.setMaximumSize(sizer.getPreferredSize());
757     acidComboBox.setPreferredSize(sizer.getPreferredSize());
758     acidComboBox.setMinimumSize(sizer.getPreferredSize());
759     acidComboBox.setFont(Font.decode("Monospaced"));
760     acidTextField.setMaximumSize(sizer.getPreferredSize());
761     acidTextField.setMinimumSize(sizer.getPreferredSize());
762     acidTextField.setPreferredSize(sizer.getPreferredSize());
763     acidTextArea.setEditable(false);
764     acidTextArea.setWrapStyleWord(true);
765     acidTextArea.setLineWrap(true);
766     acidTextArea.setFont(Font.decode("Monospaced"));
767     acidScrollPane = new JScrollPane(acidTextArea);
768     Dimension d = new Dimension(300, 400);
769     acidScrollPane.setPreferredSize(d);
770     acidScrollPane.setMaximumSize(d);
771     acidScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
772     // Load the FFX commands.xml file that defines FFX commands.
773     try {
774       String provider = "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl";
775       DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(provider, null);
776       DocumentBuilder db = dbf.newDocumentBuilder();
777       db.setEntityResolver(new DTDResolver());
778       URL comURL = getClass().getClassLoader().getResource("ffx/ui/commands/commands.xml");
779       Document doc = db.parse(comURL.openStream());
780       NodeList nodelist = doc.getChildNodes();
781       Node commandroot = null;
782       for (int i = 0; i < nodelist.getLength(); i++) {
783         commandroot = nodelist.item(i);
784         if (commandroot.getNodeName().equals("FFXCommands") && commandroot instanceof Element) {
785           break;
786         }
787       }
788       if (!(commandroot instanceof Element)) {
789         commandList = null;
790       }
791       commandList = ((Element) commandroot).getElementsByTagName("Command");
792     } catch (ParserConfigurationException | SAXException | IOException e) {
793       System.err.println(e);
794     } finally {
795       if (commandList == null) {
796         System.out.println("Force Field X commands.xml could not be parsed.");
797         logger.severe("Force Field X will exit.");
798         System.exit(-1);
799       }
800     }
801     // Create a ComboBox with commands specific to each type of coordinate
802     // file.
803     xyzCommands = new JComboBox<>();
804     intCommands = new JComboBox<>();
805     arcCommands = new JComboBox<>();
806     pdbCommands = new JComboBox<>();
807     anyCommands = new JComboBox<>();
808     Element command;
809     String name;
810     int numcommands = commandList.getLength();
811     for (int i = 0; i < numcommands; i++) {
812       command = (Element) commandList.item(i);
813       name = command.getAttribute("name");
814       String temp = command.getAttribute("fileType");
815       if (temp.contains("ANY")) {
816         temp = "XYZ INT ARC PDB";
817         anyCommands.addItem(name);
818       }
819       String[] types = temp.split(" +");
820       for (String type : types) {
821         if (type.contains("XYZ")) {
822           xyzCommands.addItem(name);
823         }
824         if (type.contains("INT")) {
825           intCommands.addItem(name);
826         }
827         if (type.contains("ARC")) {
828           arcCommands.addItem(name);
829         }
830         if (type.contains("PDB")) {
831           pdbCommands.addItem(name);
832         }
833       }
834     }
835     initCommandComboBox(xyzCommands);
836     initCommandComboBox(intCommands);
837     initCommandComboBox(arcCommands);
838     initCommandComboBox(pdbCommands);
839     initCommandComboBox(anyCommands);
840     currentCommandBox = anyCommands;
841     activeCommand = (String) anyCommands.getSelectedItem();
842     // Load the default Command.
843     loadCommand();
844     // Load the default Log File Settings.
845     logSettings.setActionCommand("LogSettings");
846     loadLogSettings();
847 
848     // Create the Toolbar.
849     toolBar = new JToolBar("Modeling Commands", JToolBar.HORIZONTAL);
850     toolBar.setLayout(new FlowLayout(FlowLayout.LEFT));
851     jbLaunch =
852         new JButton(
853             new ImageIcon(getClass().getClassLoader().getResource("ffx/ui/icons/cog_go.png")));
854     jbLaunch.setActionCommand("Launch");
855     jbLaunch.setToolTipText("Launch the Force Field X Command");
856     jbLaunch.addActionListener(this);
857     insets.set(2, 2, 2, 2);
858     jbLaunch.setMargin(insets);
859     toolBar.add(jbLaunch);
860     jbStop =
861         new JButton(
862             new ImageIcon(getClass().getClassLoader().getResource("ffx/ui/icons/stop.png")));
863     jbStop.setActionCommand("End");
864     jbStop.setToolTipText("Terminate the Current Force Field X Command");
865     jbStop.addActionListener(this);
866     jbStop.setMargin(insets);
867     jbStop.setEnabled(false);
868     toolBar.add(jbStop);
869     toolBar.addSeparator();
870     toolBar.add(anyCommands);
871     currentCommandBox = anyCommands;
872     toolBar.addSeparator();
873     /*
874     toolBar.add(logSettings);
875     JButton jbdelete = new JButton(new ImageIcon(getClass().getClassLoader().getResource("ffx/ui/icons/page_delete.png")));
876     jbdelete.setActionCommand("Delete");
877     jbdelete.setToolTipText("Delete Log Files");
878     jbdelete.addActionListener(this);
879     jbdelete.setMargin(insets);
880     toolBar.add(jbdelete);
881     toolBar.addSeparator();
882     */
883     ImageIcon icinfo =
884         new ImageIcon(getClass().getClassLoader().getResource("ffx/ui/icons/information.png"));
885     descriptCheckBox = new JCheckBoxMenuItem(icinfo);
886     descriptCheckBox.addActionListener(this);
887     descriptCheckBox.setActionCommand("Description");
888     descriptCheckBox.setToolTipText("Show/Hide Modeling Command Descriptions");
889     descriptCheckBox.setMargin(insets);
890     toolBar.add(descriptCheckBox);
891     toolBar.add(new JLabel(""));
892     toolBar.setBorderPainted(false);
893     toolBar.setFloatable(false);
894     toolBar.setRollover(true);
895     add(toolBar, BorderLayout.NORTH);
896     // Load ModelingPanel preferences.
897     Preferences prefs = Preferences.userNodeForPackage(ffx.ui.ModelingPanel.class);
898     descriptCheckBox.setSelected(!prefs.getBoolean("JobPanel_description", true));
899     descriptCheckBox.doClick();
900   }
901 
902   private void runScript() {
903     String name = activeCommand;
904     name = name.replace('.', File.separatorChar);
905     ClassLoader loader = getClass().getClassLoader();
906     URL embeddedScript = loader.getResource("ffx/scripts/" + name + ".ffx");
907     if (embeddedScript == null) {
908       embeddedScript = loader.getResource("ffx/scripts/" + name + ".groovy");
909     }
910     File scriptFile = null;
911     if (embeddedScript != null) {
912       try {
913         scriptFile =
914             new File(copyInputStreamToTmpFile(embeddedScript.openStream(), "ffx", name, "groovy"));
915       } catch (IOException e) {
916         logger.log(
917             Level.WARNING,
918             "Exception extracting embedded script {0}\n{1}",
919             new Object[] {embeddedScript.toString(), e.toString()});
920       }
921     }
922     if (scriptFile != null && scriptFile.exists()) {
923       String argLine = statusLabel.getText().replace('\n', ' ');
924       String[] args = argLine.trim().split(" +");
925       // Remove the command (first token) and system name (last token).
926       args = copyOfRange(args, 1, args.length - 1);
927       List<String> argList = Arrays.asList(args);
928       FFXLauncher ffxLauncher = new FFXLauncher(argList, scriptFile);
929       ffxThread = new Thread(ffxLauncher);
930       ffxThread.setName(statusLabel.getText());
931       ffxThread.setPriority(Thread.MAX_PRIORITY);
932       ffxThread.start();
933     } else {
934       logger.warning(format("%s was not found.", name));
935     }
936   }
937 
938   void enableLaunch(boolean enable) {
939     jbLaunch.setEnabled(enable);
940     jbStop.setEnabled(!enable);
941   }
942 
943   /**
944    * Launch the active command on the active system in the specified directory.
945    *
946    * @param command The command to be excuted.
947    * @param dir The directory to execute the command in.
948    * @return a {@link ffx.ui.FFXExec} object.
949    */
950   private FFXExec launch(String command, String dir) {
951     logger.log(Level.INFO, "Command: {0}\nDirectory: {1}", new Object[] {command, dir});
952     synchronized (this) {
953       // Check that the TINKER *.exe exists in TINKER/bin
954       String path = MainPanel.ffxDir.getAbsolutePath();
955       File exe = new File(path + File.separator + activeCommand.toLowerCase());
956       if (!exe.exists()) {
957         exe = new File(exe.getAbsolutePath() + ".exe");
958         if (!exe.exists()) {
959           String message =
960               "The "
961                   + activeCommand
962                   + " executable was not found in "
963                   + path
964                   + ". Please use the 'Set TINKER...' dialog to change the TINKER directory.";
965           JOptionPane.showMessageDialog(
966               null, message, "Could not launch " + activeCommand, JOptionPane.ERROR_MESSAGE);
967           return null;
968         }
969       }
970       // Check that the directory to execute the command in is valid
971       File dirf = new File(dir);
972       if (!dirf.exists()) {
973         logger.log(Level.WARNING, "Directory doesn''t exist: {0}", dirf.getAbsolutePath());
974         return null;
975       }
976       // Check if we need a key file
977       if (!commandFileTypes.contains(FileType.ANY)) {
978         if (activeSystem == null) {
979           return null;
980         }
981         activeFileType = FileType.XYZ;
982         // Check that the TINKER command executes on this file type
983         if (!commandFileTypes.contains(activeFileType)) {
984           String message =
985               activeCommand.toUpperCase() + " does not execute on " + activeFileType + " files.";
986           JOptionPane.showMessageDialog(
987               null, message, "Could not launch " + activeCommand, JOptionPane.ERROR_MESSAGE);
988           return null;
989         }
990         // Check that a key file exists or prompt to create one.
991         if (activeSystem.getKeyFile() == null) {
992           mainPanel.createKeyFile(activeSystem);
993           // Give up if the key file is null.
994           if (activeSystem.getKeyFile() == null) {
995             return null;
996           }
997         }
998       } else {
999         // Determine names to use for the output of Protein/Nucleic
1000         command = createCommandInput();
1001         String structureName = commandTextArea.getText().trim();
1002         if (!structureName.equalsIgnoreCase("")) {
1003           structureName = (structureName.split("\n"))[0];
1004           if (structureName != null) {
1005             structureName = structureName.trim();
1006             int dot = structureName.lastIndexOf(".");
1007             if (dot > 0) {
1008               structureName = structureName.substring(0, dot);
1009             }
1010           }
1011         }
1012         // If the above fails, just use the name of the executable
1013         // (protein or nulceic)
1014         if (structureName == null) {
1015           structureName = activeCommand.toLowerCase();
1016         }
1017         File file = new File(dir + File.separator + structureName + ".xyz");
1018         file = SystemFilter.version(file);
1019         activeSystem = new FFXSystem(file, null, Keyword.loadProperties(file));
1020         File logFile = new File(file.getParent() + File.separator + structureName + ".log");
1021         activeSystem.setLogFile(logFile);
1022         loadLogSettings();
1023         activeFileType = FileType.ANY;
1024         // Need to have a parameter file chosen.
1025         mainPanel.openKey(activeSystem, true);
1026         if (activeSystem.getKeyFile() == null) {
1027           return null;
1028         }
1029       }
1030       // Decide on a Log file
1031       if (((String) logSettings.getSelectedItem()).startsWith("Create")) {
1032         File newLog = SystemFilter.version(activeSystem.getLogFile());
1033         activeSystem.setLogFile(newLog);
1034       }
1035       String logName = activeSystem.getLogFile().getAbsolutePath();
1036       // Determine the command string
1037       command = createCommandInput();
1038       // If a new structure file will be created, determine what the name
1039       // will be.
1040       File newFile = null;
1041       if (commandActions.toUpperCase().contains("LOAD")) {
1042         File oldFile = activeSystem.getFile();
1043         if (commandActions.toUpperCase().contains("LOADXYZ")) {
1044           String fileName = oldFile.getAbsolutePath();
1045           int dot = fileName.lastIndexOf(".");
1046           if (dot > 0) {
1047             fileName = fileName.substring(0, dot) + ".xyz";
1048           }
1049           oldFile = new File(fileName);
1050         } else if (commandActions.toUpperCase().contains("LOADINT")) {
1051           String fileName = oldFile.getAbsolutePath();
1052           int dot = fileName.lastIndexOf(".");
1053           if (dot > 0) {
1054             fileName = fileName.substring(0, dot) + ".int";
1055           }
1056           oldFile = new File(fileName);
1057         } else if (commandActions.toUpperCase().contains("LOADPDB")) {
1058           String fileName = oldFile.getAbsolutePath();
1059           int dot = fileName.lastIndexOf(".");
1060           if (dot > 0) {
1061             fileName = fileName.substring(0, dot) + ".pdb";
1062           }
1063           oldFile = new File(fileName);
1064         }
1065         newFile = SystemFilter.version(oldFile);
1066       }
1067       // Save any changes that have been made to the key file
1068       mainPanel.getKeywordPanel().saveChanges();
1069       // Remove any TINKER *.END files
1070       removeEnd();
1071       // Create the input file
1072       String commandInput = commandTextArea.getText();
1073       if (commandInput != null && !commandInput.trim().equalsIgnoreCase("")) {
1074         File inputFile = new File(dir + File.separator + activeCommand.toLowerCase() + ".in");
1075         inputFile.deleteOnExit();
1076         try {
1077           FileWriter fw = new FileWriter(inputFile);
1078           fw.write(commandInput);
1079           fw.close();
1080         } catch (Exception e) {
1081           logger.info(e.toString());
1082           return null;
1083         }
1084       }
1085       // If the job progressively modifies coordinates, open a copy of it.
1086       boolean openOnto = false;
1087       if (commandActions.toUpperCase().contains("CONNECT")) {
1088         // If a version file is created, open it onto the structure used
1089         // to display the job.
1090         if (newFile != null) {
1091           openOnto = true;
1092         }
1093         mainPanel.open(activeSystem.getFile(), activeCommand);
1094         try {
1095           while (mainPanel.isOpening()) {
1096             wait(10);
1097           }
1098         } catch (Exception e) {
1099           logger.info(e.toString());
1100           return null;
1101         }
1102         activeSystem = mainPanel.getHierarchy().getActive();
1103       }
1104       // Finally, create and execute the command in a new thread.
1105       FFXExec tinkerExec =
1106           new FFXExec(activeSystem, logName, command, dir, mainPanel, newFile, openOnto);
1107       Thread tinkerThread = new Thread(tinkerExec);
1108       tinkerThread.setPriority(Thread.NORM_PRIORITY);
1109       tinkerThread.setName(logName);
1110       // If the job progressively modifies coordinates, connect to it.
1111       if (commandActions.toUpperCase().contains("CONNECT")) {
1112         mainPanel.connectToTINKER(activeSystem, tinkerThread);
1113       } else {
1114         tinkerThread.start();
1115       }
1116       // If some action should be taken when the job finishes,
1117       // add it to the Modeling Jobs Vector
1118       if (!commandActions.equalsIgnoreCase("NONE")) {
1119         executingCommands.add(tinkerThread);
1120         // mainPanel.getLogPanel().refreshStatus();
1121       }
1122       return tinkerExec;
1123     }
1124   }
1125 
1126   /**
1127    * Load the active system into the JobPanel. This should be called whenever the active system
1128    * changes.
1129    *
1130    * @param active a {@link ffx.ui.FFXSystem} object.
1131    */
1132   void loadActive(FFXSystem active) {
1133     synchronized (this) {
1134       activeSystem = active;
1135       FileType fileType;
1136       // No Open Molecules
1137       if (activeSystem == null || activeSystem.isClosing()) {
1138         currentCommandBox = anyCommands;
1139         activeCommand = (String) anyCommands.getSelectedItem();
1140         statusLabel.setText("  ");
1141         fileType = FileType.ANY;
1142       } else {
1143         fileType = FileType.XYZ;
1144       }
1145       if (fileType != activeFileType) {
1146         activeFileType = fileType;
1147         toolBar.remove(2);
1148         if (activeFileType == FileType.XYZ) {
1149           toolBar.add(xyzCommands);
1150           currentCommandBox = xyzCommands;
1151         } else if (activeFileType == FileType.INT) {
1152           toolBar.add(intCommands);
1153           currentCommandBox = intCommands;
1154         } else if (activeFileType == FileType.ARC) {
1155           toolBar.add(arcCommands);
1156           currentCommandBox = arcCommands;
1157         } else if (activeFileType == FileType.PDB) {
1158           toolBar.add(pdbCommands);
1159           currentCommandBox = pdbCommands;
1160         } else {
1161           toolBar.add(anyCommands);
1162           currentCommandBox = anyCommands;
1163         }
1164         toolBar.add(currentCommandBox, 2);
1165         toolBar.validate();
1166         toolBar.repaint();
1167         activeCommand = (String) currentCommandBox.getSelectedItem();
1168       }
1169       loadCommand();
1170     }
1171   }
1172 
1173   private void loadCommand() {
1174     synchronized (this) {
1175       // Force Field X Command
1176       Element command;
1177       // Command Options
1178       NodeList options;
1179       Element option;
1180       // Option Values
1181       NodeList values;
1182       Element value;
1183       // Options may be Conditional based on previous Option values (not
1184       // always supplied)
1185       NodeList conditionalList;
1186       Element conditional;
1187       // JobPanel GUI Components that change based on command
1188       JPanel optionPanel;
1189       // Clear the previous components
1190       commandPanel.removeAll();
1191       optionsTabbedPane.removeAll();
1192       conditionals.clear();
1193       String currentCommand = (String) currentCommandBox.getSelectedItem();
1194       if (currentCommand == null) {
1195         commandPanel.validate();
1196         commandPanel.repaint();
1197         return;
1198       }
1199       command = null;
1200       for (int i = 0; i < commandList.getLength(); i++) {
1201         command = (Element) commandList.item(i);
1202         String name = command.getAttribute("name");
1203         if (name.equalsIgnoreCase(currentCommand)) {
1204           break;
1205         }
1206       }
1207       int div = splitPane.getDividerLocation();
1208       descriptTextArea.setText(
1209           currentCommand.toUpperCase() + ": " + command.getAttribute("description"));
1210       splitPane.setBottomComponent(descriptScrollPane);
1211       splitPane.setDividerLocation(div);
1212       // The "action" tells Force Field X what to do when the
1213       // command finishes
1214       commandActions = command.getAttribute("action").trim();
1215       // The "fileType" specifies what file types this command can execute
1216       // on
1217       String string = command.getAttribute("fileType").trim();
1218       String[] types = string.split(" +");
1219       commandFileTypes.clear();
1220       for (String type : types) {
1221         if (type.contains("XYZ")) {
1222           commandFileTypes.add(FileType.XYZ);
1223         }
1224         if (type.contains("INT")) {
1225           commandFileTypes.add(FileType.INT);
1226         }
1227         if (type.contains("ARC")) {
1228           commandFileTypes.add(FileType.ARC);
1229         }
1230         if (type.contains("PDB")) {
1231           commandFileTypes.add(FileType.PDB);
1232         }
1233         if (type.contains("ANY")) {
1234           commandFileTypes.add(FileType.ANY);
1235         }
1236       }
1237       // Determine what options are available for this command
1238       options = command.getElementsByTagName("Option");
1239       int length = options.getLength();
1240       for (int i = 0; i < length; i++) {
1241         // This Option will be enabled (isEnabled = true) unless a
1242         // Conditional disables it
1243         boolean isEnabled = true;
1244         option = (Element) options.item(i);
1245         conditionalList = option.getElementsByTagName("Conditional");
1246         /*
1247          * Currently, there can only be 0 or 1 Conditionals per Option
1248          * There are three types of Conditionals implemented. 1.)
1249          * Conditional on a previous Option, this option may be
1250          * available 2.) Conditional on input for this option, a
1251          * sub-option may be available 3.) Conditional on the presence
1252          * of keywords, this option may be available
1253          */
1254         if (conditionalList != null) {
1255           conditional = (Element) conditionalList.item(0);
1256         } else {
1257           conditional = null;
1258         }
1259         // Get the command line flag
1260         String flag = option.getAttribute("flag").trim();
1261         // Get the description
1262         String optionDescript = option.getAttribute("description");
1263         JTextArea optionTextArea = new JTextArea("  " + optionDescript.trim());
1264         optionTextArea.setEditable(false);
1265         optionTextArea.setLineWrap(true);
1266         optionTextArea.setWrapStyleWord(true);
1267         optionTextArea.setBorder(etchedBorder);
1268         // Get the default for this Option (if one exists)
1269         String defaultOption = option.getAttribute("default");
1270         // Option Panel
1271         optionPanel = new JPanel(new BorderLayout());
1272         optionPanel.add(optionTextArea, BorderLayout.NORTH);
1273         String swing = option.getAttribute("gui");
1274         JPanel optionValuesPanel = new JPanel(new FlowLayout());
1275         optionValuesPanel.setBorder(etchedBorder);
1276         ButtonGroup bg = null;
1277         if (swing.equalsIgnoreCase("CHECKBOXES")) {
1278           // CHECKBOXES allows selection of 1 or more values from a
1279           // predefined set (Analyze, for example)
1280           values = option.getElementsByTagName("Value");
1281           for (int j = 0; j < values.getLength(); j++) {
1282             value = (Element) values.item(j);
1283             JCheckBox jcb = new JCheckBox(value.getAttribute("name"));
1284             jcb.addMouseListener(this);
1285             jcb.setName(flag);
1286             if (defaultOption != null && jcb.getActionCommand().equalsIgnoreCase(defaultOption)) {
1287               jcb.setSelected(true);
1288             }
1289             optionValuesPanel.add(jcb);
1290           }
1291         } else if (swing.equalsIgnoreCase("TEXTFIELD")) {
1292           // TEXTFIELD takes an arbitrary String as input
1293           JTextField jtf = new JTextField(20);
1294           jtf.addMouseListener(this);
1295           jtf.setName(flag);
1296           if (defaultOption != null && defaultOption.equals("ATOMS")) {
1297             FFXSystem sys = mainPanel.getHierarchy().getActive();
1298             if (sys != null) {
1299               jtf.setText("" + sys.getAtomList().size());
1300             }
1301           } else if (defaultOption != null) {
1302             jtf.setText(defaultOption);
1303           }
1304           optionValuesPanel.add(jtf);
1305         } else if (swing.equalsIgnoreCase("RADIOBUTTONS")) {
1306           // RADIOBUTTONS allows one choice from a set of predifined
1307           // values
1308           bg = new ButtonGroup();
1309           values = option.getElementsByTagName("Value");
1310           for (int j = 0; j < values.getLength(); j++) {
1311             value = (Element) values.item(j);
1312             JRadioButton jrb = new JRadioButton(value.getAttribute("name"));
1313             jrb.addMouseListener(this);
1314             jrb.setName(flag);
1315             bg.add(jrb);
1316             if (defaultOption != null && jrb.getActionCommand().equalsIgnoreCase(defaultOption)) {
1317               jrb.setSelected(true);
1318             }
1319             optionValuesPanel.add(jrb);
1320           }
1321         } else if (swing.equalsIgnoreCase("PROTEIN")) {
1322           // Protein allows selection of amino acids for the protein
1323           // builder
1324           optionValuesPanel.setLayout(new BoxLayout(optionValuesPanel, BoxLayout.Y_AXIS));
1325           optionValuesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
1326           optionValuesPanel.add(getAminoAcidPanel());
1327           optionValuesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
1328           acidComboBox.removeAllItems();
1329           JButton add = new JButton("Edit");
1330           add.setActionCommand("PROTEIN");
1331           add.addActionListener(this);
1332           add.setAlignmentX(Button.CENTER_ALIGNMENT);
1333           JPanel comboPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
1334           comboPanel.add(acidTextField);
1335           comboPanel.add(add);
1336           optionValuesPanel.add(comboPanel);
1337           optionValuesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
1338           JButton remove = new JButton("Remove");
1339           add.setMinimumSize(remove.getPreferredSize());
1340           add.setPreferredSize(remove.getPreferredSize());
1341           remove.setActionCommand("PROTEIN");
1342           remove.addActionListener(this);
1343           remove.setAlignmentX(Button.CENTER_ALIGNMENT);
1344           comboPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
1345           comboPanel.add(acidComboBox);
1346           comboPanel.add(remove);
1347           optionValuesPanel.add(comboPanel);
1348           optionValuesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
1349           optionValuesPanel.add(acidScrollPane);
1350           optionValuesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
1351           JButton reset = new JButton("Reset");
1352           reset.setActionCommand("PROTEIN");
1353           reset.addActionListener(this);
1354           reset.setAlignmentX(Button.CENTER_ALIGNMENT);
1355           optionValuesPanel.add(reset);
1356           optionValuesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
1357           acidTextArea.setText("");
1358           acidTextField.setText("");
1359         } else if (swing.equalsIgnoreCase("NUCLEIC")) {
1360           // Nucleic allows selection of nucleic acids for the nucleic
1361           // acid builder
1362           optionValuesPanel.setLayout(new BoxLayout(optionValuesPanel, BoxLayout.Y_AXIS));
1363           optionValuesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
1364           optionValuesPanel.add(getNucleicAcidPanel());
1365           optionValuesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
1366           acidComboBox.removeAllItems();
1367           JButton add = new JButton("Edit");
1368           add.setActionCommand("NUCLEIC");
1369           add.addActionListener(this);
1370           add.setAlignmentX(Button.CENTER_ALIGNMENT);
1371           JPanel comboPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
1372           comboPanel.add(acidTextField);
1373           comboPanel.add(add);
1374           optionValuesPanel.add(comboPanel);
1375           optionValuesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
1376           JButton remove = new JButton("Remove");
1377           add.setMinimumSize(remove.getPreferredSize());
1378           add.setPreferredSize(remove.getPreferredSize());
1379           remove.setActionCommand("NUCLEIC");
1380           remove.addActionListener(this);
1381           remove.setAlignmentX(Button.CENTER_ALIGNMENT);
1382           comboPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
1383           comboPanel.add(acidComboBox);
1384           comboPanel.add(remove);
1385           optionValuesPanel.add(comboPanel);
1386           optionValuesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
1387           optionValuesPanel.add(acidScrollPane);
1388           optionValuesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
1389           JButton button = new JButton("Reset");
1390           button.setActionCommand("NUCLEIC");
1391           button.addActionListener(this);
1392           button.setAlignmentX(Button.CENTER_ALIGNMENT);
1393           optionValuesPanel.add(button);
1394           optionValuesPanel.add(Box.createRigidArea(new Dimension(0, 5)));
1395           acidTextArea.setText("");
1396           acidTextField.setText("");
1397         } else if (swing.equalsIgnoreCase("SYSTEMS")) {
1398           // SYSTEMS allows selection of an open system
1399           JComboBox<FFXSystem> jcb =
1400               new JComboBox<>(mainPanel.getHierarchy().getNonActiveSystems());
1401           jcb.setSize(jcb.getMaximumSize());
1402           jcb.addActionListener(this);
1403           optionValuesPanel.add(jcb);
1404         }
1405         // Set up a Conditional for this Option
1406         if (conditional != null) {
1407           isEnabled = false;
1408           String conditionalName = conditional.getAttribute("name");
1409           String conditionalValues = conditional.getAttribute("value");
1410           String cDescription = conditional.getAttribute("description");
1411           String cpostProcess = conditional.getAttribute("postProcess");
1412           if (conditionalName.toUpperCase().startsWith("KEYWORD")) {
1413             optionPanel.setName(conditionalName);
1414             String[] keywords = conditionalValues.split(" +");
1415             if (activeSystem != null) {
1416               Hashtable<String, Keyword> systemKeywords = activeSystem.getKeywords();
1417               for (String key : keywords) {
1418                 if (systemKeywords.containsKey(key.toUpperCase())) {
1419                   isEnabled = true;
1420                   break;
1421                 }
1422               }
1423             }
1424           } else if (conditionalName.toUpperCase().startsWith("VALUE")) {
1425             isEnabled = true;
1426             // Add listeners to the values of this option so
1427             // the conditional options can be disabled/enabled.
1428             for (int j = 0; j < optionValuesPanel.getComponentCount(); j++) {
1429               JToggleButton jtb = (JToggleButton) optionValuesPanel.getComponent(j);
1430               jtb.addActionListener(this);
1431               jtb.setActionCommand("Conditional");
1432             }
1433             JPanel condpanel = new JPanel();
1434             condpanel.setBorder(etchedBorder);
1435             JLabel condlabel = new JLabel(cDescription);
1436             condlabel.setEnabled(false);
1437             condlabel.setName(conditionalValues);
1438             JTextField condtext = new JTextField(10);
1439             condlabel.setLabelFor(condtext);
1440             if (cpostProcess != null) {
1441               condtext.setName(cpostProcess);
1442             }
1443             condtext.setEnabled(false);
1444             condpanel.add(condlabel);
1445             condpanel.add(condtext);
1446             conditionals.add(condlabel);
1447             optionPanel.add(condpanel, BorderLayout.SOUTH);
1448           } else if (conditionalName.toUpperCase().startsWith("REFLECTION")) {
1449             String[] condModifiers;
1450             if (conditionalValues.equalsIgnoreCase("AltLoc")) {
1451               condModifiers = activeSystem.getAltLocations();
1452               if (condModifiers != null && condModifiers.length > 1) {
1453                 isEnabled = true;
1454                 bg = new ButtonGroup();
1455                 for (int j = 0; j < condModifiers.length; j++) {
1456                   JRadioButton jrbmi = new JRadioButton(condModifiers[j]);
1457                   jrbmi.addMouseListener(this);
1458                   bg.add(jrbmi);
1459                   optionValuesPanel.add(jrbmi);
1460                   if (j == 0) {
1461                     jrbmi.setSelected(true);
1462                   }
1463                 }
1464               }
1465             } else if (conditionalValues.equalsIgnoreCase("Chains")) {
1466               condModifiers = activeSystem.getChainNames();
1467               if (condModifiers != null && condModifiers.length > 0) {
1468                 isEnabled = true;
1469                 for (int j = 0; j < condModifiers.length; j++) {
1470                   JRadioButton jrbmi = new JRadioButton(condModifiers[j]);
1471                   jrbmi.addMouseListener(this);
1472                   bg.add(jrbmi);
1473                   optionValuesPanel.add(jrbmi, j);
1474                 }
1475               }
1476             }
1477           }
1478         }
1479         optionPanel.add(optionValuesPanel, BorderLayout.CENTER);
1480         optionPanel.setPreferredSize(optionPanel.getPreferredSize());
1481         optionsTabbedPane.addTab(option.getAttribute("name"), optionPanel);
1482         optionsTabbedPane.setEnabledAt(optionsTabbedPane.getTabCount() - 1, isEnabled);
1483       }
1484     }
1485     optionsTabbedPane.setPreferredSize(optionsTabbedPane.getPreferredSize());
1486     commandPanel.setLayout(borderLayout);
1487     commandPanel.add(optionsTabbedPane, BorderLayout.CENTER);
1488     commandPanel.validate();
1489     commandPanel.repaint();
1490     loadLogSettings();
1491     statusLabel.setText("  " + createCommandInput());
1492   }
1493 
1494   // *********************************************************************
1495   // Initialization code and misc. methods.
1496 
1497   private void loadLogSettings() {
1498     String selected = (String) logSettings.getSelectedItem();
1499     logSettings.removeActionListener(this);
1500     logSettings.removeAllItems();
1501     File currentLog;
1502     String fileName;
1503     File logDir;
1504     File systemDir;
1505     // This implies no Open System
1506     if (activeSystem == null) {
1507       fileName = ((String) currentCommandBox.getSelectedItem()).toLowerCase() + ".log";
1508       currentLog = new File(MainPanel.getPWD() + File.separator + fileName);
1509       logDir = currentLog.getParentFile();
1510       systemDir = MainPanel.getPWD();
1511     } else {
1512       currentLog = activeSystem.getLogFile();
1513       fileName = currentLog.getName();
1514       logDir = currentLog.getParentFile();
1515       systemDir = activeSystem.getFile().getAbsoluteFile().getParentFile();
1516     }
1517     File tempLog;
1518     File newLog;
1519     if (logDir == null || !logDir.equals(systemDir)) {
1520       tempLog = new File(systemDir.getAbsolutePath() + File.separator + fileName);
1521       if (!commandFileTypes.contains(FileType.ANY)) {
1522         activeSystem.setLogFile(tempLog);
1523       }
1524     } else {
1525       tempLog = currentLog;
1526     }
1527     // Simple Case - default log file doesn't exist yet
1528     String createNew = null;
1529     String logString = null;
1530     if (!tempLog.exists()) {
1531       createNew = "Create " + fileName;
1532       logSettings.addItem(createNew);
1533       logSettings.setSelectedItem(createNew);
1534       logString = getLogString(tempLog);
1535       logSettings.addActionListener(this);
1536       if (!commandFileTypes.contains(FileType.ANY)) {
1537         activeSystem.setLogFile(tempLog);
1538       }
1539       return;
1540     }
1541     // The default log exists, so we can append to it, overwrite it, or
1542     // create a new one
1543     newLog = SystemFilter.version(tempLog);
1544     tempLog = SystemFilter.previousVersion(newLog);
1545     fileName = tempLog.getName();
1546     String append = "Append to " + fileName;
1547     String overwrite = "Overwrite " + fileName;
1548     logSettings.addItem(append);
1549     logSettings.addItem(overwrite);
1550     if (!newLog.equals(tempLog)) {
1551       createNew = "Create " + newLog.getName();
1552       logSettings.addItem(createNew);
1553     }
1554     if (selected == null) {
1555       logSettings.setSelectedIndex(0);
1556       logString = null;
1557     } else if (selected.startsWith("Append")) {
1558       logSettings.setSelectedItem(append);
1559       logString = getLogString(tempLog);
1560     } else if (selected.startsWith("Overwrite")) {
1561       logSettings.setSelectedItem(overwrite);
1562       logString = getLogString(tempLog);
1563     } else {
1564       if (createNew != null) {
1565         logSettings.setSelectedItem(createNew);
1566         logString = getLogString(newLog);
1567       } else {
1568         logString = getLogString(tempLog);
1569         logSettings.setSelectedItem(append);
1570       }
1571     }
1572     logSettings.addActionListener(this);
1573   }
1574 
1575   /** If a TINKER END file exists for the active system & command, remove it. */
1576   private void removeEnd() {
1577     FFXSystem m = mainPanel.getHierarchy().getActive();
1578     if (m == null) {
1579       return;
1580     }
1581     File f = m.getFile();
1582     if (f == null) {
1583       return;
1584     }
1585     File end = null;
1586     try {
1587       if (f.getName().indexOf(".") > 0) {
1588         String name = f.getName().substring(0, f.getName().lastIndexOf("."));
1589         end = new File(f.getParent(), name + ".end");
1590       } else {
1591         end = new File(f.getParent(), f.getName() + ".end");
1592       }
1593     } catch (Exception e) {
1594       //
1595     }
1596     if (end != null && end.exists()) {
1597       end.delete();
1598     }
1599   }
1600 
1601   /** Save ModelingPanel user preferences */
1602   void savePrefs() {
1603     String c = ModelingPanel.class.getName();
1604     prefs.putBoolean(c + ".description", descriptCheckBox.isSelected());
1605   }
1606 
1607   /**
1608    * Set the description divider location
1609    *
1610    * @param showDescription True to show the command description
1611    */
1612   private void setDivider(boolean showDescription) {
1613     descriptCheckBox.setSelected(showDescription);
1614     if (showDescription) {
1615       int spDivider = (int) (this.getHeight() * (3.0f / 5.0f));
1616       splitPane.setDividerLocation(spDivider);
1617     } else {
1618       splitPane.setDividerLocation(1.0);
1619     }
1620   }
1621 
1622   // *********************************************************************
1623   // Logging Configuation
1624 
1625   /** Prompt the user to toggle the existence of a TINKER END file. */
1626   private void setEnd() {
1627     if (ffxThread != null && ffxThread.isAlive()) {
1628       ModelingShell modelingShell = mainPanel.getModelingShell();
1629       modelingShell.doInterrupt();
1630     }
1631   }
1632 
1633   /** deleteLogs */
1634   private void deleteLogs() {
1635     if (activeSystem != null) {
1636       File file = activeSystem.getFile();
1637       String name = file.getName();
1638       String dir = file.getParent();
1639       int i =
1640           JOptionPane.showConfirmDialog(
1641               this,
1642               "Delete all logs for " + name + " from " + dir + " ?",
1643               "Delete Logs",
1644               JOptionPane.YES_NO_OPTION,
1645               JOptionPane.WARNING_MESSAGE);
1646       if (i == JOptionPane.YES_OPTION) {
1647         try {
1648           File[] files = activeSystem.getFile().getParentFile().listFiles();
1649           for (File f : files) {
1650             name = FilenameUtils.getBaseName(f.getAbsolutePath());
1651             if (FilenameUtils.wildcardMatch(f.getName(), name + ".log*")) {
1652               f.delete();
1653             }
1654           }
1655         } catch (Exception e) {
1656           //
1657         }
1658         activeSystem.setLogFile(null);
1659         loadLogSettings();
1660       }
1661     }
1662   }
1663 
1664   private class FFXLauncher implements Runnable {
1665 
1666     List<String> argList;
1667     File scriptFile;
1668 
1669     FFXLauncher(List<String> argList, File scriptFile) {
1670       this.argList = argList;
1671       this.scriptFile = scriptFile;
1672     }
1673 
1674     @Override
1675     public void run() {
1676       mainPanel.getModelingShell().setArgList(argList);
1677       mainPanel.open(scriptFile, null);
1678     }
1679   }
1680 }