67b45e3a249eddaf4174192abd16b130498395ab
[jalview.git] / src / jalview / gui / Preferences.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import java.awt.BorderLayout;
24 import java.awt.Color;
25 import java.awt.Component;
26 import java.awt.Dimension;
27 import java.awt.Font;
28 import java.awt.event.ActionEvent;
29 import java.awt.event.ActionListener;
30 import java.awt.event.FocusAdapter;
31 import java.awt.event.FocusEvent;
32 import java.awt.event.MouseEvent;
33 import java.io.File;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.concurrent.CompletableFuture;
37
38 import javax.help.HelpSetException;
39 import javax.swing.JComboBox;
40 import javax.swing.JFileChooser;
41 import javax.swing.JInternalFrame;
42 import javax.swing.JPanel;
43 import javax.swing.JTabbedPane;
44 import javax.swing.JTextField;
45 import javax.swing.ListSelectionModel;
46 import javax.swing.RowFilter;
47 import javax.swing.RowSorter;
48 import javax.swing.SortOrder;
49 import javax.swing.event.DocumentEvent;
50 import javax.swing.event.DocumentListener;
51 import javax.swing.event.ListSelectionEvent;
52 import javax.swing.event.ListSelectionListener;
53 import javax.swing.table.TableCellRenderer;
54 import javax.swing.table.TableColumn;
55 import javax.swing.table.TableModel;
56 import javax.swing.table.TableRowSorter;
57
58 import jalview.hmmer.HmmerCommand;
59 import jalview.util.FileUtils;
60 import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
61 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
62 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
63 import jalview.bin.Cache;
64 import jalview.bin.Console;
65 import jalview.bin.MemorySetting;
66 import jalview.ext.pymol.PymolManager;
67 import jalview.gui.Help.HelpId;
68 import jalview.gui.StructureViewer.ViewerType;
69 import jalview.io.BackupFiles;
70 import jalview.io.BackupFilesPresetEntry;
71 import jalview.io.FileFormatI;
72 import jalview.io.JalviewFileChooser;
73 import jalview.io.JalviewFileView;
74 import jalview.jbgui.GPreferences;
75 import jalview.jbgui.GSequenceLink;
76 import jalview.schemes.ColourSchemeI;
77 import jalview.schemes.ColourSchemes;
78 import jalview.schemes.ResidueColourScheme;
79 import jalview.urls.UrlLinkTableModel;
80 import jalview.urls.api.UrlProviderFactoryI;
81 import jalview.urls.api.UrlProviderI;
82 import jalview.urls.desktop.DesktopUrlProviderFactory;
83 import jalview.util.MessageManager;
84 import jalview.util.Platform;
85 import jalview.util.UrlConstants;
86 import jalview.ws.sifts.SiftsSettings;
87 import jalview.ws2.client.api.WebServiceDiscovererI;
88 import jalview.ws2.client.ebi.JobDispatcherWSDiscoverer;
89 import jalview.ws2.client.jpred4.JPred4WSDiscoverer;
90 import jalview.ws2.client.slivka.SlivkaWSDiscoverer;
91
92 /**
93  * DOCUMENT ME!
94  * 
95  * @author $author$
96  * @version $Revision$
97  */
98 public class Preferences extends GPreferences implements ApplicationSingletonI
99 {
100   // suggested list delimiter character
101   public static final String COMMA = ",";
102
103   public static final String HMMSEARCH_SEQCOUNT = "HMMSEARCH_SEQCOUNT";
104
105   public static final String HMMINFO_GLOBAL_BACKGROUND = "HMMINFO_GLOBAL_BACKGROUND";
106
107   public static final String HMMALIGN_TRIM_TERMINI = "HMMALIGN_TRIM_TERMINI";
108   
109   public static final String ADD_SS_ANN = "ADD_SS_ANN";
110
111   public static final String ADD_TEMPFACT_ANN = "ADD_TEMPFACT_ANN";
112
113   public static final String ALLOW_UNPUBLISHED_PDB_QUERYING = "ALLOW_UNPUBLISHED_PDB_QUERYING";
114
115   public static final String ANNOTATIONCOLOUR_MAX = "ANNOTATIONCOLOUR_MAX";
116
117   public static final String ANNOTATIONCOLOUR_MIN = "ANNOTATIONCOLOUR_MIN";
118
119   public static final String ANTI_ALIAS = "ANTI_ALIAS";
120
121   public static final String AUTO_CALC_CONSENSUS = "AUTO_CALC_CONSENSUS";
122
123   public static final String AUTOASSOCIATE_PDBANDSEQS = "AUTOASSOCIATE_PDBANDSEQS";
124
125   public static final String BLOSUM62_PCA_FOR_NUCLEOTIDE = "BLOSUM62_PCA_FOR_NUCLEOTIDE";
126
127   public static final String CENTRE_COLUMN_LABELS = "CENTRE_COLUMN_LABELS";
128
129   public static final String CHIMERA_PATH = "CHIMERA_PATH";
130
131   public static final String CHIMERAX_PATH = "CHIMERAX_PATH";
132
133   public static final String DBREFFETCH_USEPICR = "DBREFFETCH_USEPICR";
134
135   public static final String DEFAULT_COLOUR = "DEFAULT_COLOUR";
136
137   public static final String DEFAULT_COLOUR_NUC = "DEFAULT_COLOUR_NUC";
138   public static final String DEFAULT_COLOUR_PROT = "DEFAULT_COLOUR_PROT";
139
140   public static final String ENABLE_SPLIT_FRAME = "ENABLE_SPLIT_FRAME";
141
142   public static final String FIGURE_AUTOIDWIDTH = "FIGURE_AUTOIDWIDTH";
143
144   public static final String FIGURE_FIXEDIDWIDTH = "FIGURE_FIXEDIDWIDTH";
145
146   public static final String FOLLOW_SELECTIONS = "FOLLOW_SELECTIONS";
147
148   public static final String FONT_NAME = "FONT_NAME";
149
150   public static final String FONT_SIZE = "FONT_SIZE";
151
152   public static final String FONT_STYLE = "FONT_STYLE";
153   
154   public static final String HMMER_PATH = "HMMER_PATH";
155
156   public static final String CYGWIN_PATH = "CYGWIN_PATH";
157
158   public static final String HMMSEARCH_DBS = "HMMSEARCH_DBS";
159
160   public static final String GAP_COLOUR = "GAP_COLOUR";
161
162   public static final String GAP_SYMBOL = "GAP_SYMBOL";
163
164   public static final String HIDDEN_COLOUR = "HIDDEN_COLOUR";
165
166   public static final String HIDE_INTRONS = "HIDE_INTRONS";
167
168   public static final String ID_ITALICS = "ID_ITALICS";
169
170   public static final String ID_ORG_HOSTURL = "ID_ORG_HOSTURL";
171
172   public static final String MAP_WITH_SIFTS = "MAP_WITH_SIFTS";
173
174   public static final String NOQUESTIONNAIRES = "NOQUESTIONNAIRES";
175
176   public static final String NORMALISE_CONSENSUS_LOGO = "NORMALISE_CONSENSUS_LOGO";
177
178   public static final String NORMALISE_LOGO = "NORMALISE_LOGO";
179
180   public static final String PAD_GAPS = "PAD_GAPS";
181
182   public static final String PDB_DOWNLOAD_FORMAT = "PDB_DOWNLOAD_FORMAT";
183   public static final String PYMOL_PATH = "PYMOL_PATH";
184
185   public static final String QUESTIONNAIRE = "QUESTIONNAIRE";
186
187   public static final String RELAXEDSEQIDMATCHING = "RELAXEDSEQIDMATCHING";
188
189   public static final String RIGHT_ALIGN_IDS = "RIGHT_ALIGN_IDS";
190
191   public static final String SCALE_PROTEIN_TO_CDNA = "SCALE_PROTEIN_TO_CDNA";
192
193   public static final String SHOW_ANNOTATIONS = "SHOW_ANNOTATIONS";
194
195   public static final String SHOW_AUTOCALC_ABOVE = "SHOW_AUTOCALC_ABOVE";
196
197   public static final String SHOW_CONSENSUS = "SHOW_CONSENSUS";
198
199   public static final String SHOW_CONSENSUS_HISTOGRAM = "SHOW_CONSENSUS_HISTOGRAM";
200
201   public static final String SHOW_CONSENSUS_LOGO = "SHOW_CONSENSUS_LOGO";
202
203   public static final String SHOW_CONSERVATION = "SHOW_CONSERVATION";
204
205   public static final String SHOW_DBREFS_TOOLTIP = "SHOW_DBREFS_TOOLTIP";
206
207   public static final String SHOW_GROUP_CONSENSUS = "SHOW_GROUP_CONSENSUS";
208
209   public static final String SHOW_GROUP_CONSERVATION = "SHOW_GROUP_CONSERVATION";
210
211   public static final String SHOW_JVSUFFIX = "SHOW_JVSUFFIX";
212
213   public static final String SHOW_NPFEATS_TOOLTIP = "SHOW_NPFEATS_TOOLTIP";
214   public static final String SHOW_OCCUPANCY = "SHOW_OCCUPANCY";
215
216   public static final String SHOW_OV_HIDDEN_AT_START = "SHOW_OV_HIDDEN_AT_START";
217
218   public static final String SHOW_OVERVIEW = "SHOW_OVERVIEW";
219
220   public static final String SHOW_QUALITY = "SHOW_QUALITY";
221
222   public static final String SHOW_UNCONSERVED = "SHOW_UNCONSERVED";
223
224   public static final String SORT_ALIGNMENT = "SORT_ALIGNMENT";
225
226   public static final String SORT_ANNOTATIONS = "SORT_ANNOTATIONS";
227
228   public static final String SORT_BY_TREE = "SORT_BY_TREE";
229
230   public static final String STRUCT_FROM_PDB = "STRUCT_FROM_PDB";
231
232   public static final String STRUCTURE_DISPLAY = "STRUCTURE_DISPLAY";
233
234   public static final String STRUCTURE_DIMENSIONS = "STRUCTURE_DIMENSIONS";
235
236   public static final String UNIPROT_DOMAIN = "UNIPROT_DOMAIN";
237
238   public static final String USE_FULL_SO = "USE_FULL_SO";
239   public static final String USE_LEGACY_GAP = "USE_LEGACY_GAP";
240
241   public static final String USE_RNAVIEW = "USE_RNAVIEW";
242
243   public static final String USER_DEFINED_COLOURS = "USER_DEFINED_COLOURS";
244
245   public static final String WRAP_ALIGNMENT = "WRAP_ALIGNMENT";
246
247   private static final int MIN_FONT_SIZE = 1;
248
249   private static final int MAX_FONT_SIZE = 30;
250
251   private String previousProxyType;
252
253   private static Preferences INSTANCE = null; // add "final"
254
255   /**
256    * Holds name and link separated with | character. Sequence ID must be
257    * $SEQUENCE_ID$ or $SEQUENCE_ID=/.possible | chars ./=$
258    */
259   public static UrlProviderI sequenceUrlLinks;
260
261   public static UrlLinkTableModel dataModel;
262
263   /**
264    * Holds name and link separated with | character. Sequence IDS and Sequences
265    * must be $SEQUENCEIDS$ or $SEQUENCEIDS=/.possible | chars ./=$ and $SEQUENCES$
266    * or $SEQUENCES=/.possible | chars ./=$ and separation character for first and
267    * second token specified after a pipe character at end |,|. (TODO: proper
268    * escape for using | to separate ids or sequences
269    */
270
271   public static List<String> groupURLLinks;
272   static
273   {
274     // get links selected to be in the menu (SEQUENCE_LINKS)
275     // and links entered by the user but not selected (STORED_LINKS)
276     String inMenuString = Cache.getDefault("SEQUENCE_LINKS", "");
277     String notInMenuString = Cache.getDefault("STORED_LINKS", "");
278     String defaultUrl = Cache.getDefault("DEFAULT_URL",
279             UrlConstants.DEFAULT_LABEL);
280
281     // if both links lists are empty, add the DEFAULT_URL link
282     // otherwise we assume the default link is in one of the lists
283     if (inMenuString.isEmpty() && notInMenuString.isEmpty())
284     {
285       inMenuString = UrlConstants.DEFAULT_STRING;
286     }
287     UrlProviderFactoryI factory = new DesktopUrlProviderFactory(defaultUrl,
288             inMenuString, notInMenuString);
289     sequenceUrlLinks = factory.createUrlProvider();
290     dataModel = new UrlLinkTableModel(sequenceUrlLinks);
291
292     /**
293      * TODO: reformulate groupURL encoding so two or more can be stored in the
294      * .properties file as '|' separated strings
295      */
296
297     groupURLLinks = new ArrayList<>();
298   }
299
300   JInternalFrame frame;
301
302   private WsPreferences wsPrefs;
303
304   private OptionsParam promptEachTimeOpt = new OptionsParam(
305           MessageManager.getString("label.prompt_each_time"),
306           "Prompt each time");
307
308   private OptionsParam lineArtOpt = new OptionsParam(
309           MessageManager.getString("label.lineart"), "Lineart");
310
311   private OptionsParam textOpt = new OptionsParam(
312           MessageManager.getString("action.text"), "Text");
313
314   // get singleton Preferences instance
315   public static Preferences getInstance()
316   {
317     if (INSTANCE == null || INSTANCE.frame == null
318             || INSTANCE.frame.isClosed())
319     {
320       INSTANCE = new Preferences();
321     }
322     return INSTANCE;
323
324     /*
325      * Replace code with the following for Jalvew-JS
326     Preferences INSTANCE = ApplicationSingletonProvider.getInstance(Preferences.class);
327     if (INSTANCE == null || INSTANCE.frame == null
328             || INSTANCE.frame.isClosed())
329     {
330       ApplicationSingletonProvider.remove(Preferences.class);
331       INSTANCE = ApplicationSingletonProvider.getInstance(Preferences.class);
332     }
333     return INSTANCE;
334     */
335   }
336
337   public static void openPreferences()
338   {
339     openPreferences(null, null);
340   }
341
342   public static void openPreferences(TabRef selectTab, String message)
343   {
344     Preferences p = getInstance();
345     if (selectTab != null)
346       p.selectTab(selectTab, message);
347     p.frame.show();
348     p.frame.moveToFront();
349     p.frame.grabFocus();
350   }
351
352   public void selectTab(TabRef selectTab, String message)
353   {
354     this.selectTab(selectTab);
355     if (message != null)
356       this.setMessage(message);
357     this.frame.show();
358   }
359
360   /**
361    * Creates a new Preferences object.
362    */
363   private Preferences()
364   {
365     super();
366     frame = new JInternalFrame();
367     frame.setContentPane(this);
368     if (!Platform.isJS())
369     /**
370      * Java only
371      * 
372      * @j2sIgnore
373      */
374     {
375       wsPrefs = new WsPreferences();
376       wsTab.add(wsPrefs, BorderLayout.CENTER);
377     }
378
379     if (Cache.getDefault("SHOW_SLIVKA_SERVICES", true))
380       addWebServiceTab(tabbedPane, SlivkaWSDiscoverer.getInstance());
381     if (Cache.getDefault("SHOW_EBI_SERVICES", true))
382       addWebServiceTab(tabbedPane, JobDispatcherWSDiscoverer.getInstance());
383     if (Cache.getDefault("SHOW_JPRED4_SERVICES", true))
384       addWebServiceTab(tabbedPane, JPred4WSDiscoverer.getInstance());
385
386     int width = 500, height = 450;
387     if (Platform.isAMacAndNotJS())
388     {
389       width = 570;
390       height = 480;
391     }
392
393     Desktop.addInternalFrame(frame,
394             MessageManager.getString("label.preferences"), width, height);
395     frame.setMinimumSize(new Dimension(width, height));
396
397     /*
398      * Set HMMER tab defaults
399      */
400     hmmrTrimTermini.setSelected(Cache.getDefault(HMMALIGN_TRIM_TERMINI, false));
401     if (Cache.getDefault(HMMINFO_GLOBAL_BACKGROUND, false))
402     {
403       hmmerBackgroundUniprot.setSelected(true);
404     }
405     else
406     {
407       hmmerBackgroundAlignment.setSelected(true);
408     }
409     hmmerSequenceCount
410             .setText(Cache.getProperty(HMMSEARCH_SEQCOUNT));
411     hmmerPath.setText(Cache.getProperty(HMMER_PATH));
412     hmmerPath.addActionListener(new ActionListener()
413     {
414       @Override
415       public void actionPerformed(ActionEvent e)
416       {
417         validateHmmerPath();
418       }
419     });
420     hmmerPath.addFocusListener(new FocusAdapter()
421     {
422       @Override
423       public void focusLost(FocusEvent e)
424       {
425         validateHmmerPath();
426       }
427     });
428     if (cygwinPath != null)
429     {
430       String path = Cache.getProperty(CYGWIN_PATH);
431       if (path == null)
432       {
433         path = FileUtils.getPathTo("bash");
434       }
435       cygwinPath.setText(path);
436       cygwinPath.addActionListener(new ActionListener()
437       {
438         @Override
439         public void actionPerformed(ActionEvent e)
440         {
441           validateCygwinPath();
442         }
443       });
444       cygwinPath.addFocusListener(new FocusAdapter()
445       {
446         @Override
447         public void focusLost(FocusEvent e)
448         {
449           validateCygwinPath();
450         }
451       });
452     }
453
454     /*
455      * Set Visual tab defaults
456      */
457     seqLimit.setSelected(Cache.getDefault("SHOW_JVSUFFIX", true));
458     rightAlign.setSelected(Cache.getDefault("RIGHT_ALIGN_IDS", false));
459     fullScreen.setSelected(Cache.getDefault("SHOW_FULLSCREEN", false));
460     annotations.setSelected(Cache.getDefault("SHOW_ANNOTATIONS", true));
461
462     conservation.setSelected(Cache.getDefault("SHOW_CONSERVATION", true));
463     quality.setSelected(Cache.getDefault("SHOW_QUALITY", true));
464     identity.setSelected(Cache.getDefault("SHOW_IDENTITY", true));
465     openoverv.setSelected(Cache.getDefault("SHOW_OVERVIEW", false));
466     showUnconserved
467             .setSelected(Cache.getDefault("SHOW_UNCONSERVED", false));
468     showOccupancy.setSelected(Cache.getDefault(SHOW_OCCUPANCY, false));
469     showGroupConsensus
470             .setSelected(Cache.getDefault("SHOW_GROUP_CONSENSUS", false));
471     showGroupConservation.setSelected(
472             Cache.getDefault("SHOW_GROUP_CONSERVATION", false));
473     showConsensHistogram.setSelected(
474             Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM", true));
475     showConsensLogo
476             .setSelected(Cache.getDefault("SHOW_CONSENSUS_LOGO", false));
477     showInformationHistogram.setSelected(
478             Cache.getDefault("SHOW_INFORMATION_HISTOGRAM", true));
479     showHMMLogo.setSelected(Cache.getDefault("SHOW_HMM_LOGO", false));
480     showNpTooltip
481             .setSelected(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true));
482     showDbRefTooltip
483             .setSelected(Cache.getDefault("SHOW_DBREFS_TOOLTIP", true));
484
485     String[] fonts = java.awt.GraphicsEnvironment
486             .getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
487     for (int i = 0; i < fonts.length; i++)
488     {
489       fontNameCB.addItem(fonts[i]);
490     }
491
492     for (int i = MIN_FONT_SIZE; i <= MAX_FONT_SIZE; i++)
493     {
494       fontSizeCB.addItem(i + "");
495     }
496
497     fontStyleCB.addItem("plain");
498     fontStyleCB.addItem("bold");
499     fontStyleCB.addItem("italic");
500
501     fontNameCB.setSelectedItem(Cache.getDefault("FONT_NAME", "SansSerif"));
502     fontSizeCB.setSelectedItem(Cache.getDefault("FONT_SIZE", "10"));
503     fontStyleCB.setSelectedItem(
504             Cache.getDefault("FONT_STYLE", Font.PLAIN + ""));
505
506     smoothFont.setSelected(Cache.getDefault("ANTI_ALIAS", true));
507     scaleProteinToCdna
508             .setSelected(Cache.getDefault(SCALE_PROTEIN_TO_CDNA, false));
509
510     idItalics.setSelected(Cache.getDefault("ID_ITALICS", true));
511
512     wrap.setSelected(Cache.getDefault("WRAP_ALIGNMENT", false));
513
514     gapSymbolCB.addItem("-");
515     gapSymbolCB.addItem(".");
516
517     gapSymbolCB.setSelectedItem(Cache.getDefault("GAP_SYMBOL", "-"));
518
519     sortby.addItem("No sort");
520     sortby.addItem("Id");
521     sortby.addItem("Pairwise Identity");
522     sortby.setSelectedItem(Cache.getDefault("SORT_ALIGNMENT", "No sort"));
523
524     sortAnnBy.addItem(SequenceAnnotationOrder.NONE.toString());
525     sortAnnBy
526             .addItem(SequenceAnnotationOrder.SEQUENCE_AND_LABEL.toString());
527     sortAnnBy
528             .addItem(SequenceAnnotationOrder.LABEL_AND_SEQUENCE.toString());
529     SequenceAnnotationOrder savedSort = SequenceAnnotationOrder
530             .valueOf(Cache.getDefault(SORT_ANNOTATIONS,
531                     SequenceAnnotationOrder.NONE.name()));
532     sortAnnBy.setSelectedItem(savedSort.toString());
533
534     sortAutocalc.addItem("Autocalculated first");
535     sortAutocalc.addItem("Autocalculated last");
536     final boolean showAbove = Cache.getDefault(SHOW_AUTOCALC_ABOVE, true);
537     sortAutocalc.setSelectedItem(showAbove ? sortAutocalc.getItemAt(0)
538             : sortAutocalc.getItemAt(1));
539     startupCheckbox
540             .setSelected(Cache.getDefault("SHOW_STARTUP_FILE", true));
541     startupFileTextfield.setText(Cache.getDefault("STARTUP_FILE",
542             Cache.getDefault("www.jalview.org", "https://www.jalview.org")
543                     + "/examples/exampleFile_2_7.jvp"));
544
545     /*
546      * Set Colours tab defaults
547      */
548     protColour.addItem(ResidueColourScheme.NONE);
549     nucColour.addItem(ResidueColourScheme.NONE);
550     for (ColourSchemeI cs : ColourSchemes.getInstance().getColourSchemes())
551     {
552       String name = cs.getSchemeName();
553       protColour.addItem(name);
554       nucColour.addItem(name);
555     }
556     String oldProp = Cache.getDefault(DEFAULT_COLOUR,
557             ResidueColourScheme.NONE);
558     String newProp = Cache.getDefault(DEFAULT_COLOUR_PROT, null);
559     protColour.setSelectedItem(newProp != null ? newProp : oldProp);
560     newProp = Cache.getDefault(DEFAULT_COLOUR_NUC, null);
561     nucColour.setSelectedItem(newProp != null ? newProp : oldProp);
562     minColour.setBackground(
563             Cache.getDefaultColour("ANNOTATIONCOLOUR_MIN", Color.orange));
564     maxColour.setBackground(
565             Cache.getDefaultColour("ANNOTATIONCOLOUR_MAX", Color.red));
566
567     /*
568      * Set overview panel defaults
569      */
570     gapColour.setBackground(Cache.getDefaultColour(GAP_COLOUR,
571             jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP));
572     hiddenColour.setBackground(Cache.getDefaultColour(HIDDEN_COLOUR,
573             jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_HIDDEN));
574     useLegacyGap.setSelected(Cache.getDefault(USE_LEGACY_GAP, false));
575     gapLabel.setEnabled(!useLegacyGap.isSelected());
576     gapColour.setEnabled(!useLegacyGap.isSelected());
577     showHiddenAtStart
578             .setSelected(Cache.getDefault(SHOW_OV_HIDDEN_AT_START, false));
579
580     /*
581      * Set Structure tab defaults
582      */
583     final boolean structSelected = Cache.getDefault(STRUCT_FROM_PDB, true);
584     structFromPdb.setSelected(structSelected);
585     addSecondaryStructure.setSelected(Cache.getDefault(ADD_SS_ANN, true));
586     addSecondaryStructure.setEnabled(structSelected);
587     addTempFactor.setSelected(Cache.getDefault(ADD_TEMPFACT_ANN, true));
588     addTempFactor.setEnabled(structSelected);
589
590     /*
591      * set choice of structure viewer, and path if saved as a preference;
592      * default to Jmol (first choice) if an unexpected value is found
593      */
594     String viewerType = ViewerType.JMOL.name();
595     if (!Platform.isJS())
596     {
597       Cache.getDefault(STRUCTURE_DISPLAY,
598             ViewerType.JMOL.name());
599     }
600     // TODO - disable external viewers for JS
601     structViewer.setSelectedItem(viewerType);
602     String viewerPath = "";
603     ViewerType type = null;
604     try
605     {
606       type = ViewerType.valueOf(viewerType);
607       switch (type)
608       {
609       case JMOL:
610         break;
611       case CHIMERA:
612         viewerPath = Cache.getDefault(CHIMERA_PATH, "");
613         break;
614       case CHIMERAX:
615         viewerPath = Cache.getDefault(CHIMERAX_PATH, "");
616         break;
617       case PYMOL:
618         viewerPath = Cache.getDefault(PYMOL_PATH, "");
619         break;
620       }
621     } catch (IllegalArgumentException e)
622     {
623       Console.error("Unknown structure viewer type: " + viewerType
624               + ", defaulting to Jmol");
625       type = ViewerType.JMOL;
626     }
627     structureViewerPath.setText(viewerPath);
628
629     structureViewerPath.addActionListener(new ActionListener()
630     {
631       @Override
632       public void actionPerformed(ActionEvent e)
633       {
634         if (validateViewerPath())
635         {
636           String path = structureViewerPath.getText();
637           try
638           {
639             ViewerType type = ViewerType.valueOf(viewerType);
640             switch (type)
641             {
642             case JMOL:
643               break;
644             case CHIMERA:
645               Cache.setProperty(CHIMERA_PATH, path);
646               break;
647             case CHIMERAX:
648               Cache.setProperty(CHIMERAX_PATH, path);
649               break;
650             case PYMOL:
651               Cache.setProperty(PYMOL_PATH, path);
652               break;
653             }
654           } catch (IllegalArgumentException x)
655           {
656             Console.error("Failed to set path - unknown viewer type", x);
657           }
658         }
659       }
660     });
661
662     if (Cache.getDefault("MAP_WITH_SIFTS", false))
663     {
664       siftsMapping.setSelected(true);
665     }
666     else
667     {
668       nwMapping.setSelected(true);
669     }
670
671     SiftsSettings
672             .setMapWithSifts(Cache.getDefault("MAP_WITH_SIFTS", false));
673
674     /*
675      * Set Connections tab defaults
676      */
677
678     // set up sorting
679     linkUrlTable.setModel(dataModel);
680     final TableRowSorter<TableModel> sorter = new TableRowSorter<>(
681             linkUrlTable.getModel());
682     linkUrlTable.setRowSorter(sorter);
683     List<RowSorter.SortKey> sortKeys = new ArrayList<>();
684
685     UrlLinkTableModel m = (UrlLinkTableModel) linkUrlTable.getModel();
686     sortKeys.add(new RowSorter.SortKey(m.getPrimaryColumn(),
687             SortOrder.DESCENDING));
688     sortKeys.add(new RowSorter.SortKey(m.getSelectedColumn(),
689             SortOrder.DESCENDING));
690     sortKeys.add(
691             new RowSorter.SortKey(m.getNameColumn(), SortOrder.ASCENDING));
692
693     sorter.setSortKeys(sortKeys);
694     // BH 2018 setSortKeys will do the sort
695     // sorter.sort();
696
697     // set up filtering
698     ActionListener onReset;
699     onReset = new ActionListener()
700     {
701       @Override
702       public void actionPerformed(ActionEvent e)
703       {
704         filterTB.setText("");
705         sorter.setRowFilter(RowFilter.regexFilter(""));
706       }
707
708     };
709     doReset.addActionListener(onReset);
710
711     // filter to display only custom urls
712     final RowFilter<TableModel, Object> customUrlFilter = new RowFilter<TableModel, Object>()
713     {
714       @Override
715       public boolean include(
716               Entry<? extends TableModel, ? extends Object> entry)
717       {
718         return ((UrlLinkTableModel) entry.getModel()).isUserEntry(entry);
719       }
720     };
721
722     final TableRowSorter<TableModel> customSorter = new TableRowSorter<>(
723             linkUrlTable.getModel());
724     customSorter.setRowFilter(customUrlFilter);
725
726     ActionListener onCustomOnly;
727     onCustomOnly = new ActionListener()
728     {
729       @Override
730       public void actionPerformed(ActionEvent e)
731       {
732         filterTB.setText("");
733         sorter.setRowFilter(customUrlFilter);
734       }
735     };
736     userOnly.addActionListener(onCustomOnly);
737
738     filterTB.getDocument().addDocumentListener(new DocumentListener()
739     {
740       String caseInsensitiveFlag = "(?i)";
741
742       @Override
743       public void changedUpdate(DocumentEvent e)
744       {
745         sorter.setRowFilter(RowFilter
746                 .regexFilter(caseInsensitiveFlag + filterTB.getText()));
747       }
748
749       @Override
750       public void removeUpdate(DocumentEvent e)
751       {
752         sorter.setRowFilter(RowFilter
753                 .regexFilter(caseInsensitiveFlag + filterTB.getText()));
754       }
755
756       @Override
757       public void insertUpdate(DocumentEvent e)
758       {
759         sorter.setRowFilter(RowFilter
760                 .regexFilter(caseInsensitiveFlag + filterTB.getText()));
761       }
762     });
763
764     // set up list selection functionality
765     linkUrlTable.getSelectionModel()
766             .addListSelectionListener(new UrlListSelectionHandler());
767
768     // set up radio buttons
769     int onClickCol = ((UrlLinkTableModel) linkUrlTable.getModel())
770             .getPrimaryColumn();
771     String onClickName = linkUrlTable.getColumnName(onClickCol);
772     linkUrlTable.getColumn(onClickName)
773             .setCellRenderer(new RadioButtonRenderer());
774     linkUrlTable.getColumn(onClickName)
775             .setCellEditor(new RadioButtonEditor());
776
777     // get boolean columns and resize those to min possible
778     for (int column = 0; column < linkUrlTable.getColumnCount(); column++)
779     {
780       if (linkUrlTable.getModel().getColumnClass(column)
781               .equals(Boolean.class))
782       {
783         TableColumn tableColumn = linkUrlTable.getColumnModel()
784                 .getColumn(column);
785         int preferredWidth = tableColumn.getMinWidth();
786
787         TableCellRenderer cellRenderer = linkUrlTable.getCellRenderer(0,
788                 column);
789         Component c = linkUrlTable.prepareRenderer(cellRenderer, 0, column);
790         int cwidth = c.getPreferredSize().width
791                 + linkUrlTable.getIntercellSpacing().width;
792         preferredWidth = Math.max(preferredWidth, cwidth);
793
794         tableColumn.setPreferredWidth(preferredWidth);
795       }
796     }
797
798     String proxyTypeString = Cache.getDefault("USE_PROXY", "false");
799     previousProxyType = proxyTypeString;
800     switch (proxyTypeString)
801     {
802     case Cache.PROXYTYPE_NONE:
803       proxyType.setSelected(noProxy.getModel(), true);
804       break;
805     case Cache.PROXYTYPE_SYSTEM:
806       proxyType.setSelected(systemProxy.getModel(), true);
807       break;
808     case Cache.PROXYTYPE_CUSTOM:
809       proxyType.setSelected(customProxy.getModel(), true);
810       break;
811     default:
812       Console.warn(
813               "Incorrect PROXY_TYPE - should be 'none' (clear proxy properties), 'false' (system settings), 'true' (custom settings): "
814                       + proxyTypeString);
815     }
816     proxyServerHttpTB.setText(Cache.getDefault("PROXY_SERVER", ""));
817     proxyPortHttpTB.setText(Cache.getDefault("PROXY_PORT", ""));
818     proxyServerHttpsTB.setText(Cache.getDefault("PROXY_SERVER_HTTPS", ""));
819     proxyPortHttpsTB.setText(Cache.getDefault("PROXY_PORT_HTTPS", ""));
820     proxyAuth.setSelected(Cache.getDefault("PROXY_AUTH", false));
821     proxyAuthUsernameTB
822             .setText(Cache.getDefault("PROXY_AUTH_USERNAME", ""));
823     // we are not storing or retrieving proxy password from .jalview_properties
824     proxyAuthPasswordPB.setText(Cache.proxyAuthPassword == null ? ""
825             : new String(Cache.proxyAuthPassword));
826     setCustomProxyEnabled();
827     applyProxyButtonEnabled(false);
828
829     defaultBrowser.setText(Cache.getDefault("DEFAULT_BROWSER", ""));
830
831     usagestats.setSelected(Cache.getDefault("USAGESTATS", false));
832     // note antisense here: default is true
833     questionnaire
834             .setSelected(Cache.getProperty("NOQUESTIONNAIRES") == null);
835     versioncheck.setSelected(Cache.getDefault("VERSION_CHECK", true));
836
837     /*
838      * Set Output tab defaults
839      */
840     setupOutputCombo(epsRendering, "EPS_RENDERING");
841     setupOutputCombo(htmlRendering, "HTML_RENDERING");
842     setupOutputCombo(svgRendering, "SVG_RENDERING");
843     autoIdWidth.setSelected(Cache.getDefault("FIGURE_AUTOIDWIDTH", false));
844     userIdWidth.setEnabled(!autoIdWidth.isSelected());
845     userIdWidthlabel.setEnabled(!autoIdWidth.isSelected());
846     Integer wi = Cache.getIntegerProperty("FIGURE_FIXEDIDWIDTH");
847     userIdWidth.setText(wi == null ? "" : wi.toString());
848     // TODO: refactor to use common enum via FormatAdapter and allow extension
849     // for new flat file formats
850     blcjv.setSelected(Cache.getDefault("BLC_JVSUFFIX", true));
851     clustaljv.setSelected(Cache.getDefault("CLUSTAL_JVSUFFIX", true));
852     fastajv.setSelected(Cache.getDefault("FASTA_JVSUFFIX", true));
853     msfjv.setSelected(Cache.getDefault("MSF_JVSUFFIX", true));
854     pfamjv.setSelected(Cache.getDefault("PFAM_JVSUFFIX", true));
855     pileupjv.setSelected(Cache.getDefault("PILEUP_JVSUFFIX", true));
856     pirjv.setSelected(Cache.getDefault("PIR_JVSUFFIX", true));
857     modellerOutput.setSelected(Cache.getDefault("PIR_MODELLER", false));
858     embbedBioJSON
859             .setSelected(Cache.getDefault("EXPORT_EMBBED_BIOJSON", true));
860
861     /*
862      * Set Editing tab defaults
863      */
864     autoCalculateConsCheck
865             .setSelected(Cache.getDefault("AUTO_CALC_CONSENSUS", true));
866     padGaps.setSelected(Cache.getDefault("PAD_GAPS", false));
867     sortByTree.setSelected(Cache.getDefault("SORT_BY_TREE", false));
868
869     annotations_actionPerformed(null); // update the display of the annotation
870                                        // settings
871
872     
873     
874     /*
875      * Set Backups tab defaults
876      */
877     loadLastSavedBackupsOptions();
878   }
879
880   
881   private void addWebServiceTab(JTabbedPane tabs, WebServiceDiscovererI discoverer)
882   {
883     String name = discoverer.getName();
884     JPanel tab = new JPanel();
885     tab.setLayout(new BorderLayout());
886     tab.add(new WS2PreferencesPanel(discoverer), BorderLayout.CENTER);
887     tabs.add(tab, name + " services");
888   }
889   
890   /**
891    * A helper method that sets the items and initial selection in a character
892    * rendering option list (Prompt each time/Lineart/Text)
893    * 
894    * @param comboBox
895    * @param propertyKey
896    */
897   protected void setupOutputCombo(JComboBox<Object> comboBox,
898           String propertyKey)
899   {
900     comboBox.addItem(promptEachTimeOpt);
901     comboBox.addItem(lineArtOpt);
902     comboBox.addItem(textOpt);
903
904     
905     /*
906      * JalviewJS doesn't support Lineart so force it to Text
907      */
908     String defaultOption = Platform.isJS() ? "Text"
909             : Cache.getDefault(propertyKey, "Prompt each time");
910     if (defaultOption.equalsIgnoreCase("Text"))
911     {
912       comboBox.setSelectedItem(textOpt);
913     }
914     else if (defaultOption.equalsIgnoreCase("Lineart"))
915     {
916       comboBox.setSelectedItem(lineArtOpt);
917     }
918     else
919     {
920       comboBox.setSelectedItem(promptEachTimeOpt);
921     }
922   }
923
924   /**
925    * Save user selections on the Preferences tabs to the Cache and write out to
926    * file.
927    * 
928    * @param e
929    */
930   @Override
931   public void ok_actionPerformed(ActionEvent e)
932   {
933     if (!validateSettings())
934     {
935       return;
936     }
937
938     /* 
939      * Set proxy settings first (to be before web services refresh)
940      */
941     saveProxySettings();
942
943     /*
944      * Save Visual settings
945      */
946     Cache.setPropertyNoSave("SHOW_JVSUFFIX",
947             Boolean.toString(seqLimit.isSelected()));
948     Cache.setPropertyNoSave("RIGHT_ALIGN_IDS",
949             Boolean.toString(rightAlign.isSelected()));
950     Cache.setPropertyNoSave("SHOW_FULLSCREEN",
951             Boolean.toString(fullScreen.isSelected()));
952     Cache.setPropertyNoSave("SHOW_OVERVIEW",
953             Boolean.toString(openoverv.isSelected()));
954     Cache.setPropertyNoSave("SHOW_ANNOTATIONS",
955             Boolean.toString(annotations.isSelected()));
956     Cache.setPropertyNoSave("SHOW_CONSERVATION",
957             Boolean.toString(conservation.isSelected()));
958     Cache.setPropertyNoSave("SHOW_QUALITY",
959             Boolean.toString(quality.isSelected()));
960     Cache.setPropertyNoSave("SHOW_IDENTITY",
961             Boolean.toString(identity.isSelected()));
962
963     Cache.setPropertyNoSave("GAP_SYMBOL",
964             gapSymbolCB.getSelectedItem().toString());
965
966     Cache.setPropertyNoSave("FONT_NAME",
967             fontNameCB.getSelectedItem().toString());
968     Cache.setPropertyNoSave("FONT_STYLE",
969             fontStyleCB.getSelectedItem().toString());
970     Cache.setPropertyNoSave("FONT_SIZE",
971             fontSizeCB.getSelectedItem().toString());
972
973     Cache.setPropertyNoSave("ID_ITALICS",
974             Boolean.toString(idItalics.isSelected()));
975     Cache.setPropertyNoSave("SHOW_UNCONSERVED",
976             Boolean.toString(showUnconserved.isSelected()));
977     Cache.setPropertyNoSave(SHOW_OCCUPANCY,
978             Boolean.toString(showOccupancy.isSelected()));
979     Cache.setPropertyNoSave("SHOW_GROUP_CONSENSUS",
980             Boolean.toString(showGroupConsensus.isSelected()));
981     Cache.setPropertyNoSave("SHOW_GROUP_CONSERVATION",
982             Boolean.toString(showGroupConservation.isSelected()));
983     Cache.setPropertyNoSave("SHOW_CONSENSUS_HISTOGRAM",
984             Boolean.toString(showConsensHistogram.isSelected()));
985     Cache.setPropertyNoSave("SHOW_CONSENSUS_LOGO",
986             Boolean.toString(showConsensLogo.isSelected()));
987     Cache.setPropertyNoSave("SHOW_INFORMATION_HISTOGRAM",
988             Boolean.toString(showConsensHistogram.isSelected()));
989     Cache.setPropertyNoSave("SHOW_HMM_LOGO",
990             Boolean.toString(showHMMLogo.isSelected()));
991     Cache.setPropertyNoSave("ANTI_ALIAS",
992             Boolean.toString(smoothFont.isSelected()));
993     Cache.setPropertyNoSave(SCALE_PROTEIN_TO_CDNA,
994             Boolean.toString(scaleProteinToCdna.isSelected()));
995     Cache.setPropertyNoSave("SHOW_NPFEATS_TOOLTIP",
996             Boolean.toString(showNpTooltip.isSelected()));
997     Cache.setPropertyNoSave("SHOW_DBREFS_TOOLTIP",
998             Boolean.toString(showDbRefTooltip.isSelected()));
999
1000     Cache.setPropertyNoSave("WRAP_ALIGNMENT",
1001             Boolean.toString(wrap.isSelected()));
1002
1003     Cache.setPropertyNoSave("STARTUP_FILE",
1004             startupFileTextfield.getText());
1005     Cache.setPropertyNoSave("SHOW_STARTUP_FILE",
1006             Boolean.toString(startupCheckbox.isSelected()));
1007
1008     Cache.setPropertyNoSave("SORT_ALIGNMENT",
1009             sortby.getSelectedItem().toString());
1010
1011     // convert description of sort order to enum name for save
1012     SequenceAnnotationOrder annSortOrder = SequenceAnnotationOrder
1013             .forDescription(sortAnnBy.getSelectedItem().toString());
1014     if (annSortOrder != null)
1015     {
1016       Cache.setPropertyNoSave(SORT_ANNOTATIONS,
1017               annSortOrder.name());
1018     }
1019
1020     final boolean showAutocalcFirst = sortAutocalc.getSelectedIndex() == 0;
1021     Cache.setPropertyNoSave(SHOW_AUTOCALC_ABOVE,
1022             Boolean.valueOf(showAutocalcFirst).toString());
1023
1024     /*
1025      * Save Colours settings
1026      */
1027     Cache.setPropertyNoSave(DEFAULT_COLOUR_PROT,
1028             protColour.getSelectedItem().toString());
1029     Cache.setPropertyNoSave(DEFAULT_COLOUR_NUC,
1030             nucColour.getSelectedItem().toString());
1031     Cache.setColourPropertyNoSave("ANNOTATIONCOLOUR_MIN",
1032             minColour.getBackground());
1033     Cache.setColourPropertyNoSave("ANNOTATIONCOLOUR_MAX",
1034             maxColour.getBackground());
1035
1036     /*
1037      * Save HMMER settings
1038      */
1039     Cache.setPropertyNoSave(HMMALIGN_TRIM_TERMINI,
1040             Boolean.toString(hmmrTrimTermini.isSelected()));
1041     Cache.setPropertyNoSave(HMMINFO_GLOBAL_BACKGROUND,
1042             Boolean.toString(hmmerBackgroundUniprot.isSelected()));
1043     Cache.setPropertyNoSave(HMMSEARCH_SEQCOUNT,
1044             hmmerSequenceCount.getText());
1045     Cache.setOrRemove(HMMER_PATH, hmmerPath.getText());
1046     if (cygwinPath != null)
1047     {
1048       Cache.setOrRemove(CYGWIN_PATH, cygwinPath.getText());
1049     }
1050     AlignFrame[] frames = Desktop.getAlignFrames();
1051     if (frames != null && frames.length > 0)
1052     {
1053       for (AlignFrame f : frames)
1054       {
1055         f.updateHMMERStatus();
1056       }
1057     }
1058     
1059     hmmrTrimTermini.setSelected(Cache.getDefault(HMMALIGN_TRIM_TERMINI, false));
1060     if (Cache.getDefault(HMMINFO_GLOBAL_BACKGROUND, false))
1061     {
1062       hmmerBackgroundUniprot.setSelected(true);
1063     }
1064     else
1065     {
1066       hmmerBackgroundAlignment.setSelected(true);
1067     }
1068     hmmerSequenceCount
1069             .setText(Cache.getProperty(HMMSEARCH_SEQCOUNT));
1070     hmmerPath.setText(Cache.getProperty(HMMER_PATH));
1071
1072     /*
1073      * Save Overview settings
1074      */
1075     Cache.setColourPropertyNoSave(GAP_COLOUR, gapColour.getBackground());
1076     Cache.setColourPropertyNoSave(HIDDEN_COLOUR, hiddenColour.getBackground());
1077     Cache.setPropertyNoSave(USE_LEGACY_GAP,
1078             Boolean.toString(useLegacyGap.isSelected()));
1079     Cache.setPropertyNoSave(SHOW_OV_HIDDEN_AT_START,
1080             Boolean.toString(showHiddenAtStart.isSelected()));
1081
1082     /*
1083      * Save Structure settings
1084      */
1085     Cache.setPropertyNoSave(ADD_TEMPFACT_ANN,
1086             Boolean.toString(addTempFactor.isSelected()));
1087     Cache.setPropertyNoSave(ADD_SS_ANN,
1088             Boolean.toString(addSecondaryStructure.isSelected()));
1089     Cache.setPropertyNoSave(STRUCT_FROM_PDB,
1090             Boolean.toString(structFromPdb.isSelected()));
1091     if (!Platform.isJS()) {
1092     String viewer = structViewer.getSelectedItem().toString();
1093     String viewerPath = structureViewerPath.getText();
1094     Cache.setPropertyNoSave(STRUCTURE_DISPLAY, viewer);
1095     if (viewer.equals(ViewerType.CHIMERA.name()))
1096     {
1097       Cache.setOrRemove(CHIMERA_PATH, viewerPath);
1098     }
1099     else if (viewer.equals(ViewerType.CHIMERAX.name()))
1100     {
1101       Cache.setOrRemove(CHIMERAX_PATH, viewerPath);
1102     }
1103     else if (viewer.equals(ViewerType.PYMOL.name()))
1104     {
1105       Cache.setOrRemove(PYMOL_PATH, viewerPath);
1106     }
1107     } // nojs
1108     Cache.setPropertyNoSave("MAP_WITH_SIFTS",
1109             Boolean.toString(siftsMapping.isSelected()));
1110     SiftsSettings.setMapWithSifts(siftsMapping.isSelected());
1111
1112     /*
1113      * Save Output settings
1114      */
1115     Cache.setPropertyNoSave("EPS_RENDERING",
1116             ((OptionsParam) epsRendering.getSelectedItem()).getCode());
1117     Cache.setPropertyNoSave("HTML_RENDERING",
1118             ((OptionsParam) htmlRendering.getSelectedItem()).getCode());
1119     Cache.setPropertyNoSave("SVG_RENDERING",
1120             ((OptionsParam) svgRendering.getSelectedItem()).getCode());
1121
1122     /*
1123      * Save Connections settings
1124      */
1125     // Proxy settings were already set first (to catch web services)
1126     Cache.setOrRemove("DEFAULT_BROWSER", defaultBrowser.getText());
1127
1128     jalview.util.BrowserLauncher.resetBrowser();
1129
1130     // save user-defined and selected links
1131     String menuLinks = sequenceUrlLinks.writeUrlsAsString(true);
1132     if (menuLinks.isEmpty())
1133     {
1134       Cache.removePropertyNoSave("SEQUENCE_LINKS");
1135     }
1136     else
1137     {
1138       Cache.setPropertyNoSave("SEQUENCE_LINKS",
1139               menuLinks.toString());
1140     }
1141
1142     String nonMenuLinks = sequenceUrlLinks.writeUrlsAsString(false);
1143     if (nonMenuLinks.isEmpty())
1144     {
1145       Cache.removePropertyNoSave("STORED_LINKS");
1146     }
1147     else
1148     {
1149       Cache.setPropertyNoSave("STORED_LINKS",
1150               nonMenuLinks.toString());
1151     }
1152
1153     Cache.setPropertyNoSave("DEFAULT_URL",
1154             sequenceUrlLinks.getPrimaryUrlId());
1155
1156     Cache.setProperty("VERSION_CHECK",
1157             Boolean.toString(versioncheck.isSelected()));
1158     if (Cache.getProperty("USAGESTATS") != null || usagestats.isSelected())
1159     {
1160       // default is false - we only set this if the user has actively agreed
1161       Cache.setProperty("USAGESTATS",
1162               Boolean.toString(usagestats.isSelected()));
1163     }
1164     if (!questionnaire.isSelected())
1165     {
1166       Cache.setProperty("NOQUESTIONNAIRES", "true");
1167     }
1168     else
1169     {
1170       // special - made easy to edit a property file to disable questionnaires
1171       // by just adding the given line
1172       Cache.removeProperty("NOQUESTIONNAIRES");
1173     }
1174
1175     /*
1176      * Save Output settings
1177      */
1178     Cache.setPropertyNoSave("BLC_JVSUFFIX",
1179             Boolean.toString(blcjv.isSelected()));
1180     Cache.setPropertyNoSave("CLUSTAL_JVSUFFIX",
1181             Boolean.toString(clustaljv.isSelected()));
1182     Cache.setPropertyNoSave("FASTA_JVSUFFIX",
1183             Boolean.toString(fastajv.isSelected()));
1184     Cache.setPropertyNoSave("MSF_JVSUFFIX",
1185             Boolean.toString(msfjv.isSelected()));
1186     Cache.setPropertyNoSave("PFAM_JVSUFFIX",
1187             Boolean.toString(pfamjv.isSelected()));
1188     Cache.setPropertyNoSave("PILEUP_JVSUFFIX",
1189             Boolean.toString(pileupjv.isSelected()));
1190     Cache.setPropertyNoSave("PIR_JVSUFFIX",
1191             Boolean.toString(pirjv.isSelected()));
1192     Cache.setPropertyNoSave("PIR_MODELLER",
1193             Boolean.toString(modellerOutput.isSelected()));
1194     Cache.setPropertyNoSave("EXPORT_EMBBED_BIOJSON",
1195             Boolean.toString(embbedBioJSON.isSelected()));
1196     jalview.io.PIRFile.useModellerOutput = modellerOutput.isSelected();
1197
1198     Cache.setPropertyNoSave("FIGURE_AUTOIDWIDTH",
1199             Boolean.toString(autoIdWidth.isSelected()));
1200     userIdWidth_actionPerformed();
1201     Cache.setPropertyNoSave("FIGURE_FIXEDIDWIDTH",
1202             userIdWidth.getText());
1203
1204     /*
1205      * Save Editing settings
1206      */
1207     Cache.setPropertyNoSave("AUTO_CALC_CONSENSUS",
1208             Boolean.toString(autoCalculateConsCheck.isSelected()));
1209     Cache.setPropertyNoSave("SORT_BY_TREE",
1210             Boolean.toString(sortByTree.isSelected()));
1211     Cache.setPropertyNoSave("PAD_GAPS",
1212             Boolean.toString(padGaps.isSelected()));
1213
1214     if (!Platform.isJS())
1215     {
1216       wsPrefs.updateAndRefreshWsMenuConfig(false);
1217     }
1218
1219     /*
1220      * Save Backups settings
1221      */
1222     Cache.setPropertyNoSave(BackupFiles.ENABLED,
1223             Boolean.toString(enableBackupFiles.isSelected()));
1224     int preset = getComboIntStringKey(backupfilesPresetsCombo);
1225     Cache.setPropertyNoSave(BackupFiles.NS + "_PRESET", Integer.toString(preset));
1226
1227     if (preset == BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM)
1228     {
1229       BackupFilesPresetEntry customBFPE = getBackupfilesCurrentEntry();
1230       BackupFilesPresetEntry.backupfilesPresetEntriesValues.put(
1231               BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM, customBFPE);
1232       Cache.setPropertyNoSave(BackupFilesPresetEntry.CUSTOMCONFIG,
1233                       customBFPE.toString());
1234     }
1235
1236     BackupFilesPresetEntry savedBFPE = BackupFilesPresetEntry.backupfilesPresetEntriesValues
1237             .get(preset);
1238     Cache.setPropertyNoSave(
1239             BackupFilesPresetEntry.SAVEDCONFIG, savedBFPE.toString());
1240
1241     /*
1242      * Save Memory Settings
1243      */
1244     Cache.setPropertyNoSave(
1245             MemorySetting.CUSTOMISED_SETTINGS,
1246             Boolean.toString(customiseMemorySetting.isSelected()));
1247     Cache.setPropertyNoSave(MemorySetting.MEMORY_JVMMEMPC,
1248             Integer.toString(jvmMemoryPercentSlider.getValue()));
1249     Cache.setPropertyNoSave(MemorySetting.MEMORY_JVMMEMMAX,
1250             jvmMemoryMaxTextField.getText());
1251
1252     /*
1253      * save and close Preferences
1254      */
1255     Cache.saveProperties();
1256     Desktop.getInstance().doConfigureStructurePrefs();
1257     try
1258     {
1259       frame.setClosed(true);
1260     } catch (Exception ex)
1261     {
1262     }
1263   }
1264
1265   public void saveProxySettings()
1266   {
1267     String newProxyType = customProxy.isSelected() ? Cache.PROXYTYPE_CUSTOM
1268             : noProxy.isSelected() ? Cache.PROXYTYPE_NONE
1269                     : Cache.PROXYTYPE_SYSTEM;
1270     Cache.setPropertyNoSave("USE_PROXY", newProxyType);
1271     Cache.setOrRemove("PROXY_SERVER", proxyServerHttpTB.getText());
1272     Cache.setOrRemove("PROXY_PORT", proxyPortHttpTB.getText());
1273     Cache.setOrRemove("PROXY_SERVER_HTTPS", proxyServerHttpsTB.getText());
1274     Cache.setOrRemove("PROXY_PORT_HTTPS", proxyPortHttpsTB.getText());
1275     Cache.setOrRemove("PROXY_AUTH",
1276             Boolean.toString(proxyAuth.isSelected()));
1277     Cache.setOrRemove("PROXY_AUTH_USERNAME", proxyAuthUsernameTB.getText());
1278     Cache.proxyAuthPassword = proxyAuthPasswordPB.getPassword();
1279     Cache.setProxyPropertiesFromPreferences(previousProxyType);
1280     if (newProxyType.equals(Cache.PROXYTYPE_CUSTOM)
1281             || !newProxyType.equals(previousProxyType))
1282     {
1283       // force a re-lookup of ws if proxytype is custom or has changed
1284       wsPrefs.refreshWs_actionPerformed(null);
1285     }
1286     previousProxyType = newProxyType;
1287   }
1288
1289   public static void setAppletDefaults()
1290   {
1291
1292     // http://www.jalview.org/old/v2_8/examples/appletParameters.html
1293
1294     // showConservation true or false Default is true.
1295     // showQuality true or false Default is true.
1296     // showConsensus true or false Default is true.
1297     // showFeatureSettings true or false Shows the feature settings window when
1298     // startin
1299     // showTreeBootstraps true or false (default is true) show or hide branch
1300     // bootstraps
1301     // showTreeDistances true or false (default is true) show or hide branch
1302     // lengths
1303     // showUnlinkedTreeNodes true or false (default is false) indicate if
1304     // unassociated nodes should be highlighted in the tree view
1305     // showUnconserved true of false (default is false) When true, only gaps and
1306     // symbols different to the consensus sequence ions of the alignment
1307     // showGroupConsensus true of false (default is false) When true, shows
1308     // consensus annotation row for any groups on the alignment. (since 2.7)
1309     // showGroupConservation true of false (default is false) When true, shows
1310     // amino-acid property conservation annotation row for any groups on the
1311     // showConsensusHistogram true of false (default is true) When true, shows
1312     // the percentage occurence of the consensus symbol for each column as a
1313     // showSequenceLogo true of false (default is false) When true, shows a
1314     // sequence logo above the consensus sequence (overlaid above the Consensus
1315
1316     Cache.setPropertyNoSave(SHOW_CONSERVATION, "true");
1317     Cache.setPropertyNoSave(SHOW_QUALITY, "false");
1318     Cache.setPropertyNoSave(SHOW_CONSENSUS, "true");
1319     Cache.setPropertyNoSave(SHOW_UNCONSERVED, "false");
1320     Cache.setPropertyNoSave(SHOW_GROUP_CONSERVATION, "false");
1321     Cache.setPropertyNoSave(SHOW_GROUP_CONSENSUS, "false");
1322
1323     // TODO -- just a start here
1324   }
1325  /**
1326    * Do any necessary validation before saving settings. Return focus to the
1327    * first tab which fails validation.
1328    * 
1329    * @return
1330    */
1331   private boolean validateSettings()
1332   {
1333     if (!validateStructure())
1334     {
1335       structureTab.requestFocusInWindow();
1336       return false;
1337     }
1338     return true;
1339   }
1340
1341   @Override
1342   protected boolean validateStructure()
1343   {
1344     return validateViewerPath();
1345
1346   }
1347
1348   /**
1349    * DOCUMENT ME!
1350    */
1351   @Override
1352   public void startupFileTextfield_mouseClicked()
1353   {
1354     // TODO: JAL-3048 not needed for Jalview-JS
1355     String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1356     JalviewFileChooser chooser = JalviewFileChooser
1357             .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat);
1358     chooser.setFileView(new JalviewFileView());
1359     chooser.setDialogTitle(
1360             MessageManager.getString("label.select_startup_file"));
1361
1362     int value = chooser.showOpenDialog(this);
1363
1364     if (value == JalviewFileChooser.APPROVE_OPTION)
1365     {
1366       FileFormatI format = chooser.getSelectedFormat();
1367       if (format != null)
1368       {
1369         Cache.setPropertyNoSave("DEFAULT_FILE_FORMAT",
1370                 format.getName());
1371       }
1372       startupFileTextfield
1373               .setText(chooser.getSelectedFile().getAbsolutePath());
1374     }
1375   }
1376
1377   /**
1378    * DOCUMENT ME!
1379    * 
1380    * @param e
1381    *          DOCUMENT ME!
1382    */
1383   @Override
1384   public void cancel_actionPerformed(ActionEvent e)
1385   {
1386     try
1387     {
1388       if (!Platform.isJS())
1389       {
1390         wsPrefs.updateWsMenuConfig(true);
1391         wsPrefs.refreshWs_actionPerformed(e);
1392       }
1393       frame.setClosed(true);
1394     } catch (Exception ex)
1395     {
1396     }
1397   }
1398
1399   /**
1400    * DOCUMENT ME!
1401    * 
1402    * @param e
1403    *          DOCUMENT ME!
1404    */
1405   @Override
1406   public void annotations_actionPerformed(ActionEvent e)
1407   {
1408     conservation.setEnabled(annotations.isSelected());
1409     quality.setEnabled(annotations.isSelected());
1410     identity.setEnabled(annotations.isSelected());
1411     showOccupancy.setEnabled(annotations.isSelected());
1412     showGroupConsensus.setEnabled(annotations.isSelected());
1413     showGroupConservation.setEnabled(annotations.isSelected());
1414     showConsensHistogram.setEnabled(annotations.isSelected()
1415             && (identity.isSelected() || showGroupConsensus.isSelected()));
1416     showConsensLogo.setEnabled(annotations.isSelected()
1417             && (identity.isSelected() || showGroupConsensus.isSelected()));
1418     showInformationHistogram.setEnabled(annotations.isSelected());
1419     showHMMLogo.setEnabled(annotations.isSelected());
1420   }
1421
1422   @Override
1423   public void newLink_actionPerformed(ActionEvent e)
1424   {
1425     GSequenceLink link = new GSequenceLink();
1426     boolean valid = false;
1427     while (!valid)
1428     {
1429       if (JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(), link,
1430               MessageManager.getString("label.new_sequence_url_link"),
1431               JvOptionPane.OK_CANCEL_OPTION, -1,
1432               null) == JvOptionPane.OK_OPTION)
1433       {
1434         if (link.checkValid())
1435         {
1436           if (((UrlLinkTableModel) linkUrlTable.getModel())
1437                   .isUniqueName(link.getName()))
1438           {
1439             ((UrlLinkTableModel) linkUrlTable.getModel())
1440                     .insertRow(link.getName(), link.getURL());
1441             valid = true;
1442           }
1443           else
1444           {
1445             link.notifyDuplicate();
1446             continue;
1447           }
1448         }
1449       }
1450       else
1451       {
1452         break;
1453       }
1454     }
1455   }
1456
1457   @Override
1458   public void editLink_actionPerformed(ActionEvent e)
1459   {
1460     GSequenceLink link = new GSequenceLink();
1461
1462     int index = linkUrlTable.getSelectedRow();
1463     if (index == -1)
1464     {
1465       // button no longer enabled if row is not selected
1466       Console.debug("Edit with no row selected in linkUrlTable");
1467       return;
1468     }
1469
1470     int nameCol = ((UrlLinkTableModel) linkUrlTable.getModel())
1471             .getNameColumn();
1472     int urlCol = ((UrlLinkTableModel) linkUrlTable.getModel())
1473             .getUrlColumn();
1474     String oldName = linkUrlTable.getValueAt(index, nameCol).toString();
1475     link.setName(oldName);
1476     link.setURL(linkUrlTable.getValueAt(index, urlCol).toString());
1477
1478     boolean valid = false;
1479     while (!valid)
1480     {
1481       if (JvOptionPane.showInternalConfirmDialog(Desktop.getDesktopPane(), link,
1482               MessageManager.getString("label.edit_sequence_url_link"),
1483               JvOptionPane.OK_CANCEL_OPTION, -1,
1484               null) == JvOptionPane.OK_OPTION)
1485       {
1486         if (link.checkValid())
1487         {
1488           if ((oldName.equals(link.getName()))
1489                   || (((UrlLinkTableModel) linkUrlTable.getModel())
1490                           .isUniqueName(link.getName())))
1491           {
1492             linkUrlTable.setValueAt(link.getName(), index, nameCol);
1493             linkUrlTable.setValueAt(link.getURL(), index, urlCol);
1494             valid = true;
1495           }
1496           else
1497           {
1498             link.notifyDuplicate();
1499             continue;
1500           }
1501         }
1502       }
1503       else
1504       {
1505         break;
1506       }
1507     }
1508   }
1509
1510   @Override
1511   public void deleteLink_actionPerformed(ActionEvent e)
1512   {
1513     int index = linkUrlTable.getSelectedRow();
1514     int modelIndex = -1;
1515     if (index == -1)
1516     {
1517       // button no longer enabled if row is not selected
1518       Console.debug("Delete with no row selected in linkUrlTable");
1519       return;
1520     }
1521     else
1522     {
1523       modelIndex = linkUrlTable.convertRowIndexToModel(index);
1524     }
1525
1526     // make sure we use the model index to delete, and not the table index
1527     ((UrlLinkTableModel) linkUrlTable.getModel()).removeRow(modelIndex);
1528   }
1529
1530   @Override
1531   public void defaultBrowser_mouseClicked(MouseEvent e)
1532   {
1533     // TODO: JAL-3048 not needed for j2s
1534     if (!Platform.isJS()) // BH 2019
1535     /**
1536      * Java only
1537      * 
1538      * @j2sIgnore
1539      */
1540     {
1541       JFileChooser chooser = new JFileChooser(".");
1542       chooser.setDialogTitle(
1543               MessageManager.getString("label.select_default_browser"));
1544
1545       int value = chooser.showOpenDialog(this);
1546
1547       if (value == JFileChooser.APPROVE_OPTION)
1548       {
1549         defaultBrowser.setText(chooser.getSelectedFile().getAbsolutePath());
1550       }
1551     }
1552   }
1553
1554   /*
1555    * (non-Javadoc)
1556    * 
1557    * @see
1558    * jalview.jbgui.GPreferences#showunconserved_actionPerformed(java.awt.event
1559    * .ActionEvent)
1560    */
1561   @Override
1562   protected void showunconserved_actionPerformed(ActionEvent e)
1563   {
1564     // TODO Auto-generated method stub
1565     super.showunconserved_actionPerformed(e);
1566   }
1567
1568   public static List<String> getGroupURLLinks()
1569   {
1570     return groupURLLinks;
1571   }
1572
1573   @Override
1574   public void minColour_actionPerformed(JPanel panel)
1575   {
1576     JalviewColourChooser.showColourChooser(this,
1577             MessageManager.getString("label.select_colour_minimum_value"),
1578             panel);
1579   }
1580
1581   @Override
1582   public void maxColour_actionPerformed(JPanel panel)
1583   {
1584     JalviewColourChooser.showColourChooser(this,
1585             MessageManager.getString("label.select_colour_maximum_value"),
1586             panel);
1587   }
1588
1589   @Override
1590   public void gapColour_actionPerformed(JPanel gap)
1591   {
1592     if (!useLegacyGap.isSelected())
1593     {
1594       JalviewColourChooser.showColourChooser(this,
1595               MessageManager.getString("label.select_gap_colour"),
1596               gap);
1597     }
1598   }
1599
1600   @Override
1601   public void hiddenColour_actionPerformed(JPanel hidden)
1602   {
1603     JalviewColourChooser.showColourChooser(this,
1604             MessageManager.getString("label.select_hidden_colour"),
1605             hidden);
1606   }
1607
1608   @Override
1609   protected void useLegacyGaps_actionPerformed(ActionEvent e)
1610   {
1611     boolean enabled = useLegacyGap.isSelected();
1612     if (enabled)
1613     {
1614       gapColour.setBackground(
1615               jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_LEGACY_GAP);
1616     }
1617     else
1618     {
1619       gapColour.setBackground(
1620               jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP);
1621     }
1622     gapColour.setEnabled(!enabled);
1623     gapLabel.setEnabled(!enabled);
1624   }
1625
1626   @Override
1627   protected void resetOvDefaults_actionPerformed(ActionEvent e)
1628   {
1629     useLegacyGap.setSelected(false);
1630     useLegacyGaps_actionPerformed(null);
1631     showHiddenAtStart.setSelected(false);
1632     hiddenColour.setBackground(
1633             jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_HIDDEN);
1634   }
1635
1636   @Override
1637   protected void userIdWidth_actionPerformed()
1638   {
1639     try
1640     {
1641       String val = userIdWidth.getText().trim();
1642       if (val.length() > 0)
1643       {
1644         Integer iw = Integer.parseInt(val);
1645         if (iw.intValue() < 12)
1646         {
1647           throw new NumberFormatException();
1648         }
1649         userIdWidth.setText(iw.toString());
1650       }
1651     } catch (NumberFormatException x)
1652     {
1653       userIdWidth.setText("");
1654       JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1655               MessageManager
1656                       .getString("warn.user_defined_width_requirements"),
1657               MessageManager.getString("label.invalid_id_column_width"),
1658               JvOptionPane.WARNING_MESSAGE);
1659     }
1660   }
1661
1662   @Override
1663   protected void autoIdWidth_actionPerformed()
1664   {
1665     userIdWidth.setEnabled(!autoIdWidth.isSelected());
1666     userIdWidthlabel.setEnabled(!autoIdWidth.isSelected());
1667   }
1668
1669   /**
1670    * Returns true if structure viewer path is to a valid executable, else shows an
1671    * error dialog. Does nothing if the path is empty, as is the case for Jmol
1672    * (built in to Jalview) or when Jalview is left to try default paths.
1673    */
1674   private boolean validateViewerPath()
1675   {
1676     if (structureViewerPath.getText().trim().length() > 0)
1677     {
1678       File f = new File(structureViewerPath.getText());
1679       if (!f.canExecute())
1680       {
1681         JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1682                 MessageManager.getString("label.invalid_viewer_path"),
1683                 MessageManager.getString("label.invalid_viewer_path"),
1684                 JvOptionPane.ERROR_MESSAGE);
1685         return false;
1686       }
1687     }
1688     return true;
1689   }
1690   
1691   /**
1692    * Returns true if the given text field contains a path to a folder that
1693    * contains an executable with the given name, else false (after showing a
1694    * warning dialog). The executable name will be tried with .exe appended if not
1695    * found.
1696    * 
1697    * @param textField
1698    * @param executable
1699    */
1700   protected boolean validateExecutablePath(JTextField textField, String executable)
1701   {
1702     String folder = textField.getText().trim();
1703
1704     if (FileUtils.getExecutable(executable, folder) != null)
1705     {
1706       return true;
1707     }
1708     if (folder.length() > 0)
1709     {
1710       JvOptionPane.showInternalMessageDialog(Desktop.getInstance(),
1711               MessageManager.formatMessage("label.executable_not_found",
1712                       executable),
1713               MessageManager.getString("label.invalid_folder"),
1714               JvOptionPane.ERROR_MESSAGE);
1715     }
1716     return false;
1717   }
1718
1719   /**
1720    * Checks if a file can be executed
1721    * 
1722    * @param path
1723    *          the path to the file
1724    * @return
1725    */
1726   public boolean canExecute(String path)
1727   {
1728     File file = new File(path);
1729     if (!file.canExecute())
1730     {
1731       file = new File(path + ".exe");
1732       {
1733         if (!file.canExecute())
1734         {
1735           return false;
1736         }
1737       }
1738     }
1739     return true;
1740   }
1741
1742   /**
1743    * If Chimera or ChimeraX or Pymol is selected, check it can be found on default
1744    * or user-specified path, if not show a warning/help dialog
1745    */
1746   @Override
1747   protected void structureViewer_actionPerformed(String selectedItem)
1748   {
1749     if (selectedItem.equals(ViewerType.JMOL.name()))
1750     {
1751       structureViewerPath.setEnabled(false);
1752       structureViewerPathLabel.setEnabled(false);
1753       return;
1754     }
1755     boolean found = false;
1756     structureViewerPath.setEnabled(true);
1757     structureViewerPathLabel.setEnabled(true);
1758     structureViewerPathLabel.setText(MessageManager
1759             .formatMessage("label.viewer_path", selectedItem));
1760
1761     /*
1762      * Try user-specified and standard paths for structure viewer executable
1763      */
1764     String viewerPath = "";
1765     List<String> paths = null;
1766     try
1767     {
1768       ViewerType viewerType = ViewerType.valueOf(selectedItem);
1769       switch (viewerType)
1770       {
1771       case JMOL:
1772         // dealt with above
1773         break;
1774       case CHIMERA:
1775         viewerPath = Cache.getDefault(CHIMERA_PATH, "");
1776         paths = StructureManager.getChimeraPaths(false);
1777         break;
1778       case CHIMERAX:
1779         viewerPath = Cache.getDefault(CHIMERAX_PATH, "");
1780         paths = StructureManager.getChimeraPaths(true);
1781         break;
1782       case PYMOL:
1783         viewerPath = Cache.getDefault(PYMOL_PATH, "");
1784         paths = PymolManager.getPymolPaths();
1785         break;
1786       }
1787     } catch (IllegalArgumentException e)
1788     {
1789       // only valid entries should be in the drop-down
1790     }
1791     structureViewerPath.setText(viewerPath);
1792
1793     paths.add(0, structureViewerPath.getText());
1794     for (String path : paths)
1795     {
1796       if (new File(path.trim()).canExecute())
1797       {
1798         found = true;
1799         break;
1800       }
1801     }
1802
1803     if (!found)
1804     {
1805       String[] options = { "OK", "Help" };
1806       int showHelp = JvOptionPane.showInternalOptionDialog(Desktop.getDesktopPane(),
1807               JvSwingUtils.wrapTooltip(true,
1808                       MessageManager.getString("label.viewer_missing")),
1809               "", JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
1810               null, options, options[0]);
1811
1812       if (showHelp == JvOptionPane.NO_OPTION)
1813       {
1814         this.selectTab(Preferences.TabRef.STRUCTURE_TAB, null);
1815         try
1816         {
1817           Help.showHelpWindow(HelpId.StructureViewer);
1818         } catch (HelpSetException e)
1819         {
1820           e.printStackTrace();
1821         }
1822       }
1823       else if (showHelp == JvOptionPane.OK_OPTION)
1824       {
1825         this.selectTab(Preferences.TabRef.STRUCTURE_TAB, null);
1826         CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> {
1827           try
1828           {
1829             for (int i = 0; i < 3; i++)
1830             {
1831               structureViewerPath.setBackground(Color.PINK);
1832               Thread.sleep(500);
1833               structureViewerPath.setBackground(Color.WHITE);
1834               Thread.sleep(500);
1835             }
1836           } catch (InterruptedException e)
1837           {
1838           }
1839         });
1840       }
1841      }
1842   }
1843
1844   @Override
1845   protected void validateHmmerPath()
1846   {
1847     validateExecutablePath(hmmerPath, HmmerCommand.HMMBUILD);
1848   }
1849
1850   @Override
1851   protected void validateCygwinPath()
1852   {
1853     validateExecutablePath(cygwinPath, "run");
1854   }
1855
1856   public class OptionsParam
1857   {
1858     private String name;
1859
1860     private String code;
1861
1862     public OptionsParam(String name, String code)
1863     {
1864       this.name = name;
1865       this.code = code;
1866     }
1867
1868     public String getName()
1869     {
1870       return name;
1871     }
1872
1873     public void setName(String name)
1874     {
1875       this.name = name;
1876     }
1877
1878     public String getCode()
1879     {
1880       return code;
1881     }
1882
1883     public void setCode(String code)
1884     {
1885       this.code = code;
1886     }
1887
1888     @Override
1889     public String toString()
1890     {
1891       return name;
1892     }
1893
1894     @Override
1895     public boolean equals(Object that)
1896     {
1897       if (!(that instanceof OptionsParam))
1898       {
1899         return false;
1900       }
1901       return this.code.equalsIgnoreCase(((OptionsParam) that).code);
1902     }
1903
1904     @Override
1905     public int hashCode()
1906     {
1907       return name.hashCode() + code.hashCode();
1908     }
1909   }
1910
1911   private class UrlListSelectionHandler implements ListSelectionListener
1912   {
1913
1914     @Override
1915     public void valueChanged(ListSelectionEvent e)
1916     {
1917       ListSelectionModel lsm = (ListSelectionModel) e.getSource();
1918
1919       int index = lsm.getMinSelectionIndex();
1920       if (index == -1)
1921       {
1922         // no selection, so disable delete/edit buttons
1923         editLink.setEnabled(false);
1924         deleteLink.setEnabled(false);
1925         return;
1926       }
1927       int modelIndex = linkUrlTable.convertRowIndexToModel(index);
1928
1929       // enable/disable edit and delete link buttons
1930       if (((UrlLinkTableModel) linkUrlTable.getModel())
1931               .isRowDeletable(modelIndex))
1932       {
1933         deleteLink.setEnabled(true);
1934       }
1935       else
1936       {
1937         deleteLink.setEnabled(false);
1938       }
1939
1940       if (((UrlLinkTableModel) linkUrlTable.getModel())
1941               .isRowEditable(modelIndex))
1942       {
1943         editLink.setEnabled(true);
1944       }
1945       else
1946       {
1947         editLink.setEnabled(false);
1948       }
1949     }
1950   }
1951 }