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