JAL-3503 Added a Startup tab in Preferences, with gui for JVMMEMMAX and JVMMEMPC...
[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.MouseEvent;
31 import java.io.File;
32 import java.util.ArrayList;
33 import java.util.List;
34
35 import javax.help.HelpSetException;
36 import javax.swing.JComboBox;
37 import javax.swing.JFileChooser;
38 import javax.swing.JInternalFrame;
39 import javax.swing.JPanel;
40 import javax.swing.ListSelectionModel;
41 import javax.swing.RowFilter;
42 import javax.swing.RowSorter;
43 import javax.swing.SortOrder;
44 import javax.swing.event.DocumentEvent;
45 import javax.swing.event.DocumentListener;
46 import javax.swing.event.ListSelectionEvent;
47 import javax.swing.event.ListSelectionListener;
48 import javax.swing.table.TableCellRenderer;
49 import javax.swing.table.TableColumn;
50 import javax.swing.table.TableModel;
51 import javax.swing.table.TableRowSorter;
52
53 import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
54 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
55 import jalview.bin.Cache;
56 import jalview.bin.MemorySetting;
57 import jalview.ext.pymol.PymolManager;
58 import jalview.gui.Help.HelpId;
59 import jalview.gui.StructureViewer.ViewerType;
60 import jalview.io.BackupFiles;
61 import jalview.io.BackupFilesPresetEntry;
62 import jalview.io.FileFormatI;
63 import jalview.io.JalviewFileChooser;
64 import jalview.io.JalviewFileView;
65 import jalview.jbgui.GPreferences;
66 import jalview.jbgui.GSequenceLink;
67 import jalview.schemes.ColourSchemeI;
68 import jalview.schemes.ColourSchemes;
69 import jalview.schemes.ResidueColourScheme;
70 import jalview.urls.UrlLinkTableModel;
71 import jalview.urls.api.UrlProviderFactoryI;
72 import jalview.urls.api.UrlProviderI;
73 import jalview.urls.desktop.DesktopUrlProviderFactory;
74 import jalview.util.MessageManager;
75 import jalview.util.Platform;
76 import jalview.util.UrlConstants;
77 import jalview.ws.sifts.SiftsSettings;
78
79 /**
80  * DOCUMENT ME!
81  * 
82  * @author $author$
83  * @version $Revision$
84  */
85 /*
86  * for merge with Jalview-JS
87  public class Preferences extends GPreferences implements ApplicationSingletonI
88  */
89 public class Preferences extends GPreferences
90 {
91   public static final String ENABLE_SPLIT_FRAME = "ENABLE_SPLIT_FRAME";
92
93   public static final String SCALE_PROTEIN_TO_CDNA = "SCALE_PROTEIN_TO_CDNA";
94
95   public static final String DEFAULT_COLOUR = "DEFAULT_COLOUR";
96
97   public static final String DEFAULT_COLOUR_PROT = "DEFAULT_COLOUR_PROT";
98
99   public static final String DEFAULT_COLOUR_NUC = "DEFAULT_COLOUR_NUC";
100
101   public static final String ADD_TEMPFACT_ANN = "ADD_TEMPFACT_ANN";
102
103   public static final String ADD_SS_ANN = "ADD_SS_ANN";
104
105   public static final String USE_RNAVIEW = "USE_RNAVIEW";
106
107   public static final String STRUCT_FROM_PDB = "STRUCT_FROM_PDB";
108
109   public static final String STRUCTURE_DISPLAY = "STRUCTURE_DISPLAY";
110
111   public static final String CHIMERA_PATH = "CHIMERA_PATH";
112
113   public static final String CHIMERAX_PATH = "CHIMERAX_PATH";
114
115   public static final String PYMOL_PATH = "PYMOL_PATH";
116
117   public static final String SORT_ANNOTATIONS = "SORT_ANNOTATIONS";
118
119   public static final String SHOW_AUTOCALC_ABOVE = "SHOW_AUTOCALC_ABOVE";
120
121   public static final String SHOW_OCCUPANCY = "SHOW_OCCUPANCY";
122
123   public static final String SHOW_OV_HIDDEN_AT_START = "SHOW_OV_HIDDEN_AT_START";
124
125   public static final String USE_LEGACY_GAP = "USE_LEGACY_GAP";
126
127   public static final String GAP_COLOUR = "GAP_COLOUR";
128
129   public static final String HIDDEN_COLOUR = "HIDDEN_COLOUR";
130
131   private static final int MIN_FONT_SIZE = 1;
132
133   private static final int MAX_FONT_SIZE = 30;
134
135   private String previousProxyType;
136
137   private static Preferences INSTANCE = null; // add "final"
138
139   /**
140    * Holds name and link separated with | character. Sequence ID must be
141    * $SEQUENCE_ID$ or $SEQUENCE_ID=/.possible | chars ./=$
142    */
143   public static UrlProviderI sequenceUrlLinks;
144
145   public static UrlLinkTableModel dataModel;
146
147   /**
148    * Holds name and link separated with | character. Sequence IDS and Sequences
149    * must be $SEQUENCEIDS$ or $SEQUENCEIDS=/.possible | chars ./=$ and
150    * $SEQUENCES$ or $SEQUENCES=/.possible | chars ./=$ and separation character
151    * for first and second token specified after a pipe character at end |,|.
152    * (TODO: proper escape for using | to separate ids or sequences
153    */
154
155   public static List<String> groupURLLinks;
156   static
157   {
158     // get links selected to be in the menu (SEQUENCE_LINKS)
159     // and links entered by the user but not selected (STORED_LINKS)
160     String inMenuString = Cache.getDefault("SEQUENCE_LINKS", "");
161     String notInMenuString = Cache.getDefault("STORED_LINKS", "");
162     String defaultUrl = Cache.getDefault("DEFAULT_URL",
163             UrlConstants.DEFAULT_LABEL);
164
165     // if both links lists are empty, add the DEFAULT_URL link
166     // otherwise we assume the default link is in one of the lists
167     if (inMenuString.isEmpty() && notInMenuString.isEmpty())
168     {
169       inMenuString = UrlConstants.DEFAULT_STRING;
170     }
171     UrlProviderFactoryI factory = new DesktopUrlProviderFactory(defaultUrl,
172             inMenuString, notInMenuString);
173     sequenceUrlLinks = factory.createUrlProvider();
174     dataModel = new UrlLinkTableModel(sequenceUrlLinks);
175
176     /**
177      * TODO: reformulate groupURL encoding so two or more can be stored in the
178      * .properties file as '|' separated strings
179      */
180
181     groupURLLinks = new ArrayList<>();
182   }
183
184   JInternalFrame frame;
185
186   private WsPreferences wsPrefs;
187
188   private OptionsParam promptEachTimeOpt = new OptionsParam(
189           MessageManager.getString("label.prompt_each_time"),
190           "Prompt each time");
191
192   private OptionsParam lineArtOpt = new OptionsParam(
193           MessageManager.getString("label.lineart"), "Lineart");
194
195   private OptionsParam textOpt = new OptionsParam(
196           MessageManager.getString("action.text"), "Text");
197
198   // get singleton Preferences instance
199   public static Preferences getInstance()
200   {
201     if (INSTANCE == null || INSTANCE.frame == null
202             || INSTANCE.frame.isClosed())
203     {
204       INSTANCE = new Preferences();
205     }
206     return INSTANCE;
207
208     /*
209      * Replace code with the following for Jalvew-JS
210     Preferences INSTANCE = ApplicationSingletonProvider.getInstance(Preferences.class);
211     if (INSTANCE == null || INSTANCE.frame == null
212             || INSTANCE.frame.isClosed())
213     {
214       ApplicationSingletonProvider.remove(Preferences.class);
215       INSTANCE = ApplicationSingletonProvider.getInstance(Preferences.class);
216     }
217     return INSTANCE;
218     */
219   }
220
221   public static void openPreferences()
222   {
223     openPreferences(0, null);
224   }
225
226   public static void openPreferences(int selectTab, String message)
227   {
228     Preferences p = getInstance();
229     p.selectTab(selectTab);
230     p.setMessage(message);
231     p.frame.show();
232     p.frame.moveToFront();
233     p.frame.grabFocus();
234   }
235
236   /**
237    * Creates a new Preferences object.
238    */
239   private Preferences()
240   {
241     super();
242     frame = new JInternalFrame();
243     frame.setContentPane(this);
244     if (!Platform.isJS())
245     /**
246      * Java only
247      * 
248      * @j2sIgnore
249      */
250     {
251       wsPrefs = new WsPreferences();
252       wsTab.add(wsPrefs, BorderLayout.CENTER);
253     }
254     int width = 500, height = 450;
255     if (Platform.isAMacAndNotJS())
256     {
257       width = 570;
258       height = 480;
259     }
260
261     Desktop.addInternalFrame(frame,
262             MessageManager.getString("label.preferences"), width, height);
263     frame.setMinimumSize(new Dimension(width, height));
264
265     /*
266      * Set Visual tab defaults
267      */
268     seqLimit.setSelected(Cache.getDefault("SHOW_JVSUFFIX", true));
269     rightAlign.setSelected(Cache.getDefault("RIGHT_ALIGN_IDS", false));
270     fullScreen.setSelected(Cache.getDefault("SHOW_FULLSCREEN", false));
271     annotations.setSelected(Cache.getDefault("SHOW_ANNOTATIONS", true));
272
273     conservation.setSelected(Cache.getDefault("SHOW_CONSERVATION", true));
274     quality.setSelected(Cache.getDefault("SHOW_QUALITY", true));
275     identity.setSelected(Cache.getDefault("SHOW_IDENTITY", true));
276     openoverv.setSelected(Cache.getDefault("SHOW_OVERVIEW", false));
277     showUnconserved
278             .setSelected(Cache.getDefault("SHOW_UNCONSERVED", false));
279     showOccupancy.setSelected(Cache.getDefault(SHOW_OCCUPANCY, false));
280     showGroupConsensus
281             .setSelected(Cache.getDefault("SHOW_GROUP_CONSENSUS", false));
282     showGroupConservation.setSelected(
283             Cache.getDefault("SHOW_GROUP_CONSERVATION", false));
284     showConsensHistogram.setSelected(
285             Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM", true));
286     showConsensLogo
287             .setSelected(Cache.getDefault("SHOW_CONSENSUS_LOGO", false));
288     showNpTooltip
289             .setSelected(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true));
290     showDbRefTooltip
291             .setSelected(Cache.getDefault("SHOW_DBREFS_TOOLTIP", true));
292
293     String[] fonts = java.awt.GraphicsEnvironment
294             .getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
295     for (int i = 0; i < fonts.length; i++)
296     {
297       fontNameCB.addItem(fonts[i]);
298     }
299
300     for (int i = MIN_FONT_SIZE; i <= MAX_FONT_SIZE; i++)
301     {
302       fontSizeCB.addItem(i + "");
303     }
304
305     fontStyleCB.addItem("plain");
306     fontStyleCB.addItem("bold");
307     fontStyleCB.addItem("italic");
308
309     fontNameCB.setSelectedItem(Cache.getDefault("FONT_NAME", "SansSerif"));
310     fontSizeCB.setSelectedItem(Cache.getDefault("FONT_SIZE", "10"));
311     fontStyleCB.setSelectedItem(
312             Cache.getDefault("FONT_STYLE", Font.PLAIN + ""));
313
314     smoothFont.setSelected(Cache.getDefault("ANTI_ALIAS", true));
315     scaleProteinToCdna
316             .setSelected(Cache.getDefault(SCALE_PROTEIN_TO_CDNA, false));
317
318     idItalics.setSelected(Cache.getDefault("ID_ITALICS", true));
319
320     wrap.setSelected(Cache.getDefault("WRAP_ALIGNMENT", false));
321
322     gapSymbolCB.addItem("-");
323     gapSymbolCB.addItem(".");
324
325     gapSymbolCB.setSelectedItem(Cache.getDefault("GAP_SYMBOL", "-"));
326
327     sortby.addItem("No sort");
328     sortby.addItem("Id");
329     sortby.addItem("Pairwise Identity");
330     sortby.setSelectedItem(Cache.getDefault("SORT_ALIGNMENT", "No sort"));
331
332     sortAnnBy.addItem(SequenceAnnotationOrder.NONE.toString());
333     sortAnnBy
334             .addItem(SequenceAnnotationOrder.SEQUENCE_AND_LABEL.toString());
335     sortAnnBy
336             .addItem(SequenceAnnotationOrder.LABEL_AND_SEQUENCE.toString());
337     SequenceAnnotationOrder savedSort = SequenceAnnotationOrder
338             .valueOf(Cache.getDefault(SORT_ANNOTATIONS,
339                     SequenceAnnotationOrder.NONE.name()));
340     sortAnnBy.setSelectedItem(savedSort.toString());
341
342     sortAutocalc.addItem("Autocalculated first");
343     sortAutocalc.addItem("Autocalculated last");
344     final boolean showAbove = Cache.getDefault(SHOW_AUTOCALC_ABOVE, true);
345     sortAutocalc.setSelectedItem(showAbove ? sortAutocalc.getItemAt(0)
346             : sortAutocalc.getItemAt(1));
347     startupCheckbox
348             .setSelected(Cache.getDefault("SHOW_STARTUP_FILE", true));
349     startupFileTextfield.setText(Cache.getDefault("STARTUP_FILE",
350             Cache.getDefault("www.jalview.org", "http://www.jalview.org")
351                     + "/examples/exampleFile_2_3.jar"));
352
353     /*
354      * Set Colours tab defaults
355      */
356     protColour.addItem(ResidueColourScheme.NONE);
357     nucColour.addItem(ResidueColourScheme.NONE);
358     for (ColourSchemeI cs : ColourSchemes.getInstance().getColourSchemes())
359     {
360       String name = cs.getSchemeName();
361       protColour.addItem(name);
362       nucColour.addItem(name);
363     }
364     String oldProp = Cache.getDefault(DEFAULT_COLOUR,
365             ResidueColourScheme.NONE);
366     String newProp = Cache.getDefault(DEFAULT_COLOUR_PROT, null);
367     protColour.setSelectedItem(newProp != null ? newProp : oldProp);
368     newProp = Cache.getDefault(DEFAULT_COLOUR_NUC, null);
369     nucColour.setSelectedItem(newProp != null ? newProp : oldProp);
370     minColour.setBackground(
371             Cache.getDefaultColour("ANNOTATIONCOLOUR_MIN", Color.orange));
372     maxColour.setBackground(
373             Cache.getDefaultColour("ANNOTATIONCOLOUR_MAX", Color.red));
374
375     /*
376      * Set overview panel defaults
377      */
378     gapColour.setBackground(Cache.getDefaultColour(GAP_COLOUR,
379             jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP));
380     hiddenColour.setBackground(Cache.getDefaultColour(HIDDEN_COLOUR,
381             jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_HIDDEN));
382     useLegacyGap.setSelected(Cache.getDefault(USE_LEGACY_GAP, false));
383     gapLabel.setEnabled(!useLegacyGap.isSelected());
384     gapColour.setEnabled(!useLegacyGap.isSelected());
385     showHiddenAtStart
386             .setSelected(Cache.getDefault(SHOW_OV_HIDDEN_AT_START, false));
387
388     /*
389      * Set Structure tab defaults
390      */
391     final boolean structSelected = Cache.getDefault(STRUCT_FROM_PDB, false);
392     structFromPdb.setSelected(structSelected);
393     useRnaView.setSelected(Cache.getDefault(USE_RNAVIEW, false));
394     useRnaView.setEnabled(structSelected);
395     addSecondaryStructure.setSelected(Cache.getDefault(ADD_SS_ANN, false));
396     addSecondaryStructure.setEnabled(structSelected);
397     addTempFactor.setSelected(Cache.getDefault(ADD_TEMPFACT_ANN, false));
398     addTempFactor.setEnabled(structSelected);
399
400     /*
401      * set choice of structure viewer, and path if saved as a preference;
402      * default to Jmol (first choice) if an unexpected value is found
403      */
404     String viewerType = Cache.getDefault(STRUCTURE_DISPLAY,
405             ViewerType.JMOL.name());
406     structViewer.setSelectedItem(viewerType);
407     String viewerPath = "";
408     ViewerType type = null;
409     try
410     {
411       type = ViewerType.valueOf(viewerType);
412       switch (type)
413       {
414       case JMOL:
415         break;
416       case CHIMERA:
417         viewerPath = Cache.getDefault(CHIMERA_PATH, "");
418         break;
419       case CHIMERAX:
420         viewerPath = Cache.getDefault(CHIMERAX_PATH, "");
421         break;
422       case PYMOL:
423         viewerPath = Cache.getDefault(PYMOL_PATH, "");
424         break;
425       }
426     } catch (IllegalArgumentException e)
427     {
428       Cache.log.error("Unknown structure viewer type: " + viewerType
429               + ", defaulting to Jmol");
430       type = ViewerType.JMOL;
431     }
432     structureViewerPath.setText(viewerPath);
433
434     structureViewerPath.addActionListener(new ActionListener()
435     {
436       @Override
437       public void actionPerformed(ActionEvent e)
438       {
439         if (validateViewerPath())
440         {
441           Cache.setProperty(
442                   structViewer.getSelectedItem().equals(
443                           ViewerType.CHIMERAX.name()) ? CHIMERAX_PATH
444                                   : CHIMERA_PATH,
445                   structureViewerPath.getText());
446         }
447       }
448     });
449
450     if (Cache.getDefault("MAP_WITH_SIFTS", false))
451     {
452       siftsMapping.setSelected(true);
453     }
454     else
455     {
456       nwMapping.setSelected(true);
457     }
458
459     SiftsSettings
460             .setMapWithSifts(Cache.getDefault("MAP_WITH_SIFTS", false));
461
462     /*
463      * Set Connections tab defaults
464      */
465
466     // set up sorting
467     linkUrlTable.setModel(dataModel);
468     final TableRowSorter<TableModel> sorter = new TableRowSorter<>(
469             linkUrlTable.getModel());
470     linkUrlTable.setRowSorter(sorter);
471     List<RowSorter.SortKey> sortKeys = new ArrayList<>();
472
473     UrlLinkTableModel m = (UrlLinkTableModel) linkUrlTable.getModel();
474     sortKeys.add(new RowSorter.SortKey(m.getPrimaryColumn(),
475             SortOrder.DESCENDING));
476     sortKeys.add(new RowSorter.SortKey(m.getSelectedColumn(),
477             SortOrder.DESCENDING));
478     sortKeys.add(
479             new RowSorter.SortKey(m.getNameColumn(), SortOrder.ASCENDING));
480
481     sorter.setSortKeys(sortKeys);
482     // BH 2018 setSortKeys will do the sort
483     // sorter.sort();
484
485     // set up filtering
486     ActionListener onReset;
487     onReset = new ActionListener()
488     {
489       @Override
490       public void actionPerformed(ActionEvent e)
491       {
492         filterTB.setText("");
493         sorter.setRowFilter(RowFilter.regexFilter(""));
494       }
495
496     };
497     doReset.addActionListener(onReset);
498
499     // filter to display only custom urls
500     final RowFilter<TableModel, Object> customUrlFilter = new RowFilter<TableModel, Object>()
501     {
502       @Override
503       public boolean include(
504               Entry<? extends TableModel, ? extends Object> entry)
505       {
506         return ((UrlLinkTableModel) entry.getModel()).isUserEntry(entry);
507       }
508     };
509
510     final TableRowSorter<TableModel> customSorter = new TableRowSorter<>(
511             linkUrlTable.getModel());
512     customSorter.setRowFilter(customUrlFilter);
513
514     ActionListener onCustomOnly;
515     onCustomOnly = new ActionListener()
516     {
517       @Override
518       public void actionPerformed(ActionEvent e)
519       {
520         filterTB.setText("");
521         sorter.setRowFilter(customUrlFilter);
522       }
523     };
524     userOnly.addActionListener(onCustomOnly);
525
526     filterTB.getDocument().addDocumentListener(new DocumentListener()
527     {
528       String caseInsensitiveFlag = "(?i)";
529
530       @Override
531       public void changedUpdate(DocumentEvent e)
532       {
533         sorter.setRowFilter(RowFilter
534                 .regexFilter(caseInsensitiveFlag + filterTB.getText()));
535       }
536
537       @Override
538       public void removeUpdate(DocumentEvent e)
539       {
540         sorter.setRowFilter(RowFilter
541                 .regexFilter(caseInsensitiveFlag + filterTB.getText()));
542       }
543
544       @Override
545       public void insertUpdate(DocumentEvent e)
546       {
547         sorter.setRowFilter(RowFilter
548                 .regexFilter(caseInsensitiveFlag + filterTB.getText()));
549       }
550     });
551
552     // set up list selection functionality
553     linkUrlTable.getSelectionModel()
554             .addListSelectionListener(new UrlListSelectionHandler());
555
556     // set up radio buttons
557     int onClickCol = ((UrlLinkTableModel) linkUrlTable.getModel())
558             .getPrimaryColumn();
559     String onClickName = linkUrlTable.getColumnName(onClickCol);
560     linkUrlTable.getColumn(onClickName)
561             .setCellRenderer(new RadioButtonRenderer());
562     linkUrlTable.getColumn(onClickName)
563             .setCellEditor(new RadioButtonEditor());
564
565     // get boolean columns and resize those to min possible
566     for (int column = 0; column < linkUrlTable.getColumnCount(); column++)
567     {
568       if (linkUrlTable.getModel().getColumnClass(column)
569               .equals(Boolean.class))
570       {
571         TableColumn tableColumn = linkUrlTable.getColumnModel()
572                 .getColumn(column);
573         int preferredWidth = tableColumn.getMinWidth();
574
575         TableCellRenderer cellRenderer = linkUrlTable.getCellRenderer(0,
576                 column);
577         Component c = linkUrlTable.prepareRenderer(cellRenderer, 0, column);
578         int cwidth = c.getPreferredSize().width
579                 + linkUrlTable.getIntercellSpacing().width;
580         preferredWidth = Math.max(preferredWidth, cwidth);
581
582         tableColumn.setPreferredWidth(preferredWidth);
583       }
584     }
585
586     String proxyTypeString = Cache.getDefault("USE_PROXY", "false");
587     previousProxyType = proxyTypeString;
588     switch (proxyTypeString)
589     {
590     case Cache.PROXYTYPE_NONE:
591       proxyType.setSelected(noProxy.getModel(), true);
592       break;
593     case Cache.PROXYTYPE_SYSTEM:
594       proxyType.setSelected(systemProxy.getModel(), true);
595       break;
596     case Cache.PROXYTYPE_CUSTOM:
597       proxyType.setSelected(customProxy.getModel(), true);
598       break;
599     default:
600       Cache.log.warn(
601               "Incorrect PROXY_TYPE - should be 'none' (clear proxy properties), 'false' (system settings), 'true' (custom settings): "
602                       + proxyTypeString);
603     }
604     proxyServerHttpTB.setText(Cache.getDefault("PROXY_SERVER", ""));
605     proxyPortHttpTB.setText(Cache.getDefault("PROXY_PORT", ""));
606     proxyServerHttpsTB.setText(Cache.getDefault("PROXY_SERVER_HTTPS", ""));
607     proxyPortHttpsTB.setText(Cache.getDefault("PROXY_PORT_HTTPS", ""));
608     proxyAuth.setSelected(Cache.getDefault("PROXY_AUTH", false));
609     proxyAuthUsernameTB
610             .setText(Cache.getDefault("PROXY_AUTH_USERNAME", ""));
611     // we are not storing or retrieving proxy password from .jalview_properties
612     proxyAuthPasswordPB.setText(Cache.proxyAuthPassword == null ? ""
613             : new String(Cache.proxyAuthPassword));
614     setCustomProxyEnabled();
615     applyProxyButtonEnabled(false);
616
617     defaultBrowser.setText(Cache.getDefault("DEFAULT_BROWSER", ""));
618
619     usagestats.setSelected(Cache.getDefault("USAGESTATS", false));
620     // note antisense here: default is true
621     questionnaire
622             .setSelected(Cache.getProperty("NOQUESTIONNAIRES") == null);
623     versioncheck.setSelected(Cache.getDefault("VERSION_CHECK", true));
624
625     /*
626      * Set Output tab defaults
627      */
628     setupOutputCombo(epsRendering, "EPS_RENDERING");
629     setupOutputCombo(htmlRendering, "HTML_RENDERING");
630     setupOutputCombo(svgRendering, "SVG_RENDERING");
631     autoIdWidth.setSelected(Cache.getDefault("FIGURE_AUTOIDWIDTH", false));
632     userIdWidth.setEnabled(!autoIdWidth.isSelected());
633     userIdWidthlabel.setEnabled(!autoIdWidth.isSelected());
634     Integer wi = Cache.getIntegerProperty("FIGURE_FIXEDIDWIDTH");
635     userIdWidth.setText(wi == null ? "" : wi.toString());
636     // TODO: refactor to use common enum via FormatAdapter and allow extension
637     // for new flat file formats
638     blcjv.setSelected(Cache.getDefault("BLC_JVSUFFIX", true));
639     clustaljv.setSelected(Cache.getDefault("CLUSTAL_JVSUFFIX", true));
640     fastajv.setSelected(Cache.getDefault("FASTA_JVSUFFIX", true));
641     msfjv.setSelected(Cache.getDefault("MSF_JVSUFFIX", true));
642     pfamjv.setSelected(Cache.getDefault("PFAM_JVSUFFIX", true));
643     pileupjv.setSelected(Cache.getDefault("PILEUP_JVSUFFIX", true));
644     pirjv.setSelected(Cache.getDefault("PIR_JVSUFFIX", true));
645     modellerOutput.setSelected(Cache.getDefault("PIR_MODELLER", false));
646     embbedBioJSON
647             .setSelected(Cache.getDefault("EXPORT_EMBBED_BIOJSON", true));
648
649     /*
650      * Set Editing tab defaults
651      */
652     autoCalculateConsCheck
653             .setSelected(Cache.getDefault("AUTO_CALC_CONSENSUS", true));
654     padGaps.setSelected(Cache.getDefault("PAD_GAPS", false));
655     sortByTree.setSelected(Cache.getDefault("SORT_BY_TREE", false));
656
657     annotations_actionPerformed(null); // update the display of the annotation
658                                        // settings
659
660     /*
661      * Set Backups tab defaults
662      */
663     loadLastSavedBackupsOptions();
664
665     /*
666      * Set Startup tab defaults
667      */
668
669   }
670
671   /**
672    * A helper method that sets the items and initial selection in a character
673    * rendering option list (Prompt each time/Lineart/Text)
674    * 
675    * @param comboBox
676    * @param propertyKey
677    */
678   protected void setupOutputCombo(JComboBox<Object> comboBox,
679           String propertyKey)
680   {
681     comboBox.addItem(promptEachTimeOpt);
682     comboBox.addItem(lineArtOpt);
683     comboBox.addItem(textOpt);
684
685     /*
686      * JalviewJS doesn't support Lineart so force it to Text
687      */
688     String defaultOption = Platform.isJS() ? "Text"
689             : Cache.getDefault(propertyKey, "Prompt each time");
690     if (defaultOption.equalsIgnoreCase("Text"))
691     {
692       comboBox.setSelectedItem(textOpt);
693     }
694     else if (defaultOption.equalsIgnoreCase("Lineart"))
695     {
696       comboBox.setSelectedItem(lineArtOpt);
697     }
698     else
699     {
700       comboBox.setSelectedItem(promptEachTimeOpt);
701     }
702   }
703
704   /**
705    * Save user selections on the Preferences tabs to the Cache and write out to
706    * file.
707    * 
708    * @param e
709    */
710   @Override
711   public void ok_actionPerformed(ActionEvent e)
712   {
713     if (!validateSettings())
714     {
715       return;
716     }
717
718     /* 
719      * Set proxy settings first (to be before web services refresh)
720      */
721     saveProxySettings();
722
723     /*
724      * Save Visual settings
725      */
726     Cache.applicationProperties.setProperty("SHOW_JVSUFFIX",
727             Boolean.toString(seqLimit.isSelected()));
728     Cache.applicationProperties.setProperty("RIGHT_ALIGN_IDS",
729             Boolean.toString(rightAlign.isSelected()));
730     Cache.applicationProperties.setProperty("SHOW_FULLSCREEN",
731             Boolean.toString(fullScreen.isSelected()));
732     Cache.applicationProperties.setProperty("SHOW_OVERVIEW",
733             Boolean.toString(openoverv.isSelected()));
734     Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
735             Boolean.toString(annotations.isSelected()));
736     Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
737             Boolean.toString(conservation.isSelected()));
738     Cache.applicationProperties.setProperty("SHOW_QUALITY",
739             Boolean.toString(quality.isSelected()));
740     Cache.applicationProperties.setProperty("SHOW_IDENTITY",
741             Boolean.toString(identity.isSelected()));
742
743     Cache.applicationProperties.setProperty("GAP_SYMBOL",
744             gapSymbolCB.getSelectedItem().toString());
745
746     Cache.applicationProperties.setProperty("FONT_NAME",
747             fontNameCB.getSelectedItem().toString());
748     Cache.applicationProperties.setProperty("FONT_STYLE",
749             fontStyleCB.getSelectedItem().toString());
750     Cache.applicationProperties.setProperty("FONT_SIZE",
751             fontSizeCB.getSelectedItem().toString());
752
753     Cache.applicationProperties.setProperty("ID_ITALICS",
754             Boolean.toString(idItalics.isSelected()));
755     Cache.applicationProperties.setProperty("SHOW_UNCONSERVED",
756             Boolean.toString(showUnconserved.isSelected()));
757     Cache.applicationProperties.setProperty(SHOW_OCCUPANCY,
758             Boolean.toString(showOccupancy.isSelected()));
759     Cache.applicationProperties.setProperty("SHOW_GROUP_CONSENSUS",
760             Boolean.toString(showGroupConsensus.isSelected()));
761     Cache.applicationProperties.setProperty("SHOW_GROUP_CONSERVATION",
762             Boolean.toString(showGroupConservation.isSelected()));
763     Cache.applicationProperties.setProperty("SHOW_CONSENSUS_HISTOGRAM",
764             Boolean.toString(showConsensHistogram.isSelected()));
765     Cache.applicationProperties.setProperty("SHOW_CONSENSUS_LOGO",
766             Boolean.toString(showConsensLogo.isSelected()));
767     Cache.applicationProperties.setProperty("ANTI_ALIAS",
768             Boolean.toString(smoothFont.isSelected()));
769     Cache.applicationProperties.setProperty(SCALE_PROTEIN_TO_CDNA,
770             Boolean.toString(scaleProteinToCdna.isSelected()));
771     Cache.applicationProperties.setProperty("SHOW_NPFEATS_TOOLTIP",
772             Boolean.toString(showNpTooltip.isSelected()));
773     Cache.applicationProperties.setProperty("SHOW_DBREFS_TOOLTIP",
774             Boolean.toString(showDbRefTooltip.isSelected()));
775
776     Cache.applicationProperties.setProperty("WRAP_ALIGNMENT",
777             Boolean.toString(wrap.isSelected()));
778
779     Cache.applicationProperties.setProperty("STARTUP_FILE",
780             startupFileTextfield.getText());
781     Cache.applicationProperties.setProperty("SHOW_STARTUP_FILE",
782             Boolean.toString(startupCheckbox.isSelected()));
783
784     Cache.applicationProperties.setProperty("SORT_ALIGNMENT",
785             sortby.getSelectedItem().toString());
786
787     // convert description of sort order to enum name for save
788     SequenceAnnotationOrder annSortOrder = SequenceAnnotationOrder
789             .forDescription(sortAnnBy.getSelectedItem().toString());
790     if (annSortOrder != null)
791     {
792       Cache.applicationProperties.setProperty(SORT_ANNOTATIONS,
793               annSortOrder.name());
794     }
795
796     final boolean showAutocalcFirst = sortAutocalc.getSelectedIndex() == 0;
797     Cache.applicationProperties.setProperty(SHOW_AUTOCALC_ABOVE,
798             Boolean.valueOf(showAutocalcFirst).toString());
799
800     /*
801      * Save Colours settings
802      */
803     Cache.applicationProperties.setProperty(DEFAULT_COLOUR_PROT,
804             protColour.getSelectedItem().toString());
805     Cache.applicationProperties.setProperty(DEFAULT_COLOUR_NUC,
806             nucColour.getSelectedItem().toString());
807     Cache.setColourProperty("ANNOTATIONCOLOUR_MIN",
808             minColour.getBackground());
809     Cache.setColourProperty("ANNOTATIONCOLOUR_MAX",
810             maxColour.getBackground());
811
812     /*
813      * Save Overview settings
814      */
815     Cache.setColourProperty(GAP_COLOUR, gapColour.getBackground());
816     Cache.setColourProperty(HIDDEN_COLOUR, hiddenColour.getBackground());
817     Cache.applicationProperties.setProperty(USE_LEGACY_GAP,
818             Boolean.toString(useLegacyGap.isSelected()));
819     Cache.applicationProperties.setProperty(SHOW_OV_HIDDEN_AT_START,
820             Boolean.toString(showHiddenAtStart.isSelected()));
821
822     /*
823      * Save Structure settings
824      */
825     Cache.applicationProperties.setProperty(ADD_TEMPFACT_ANN,
826             Boolean.toString(addTempFactor.isSelected()));
827     Cache.applicationProperties.setProperty(ADD_SS_ANN,
828             Boolean.toString(addSecondaryStructure.isSelected()));
829     Cache.applicationProperties.setProperty(USE_RNAVIEW,
830             Boolean.toString(useRnaView.isSelected()));
831     Cache.applicationProperties.setProperty(STRUCT_FROM_PDB,
832             Boolean.toString(structFromPdb.isSelected()));
833     String viewer = structViewer.getSelectedItem().toString();
834     String viewerPath = structureViewerPath.getText();
835     Cache.applicationProperties.setProperty(STRUCTURE_DISPLAY, viewer);
836     if (viewer.equals(ViewerType.CHIMERA.name()))
837     {
838       Cache.setOrRemove(CHIMERA_PATH, viewerPath);
839     }
840     else if (viewer.equals(ViewerType.CHIMERAX.name()))
841     {
842       Cache.setOrRemove(CHIMERAX_PATH, viewerPath);
843     }
844     else if (viewer.equals(ViewerType.PYMOL.name()))
845     {
846       Cache.setOrRemove(PYMOL_PATH, viewerPath);
847     }
848     Cache.applicationProperties.setProperty("MAP_WITH_SIFTS",
849             Boolean.toString(siftsMapping.isSelected()));
850     SiftsSettings.setMapWithSifts(siftsMapping.isSelected());
851
852     /*
853      * Save Output settings
854      */
855     Cache.applicationProperties.setProperty("EPS_RENDERING",
856             ((OptionsParam) epsRendering.getSelectedItem()).getCode());
857     Cache.applicationProperties.setProperty("HTML_RENDERING",
858             ((OptionsParam) htmlRendering.getSelectedItem()).getCode());
859     Cache.applicationProperties.setProperty("SVG_RENDERING",
860             ((OptionsParam) svgRendering.getSelectedItem()).getCode());
861
862     /*
863      * Save Connections settings
864      */
865     // Proxy settings set first (to catch web services)
866
867     Cache.setOrRemove("DEFAULT_BROWSER", defaultBrowser.getText());
868
869     jalview.util.BrowserLauncher.resetBrowser();
870
871     // save user-defined and selected links
872     String menuLinks = sequenceUrlLinks.writeUrlsAsString(true);
873     if (menuLinks.isEmpty())
874     {
875       Cache.applicationProperties.remove("SEQUENCE_LINKS");
876     }
877     else
878     {
879       Cache.applicationProperties.setProperty("SEQUENCE_LINKS",
880               menuLinks.toString());
881     }
882
883     String nonMenuLinks = sequenceUrlLinks.writeUrlsAsString(false);
884     if (nonMenuLinks.isEmpty())
885     {
886       Cache.applicationProperties.remove("STORED_LINKS");
887     }
888     else
889     {
890       Cache.applicationProperties.setProperty("STORED_LINKS",
891               nonMenuLinks.toString());
892     }
893
894     Cache.applicationProperties.setProperty("DEFAULT_URL",
895             sequenceUrlLinks.getPrimaryUrlId());
896
897     Cache.setProperty("VERSION_CHECK",
898             Boolean.toString(versioncheck.isSelected()));
899     if (Cache.getProperty("USAGESTATS") != null || usagestats.isSelected())
900     {
901       // default is false - we only set this if the user has actively agreed
902       Cache.setProperty("USAGESTATS",
903               Boolean.toString(usagestats.isSelected()));
904     }
905     if (!questionnaire.isSelected())
906     {
907       Cache.setProperty("NOQUESTIONNAIRES", "true");
908     }
909     else
910     {
911       // special - made easy to edit a property file to disable questionnaires
912       // by just adding the given line
913       Cache.removeProperty("NOQUESTIONNAIRES");
914     }
915
916     /*
917      * Save Output settings
918      */
919     Cache.applicationProperties.setProperty("BLC_JVSUFFIX",
920             Boolean.toString(blcjv.isSelected()));
921     Cache.applicationProperties.setProperty("CLUSTAL_JVSUFFIX",
922             Boolean.toString(clustaljv.isSelected()));
923     Cache.applicationProperties.setProperty("FASTA_JVSUFFIX",
924             Boolean.toString(fastajv.isSelected()));
925     Cache.applicationProperties.setProperty("MSF_JVSUFFIX",
926             Boolean.toString(msfjv.isSelected()));
927     Cache.applicationProperties.setProperty("PFAM_JVSUFFIX",
928             Boolean.toString(pfamjv.isSelected()));
929     Cache.applicationProperties.setProperty("PILEUP_JVSUFFIX",
930             Boolean.toString(pileupjv.isSelected()));
931     Cache.applicationProperties.setProperty("PIR_JVSUFFIX",
932             Boolean.toString(pirjv.isSelected()));
933     Cache.applicationProperties.setProperty("PIR_MODELLER",
934             Boolean.toString(modellerOutput.isSelected()));
935     Cache.applicationProperties.setProperty("EXPORT_EMBBED_BIOJSON",
936             Boolean.toString(embbedBioJSON.isSelected()));
937     jalview.io.PIRFile.useModellerOutput = modellerOutput.isSelected();
938
939     Cache.applicationProperties.setProperty("FIGURE_AUTOIDWIDTH",
940             Boolean.toString(autoIdWidth.isSelected()));
941     userIdWidth_actionPerformed();
942     Cache.applicationProperties.setProperty("FIGURE_FIXEDIDWIDTH",
943             userIdWidth.getText());
944
945     /*
946      * Save Editing settings
947      */
948     Cache.applicationProperties.setProperty("AUTO_CALC_CONSENSUS",
949             Boolean.toString(autoCalculateConsCheck.isSelected()));
950     Cache.applicationProperties.setProperty("SORT_BY_TREE",
951             Boolean.toString(sortByTree.isSelected()));
952     Cache.applicationProperties.setProperty("PAD_GAPS",
953             Boolean.toString(padGaps.isSelected()));
954
955     if (!Platform.isJS())
956     {
957       wsPrefs.updateAndRefreshWsMenuConfig(false);
958     }
959
960     /*
961      * Save Backups settings
962      */
963     Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
964             Boolean.toString(enableBackupFiles.isSelected()));
965     int preset = getComboIntStringKey(backupfilesPresetsCombo);
966     Cache.applicationProperties.setProperty(BackupFiles.NS + "_PRESET",
967             Integer.toString(preset));
968
969     if (preset == BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM)
970     {
971       BackupFilesPresetEntry customBFPE = getBackupfilesCurrentEntry();
972       BackupFilesPresetEntry.backupfilesPresetEntriesValues.put(
973               BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM, customBFPE);
974       Cache.applicationProperties.setProperty(
975               BackupFilesPresetEntry.CUSTOMCONFIG, customBFPE.toString());
976     }
977
978     BackupFilesPresetEntry savedBFPE = BackupFilesPresetEntry.backupfilesPresetEntriesValues
979             .get(preset);
980     Cache.applicationProperties.setProperty(
981             BackupFilesPresetEntry.SAVEDCONFIG, savedBFPE.toString());
982
983     /*
984      * Save Memory Settings
985      */
986     Cache.applicationProperties.setProperty(
987             MemorySetting.CUSTOMISED_SETTINGS,
988             Boolean.toString(customiseMemorySetting.isSelected()));
989     Cache.applicationProperties.setProperty(MemorySetting.MEMORY_JVMMEMPC,
990             Integer.toString(jvmMemoryPercentSlider.getValue()));
991     Cache.applicationProperties.setProperty(MemorySetting.MEMORY_JVMMEMMAX,
992             jvmMemoryMaxTextField.getText());
993
994     /*
995      * save and close Preferences
996      */
997
998     Cache.saveProperties();
999     Desktop.instance.doConfigureStructurePrefs();
1000     try
1001     {
1002       frame.setClosed(true);
1003     } catch (Exception ex)
1004     {
1005     }
1006   }
1007
1008   public void saveProxySettings()
1009   {
1010     String newProxyType = customProxy.isSelected() ? Cache.PROXYTYPE_CUSTOM
1011             : noProxy.isSelected() ? Cache.PROXYTYPE_NONE
1012                     : Cache.PROXYTYPE_SYSTEM;
1013     Cache.applicationProperties.setProperty("USE_PROXY", newProxyType);
1014     Cache.setOrRemove("PROXY_SERVER", proxyServerHttpTB.getText());
1015     Cache.setOrRemove("PROXY_PORT", proxyPortHttpTB.getText());
1016     Cache.setOrRemove("PROXY_SERVER_HTTPS", proxyServerHttpsTB.getText());
1017     Cache.setOrRemove("PROXY_PORT_HTTPS", proxyPortHttpsTB.getText());
1018     Cache.setOrRemove("PROXY_AUTH",
1019             Boolean.toString(proxyAuth.isSelected()));
1020     Cache.setOrRemove("PROXY_AUTH_USERNAME", proxyAuthUsernameTB.getText());
1021     Cache.proxyAuthPassword = proxyAuthPasswordPB.getPassword();
1022     Cache.setProxyPropertiesFromPreferences(previousProxyType);
1023     if (newProxyType.equals(Cache.PROXYTYPE_CUSTOM)
1024             || !newProxyType.equals(previousProxyType))
1025     {
1026       // force a re-lookup of ws if proxytype is custom or has changed
1027       wsPrefs.update++;
1028     }
1029     previousProxyType = newProxyType;
1030   }
1031
1032   /**
1033    * Do any necessary validation before saving settings. Return focus to the
1034    * first tab which fails validation.
1035    * 
1036    * @return
1037    */
1038   private boolean validateSettings()
1039   {
1040     if (!validateStructure())
1041     {
1042       structureTab.requestFocusInWindow();
1043       return false;
1044     }
1045     return true;
1046   }
1047
1048   @Override
1049   protected boolean validateStructure()
1050   {
1051     return validateViewerPath();
1052
1053   }
1054
1055   /**
1056    * DOCUMENT ME!
1057    */
1058   @Override
1059   public void startupFileTextfield_mouseClicked()
1060   {
1061     // TODO: JAL-3048 not needed for Jalview-JS
1062     String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1063     JalviewFileChooser chooser = JalviewFileChooser
1064             .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat);
1065     chooser.setFileView(new JalviewFileView());
1066     chooser.setDialogTitle(
1067             MessageManager.getString("label.select_startup_file"));
1068
1069     int value = chooser.showOpenDialog(this);
1070
1071     if (value == JalviewFileChooser.APPROVE_OPTION)
1072     {
1073       FileFormatI format = chooser.getSelectedFormat();
1074       if (format != null)
1075       {
1076         Cache.applicationProperties.setProperty("DEFAULT_FILE_FORMAT",
1077                 format.getName());
1078       }
1079       startupFileTextfield
1080               .setText(chooser.getSelectedFile().getAbsolutePath());
1081     }
1082   }
1083
1084   /**
1085    * DOCUMENT ME!
1086    * 
1087    * @param e
1088    *          DOCUMENT ME!
1089    */
1090   @Override
1091   public void cancel_actionPerformed(ActionEvent e)
1092   {
1093     try
1094     {
1095       if (!Platform.isJS())
1096       {
1097         wsPrefs.updateWsMenuConfig(true);
1098         wsPrefs.refreshWs_actionPerformed(e);
1099       }
1100       frame.setClosed(true);
1101     } catch (Exception ex)
1102     {
1103     }
1104   }
1105
1106   /**
1107    * DOCUMENT ME!
1108    * 
1109    * @param e
1110    *          DOCUMENT ME!
1111    */
1112   @Override
1113   public void annotations_actionPerformed(ActionEvent e)
1114   {
1115     conservation.setEnabled(annotations.isSelected());
1116     quality.setEnabled(annotations.isSelected());
1117     identity.setEnabled(annotations.isSelected());
1118     showOccupancy.setEnabled(annotations.isSelected());
1119     showGroupConsensus.setEnabled(annotations.isSelected());
1120     showGroupConservation.setEnabled(annotations.isSelected());
1121     showConsensHistogram.setEnabled(annotations.isSelected()
1122             && (identity.isSelected() || showGroupConsensus.isSelected()));
1123     showConsensLogo.setEnabled(annotations.isSelected()
1124             && (identity.isSelected() || showGroupConsensus.isSelected()));
1125   }
1126
1127   @Override
1128   public void newLink_actionPerformed(ActionEvent e)
1129   {
1130     GSequenceLink link = new GSequenceLink();
1131     boolean valid = false;
1132     while (!valid)
1133     {
1134       if (JvOptionPane.showInternalConfirmDialog(Desktop.desktop, link,
1135               MessageManager.getString("label.new_sequence_url_link"),
1136               JvOptionPane.OK_CANCEL_OPTION, -1,
1137               null) == JvOptionPane.OK_OPTION)
1138       {
1139         if (link.checkValid())
1140         {
1141           if (((UrlLinkTableModel) linkUrlTable.getModel())
1142                   .isUniqueName(link.getName()))
1143           {
1144             ((UrlLinkTableModel) linkUrlTable.getModel())
1145                     .insertRow(link.getName(), link.getURL());
1146             valid = true;
1147           }
1148           else
1149           {
1150             link.notifyDuplicate();
1151             continue;
1152           }
1153         }
1154       }
1155       else
1156       {
1157         break;
1158       }
1159     }
1160   }
1161
1162   @Override
1163   public void editLink_actionPerformed(ActionEvent e)
1164   {
1165     GSequenceLink link = new GSequenceLink();
1166
1167     int index = linkUrlTable.getSelectedRow();
1168     if (index == -1)
1169     {
1170       // button no longer enabled if row is not selected
1171       Cache.log.debug("Edit with no row selected in linkUrlTable");
1172       return;
1173     }
1174
1175     int nameCol = ((UrlLinkTableModel) linkUrlTable.getModel())
1176             .getNameColumn();
1177     int urlCol = ((UrlLinkTableModel) linkUrlTable.getModel())
1178             .getUrlColumn();
1179     String oldName = linkUrlTable.getValueAt(index, nameCol).toString();
1180     link.setName(oldName);
1181     link.setURL(linkUrlTable.getValueAt(index, urlCol).toString());
1182
1183     boolean valid = false;
1184     while (!valid)
1185     {
1186       if (JvOptionPane.showInternalConfirmDialog(Desktop.desktop, link,
1187               MessageManager.getString("label.edit_sequence_url_link"),
1188               JvOptionPane.OK_CANCEL_OPTION, -1,
1189               null) == JvOptionPane.OK_OPTION)
1190       {
1191         if (link.checkValid())
1192         {
1193           if ((oldName.equals(link.getName()))
1194                   || (((UrlLinkTableModel) linkUrlTable.getModel())
1195                           .isUniqueName(link.getName())))
1196           {
1197             linkUrlTable.setValueAt(link.getName(), index, nameCol);
1198             linkUrlTable.setValueAt(link.getURL(), index, urlCol);
1199             valid = true;
1200           }
1201           else
1202           {
1203             link.notifyDuplicate();
1204             continue;
1205           }
1206         }
1207       }
1208       else
1209       {
1210         break;
1211       }
1212     }
1213   }
1214
1215   @Override
1216   public void deleteLink_actionPerformed(ActionEvent e)
1217   {
1218     int index = linkUrlTable.getSelectedRow();
1219     int modelIndex = -1;
1220     if (index == -1)
1221     {
1222       // button no longer enabled if row is not selected
1223       Cache.log.debug("Delete with no row selected in linkUrlTable");
1224       return;
1225     }
1226     else
1227     {
1228       modelIndex = linkUrlTable.convertRowIndexToModel(index);
1229     }
1230
1231     // make sure we use the model index to delete, and not the table index
1232     ((UrlLinkTableModel) linkUrlTable.getModel()).removeRow(modelIndex);
1233   }
1234
1235   @Override
1236   public void defaultBrowser_mouseClicked(MouseEvent e)
1237   {
1238     // TODO: JAL-3048 not needed for j2s
1239     if (!Platform.isJS()) // BH 2019
1240     /**
1241      * Java only
1242      * 
1243      * @j2sIgnore
1244      */
1245     {
1246       JFileChooser chooser = new JFileChooser(".");
1247       chooser.setDialogTitle(
1248               MessageManager.getString("label.select_default_browser"));
1249
1250       int value = chooser.showOpenDialog(this);
1251
1252       if (value == JFileChooser.APPROVE_OPTION)
1253       {
1254         defaultBrowser.setText(chooser.getSelectedFile().getAbsolutePath());
1255       }
1256     }
1257   }
1258
1259   /*
1260    * (non-Javadoc)
1261    * 
1262    * @see
1263    * jalview.jbgui.GPreferences#showunconserved_actionPerformed(java.awt.event
1264    * .ActionEvent)
1265    */
1266   @Override
1267   protected void showunconserved_actionPerformed(ActionEvent e)
1268   {
1269     // TODO Auto-generated method stub
1270     super.showunconserved_actionPerformed(e);
1271   }
1272
1273   public static List<String> getGroupURLLinks()
1274   {
1275     return groupURLLinks;
1276   }
1277
1278   @Override
1279   public void minColour_actionPerformed(JPanel panel)
1280   {
1281     JalviewColourChooser.showColourChooser(this,
1282             MessageManager.getString("label.select_colour_minimum_value"),
1283             panel);
1284   }
1285
1286   @Override
1287   public void maxColour_actionPerformed(JPanel panel)
1288   {
1289     JalviewColourChooser.showColourChooser(this,
1290             MessageManager.getString("label.select_colour_maximum_value"),
1291             panel);
1292   }
1293
1294   @Override
1295   public void gapColour_actionPerformed(JPanel gap)
1296   {
1297     if (!useLegacyGap.isSelected())
1298     {
1299       JalviewColourChooser.showColourChooser(this,
1300               MessageManager.getString("label.select_gap_colour"), gap);
1301     }
1302   }
1303
1304   @Override
1305   public void hiddenColour_actionPerformed(JPanel hidden)
1306   {
1307     JalviewColourChooser.showColourChooser(this,
1308             MessageManager.getString("label.select_hidden_colour"), hidden);
1309   }
1310
1311   @Override
1312   protected void useLegacyGaps_actionPerformed(ActionEvent e)
1313   {
1314     boolean enabled = useLegacyGap.isSelected();
1315     if (enabled)
1316     {
1317       gapColour.setBackground(
1318               jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_LEGACY_GAP);
1319     }
1320     else
1321     {
1322       gapColour.setBackground(
1323               jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP);
1324     }
1325     gapColour.setEnabled(!enabled);
1326     gapLabel.setEnabled(!enabled);
1327   }
1328
1329   @Override
1330   protected void resetOvDefaults_actionPerformed(ActionEvent e)
1331   {
1332     useLegacyGap.setSelected(false);
1333     useLegacyGaps_actionPerformed(null);
1334     showHiddenAtStart.setSelected(false);
1335     hiddenColour.setBackground(
1336             jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_HIDDEN);
1337   }
1338
1339   @Override
1340   protected void userIdWidth_actionPerformed()
1341   {
1342     try
1343     {
1344       String val = userIdWidth.getText().trim();
1345       if (val.length() > 0)
1346       {
1347         Integer iw = Integer.parseInt(val);
1348         if (iw.intValue() < 12)
1349         {
1350           throw new NumberFormatException();
1351         }
1352         userIdWidth.setText(iw.toString());
1353       }
1354     } catch (NumberFormatException x)
1355     {
1356       userIdWidth.setText("");
1357       JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1358               MessageManager
1359                       .getString("warn.user_defined_width_requirements"),
1360               MessageManager.getString("label.invalid_id_column_width"),
1361               JvOptionPane.WARNING_MESSAGE);
1362     }
1363   }
1364
1365   @Override
1366   protected void autoIdWidth_actionPerformed()
1367   {
1368     userIdWidth.setEnabled(!autoIdWidth.isSelected());
1369     userIdWidthlabel.setEnabled(!autoIdWidth.isSelected());
1370   }
1371
1372   /**
1373    * Returns true if structure viewer path is to a valid executable, else shows
1374    * an error dialog. Does nothing if the path is empty, as is the case for Jmol
1375    * (built in to Jalview) or when Jalview is left to try default paths.
1376    */
1377   private boolean validateViewerPath()
1378   {
1379     if (structureViewerPath.getText().trim().length() > 0)
1380     {
1381       File f = new File(structureViewerPath.getText());
1382       if (!f.canExecute())
1383       {
1384         JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1385                 MessageManager.getString("label.invalid_viewer_path"),
1386                 MessageManager.getString("label.invalid_viewer_path"),
1387                 JvOptionPane.ERROR_MESSAGE);
1388         return false;
1389       }
1390     }
1391     return true;
1392   }
1393
1394   /**
1395    * If Chimera or ChimeraX or Pymol is selected, check it can be found on
1396    * default or user-specified path, if not show a warning/help dialog
1397    */
1398   @Override
1399   protected void structureViewer_actionPerformed(String selectedItem)
1400   {
1401     if (selectedItem.equals(ViewerType.JMOL.name()))
1402     {
1403       structureViewerPath.setEnabled(false);
1404       structureViewerPathLabel.setEnabled(false);
1405       return;
1406     }
1407     boolean found = false;
1408     structureViewerPath.setEnabled(true);
1409     structureViewerPathLabel.setEnabled(true);
1410     structureViewerPathLabel.setText(MessageManager
1411             .formatMessage("label.viewer_path", selectedItem));
1412
1413     /*
1414      * Try user-specified and standard paths for structure viewer executable
1415      */
1416     String viewerPath = "";
1417     List<String> paths = null;
1418     try
1419     {
1420       ViewerType viewerType = ViewerType.valueOf(selectedItem);
1421       switch (viewerType)
1422       {
1423       case JMOL:
1424         // dealt with above
1425         break;
1426       case CHIMERA:
1427         viewerPath = Cache.getDefault(CHIMERA_PATH, "");
1428         paths = StructureManager.getChimeraPaths(false);
1429         break;
1430       case CHIMERAX:
1431         viewerPath = Cache.getDefault(CHIMERAX_PATH, "");
1432         paths = StructureManager.getChimeraPaths(true);
1433         break;
1434       case PYMOL:
1435         viewerPath = Cache.getDefault(PYMOL_PATH, "");
1436         paths = PymolManager.getPymolPaths();
1437         break;
1438       }
1439     } catch (IllegalArgumentException e)
1440     {
1441       // only valid entries should be in the drop-down
1442     }
1443     structureViewerPath.setText(viewerPath);
1444
1445     paths.add(0, structureViewerPath.getText());
1446     for (String path : paths)
1447     {
1448       if (new File(path.trim()).canExecute())
1449       {
1450         found = true;
1451         break;
1452       }
1453     }
1454
1455     if (!found)
1456     {
1457       String[] options = { "OK", "Help" };
1458       int showHelp = JvOptionPane.showInternalOptionDialog(Desktop.desktop,
1459               JvSwingUtils.wrapTooltip(true,
1460                       MessageManager.getString("label.viewer_missing")),
1461               "", JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
1462               null, options, options[0]);
1463       if (showHelp == JvOptionPane.NO_OPTION)
1464       {
1465         try
1466         {
1467           Help.showHelpWindow(HelpId.StructureViewer);
1468         } catch (HelpSetException e)
1469         {
1470           e.printStackTrace();
1471         }
1472       }
1473     }
1474   }
1475
1476   public class OptionsParam
1477   {
1478     private String name;
1479
1480     private String code;
1481
1482     public OptionsParam(String name, String code)
1483     {
1484       this.name = name;
1485       this.code = code;
1486     }
1487
1488     public String getName()
1489     {
1490       return name;
1491     }
1492
1493     public void setName(String name)
1494     {
1495       this.name = name;
1496     }
1497
1498     public String getCode()
1499     {
1500       return code;
1501     }
1502
1503     public void setCode(String code)
1504     {
1505       this.code = code;
1506     }
1507
1508     @Override
1509     public String toString()
1510     {
1511       return name;
1512     }
1513
1514     @Override
1515     public boolean equals(Object that)
1516     {
1517       if (!(that instanceof OptionsParam))
1518       {
1519         return false;
1520       }
1521       return this.code.equalsIgnoreCase(((OptionsParam) that).code);
1522     }
1523
1524     @Override
1525     public int hashCode()
1526     {
1527       return name.hashCode() + code.hashCode();
1528     }
1529   }
1530
1531   private class UrlListSelectionHandler implements ListSelectionListener
1532   {
1533
1534     @Override
1535     public void valueChanged(ListSelectionEvent e)
1536     {
1537       ListSelectionModel lsm = (ListSelectionModel) e.getSource();
1538
1539       int index = lsm.getMinSelectionIndex();
1540       if (index == -1)
1541       {
1542         // no selection, so disable delete/edit buttons
1543         editLink.setEnabled(false);
1544         deleteLink.setEnabled(false);
1545         return;
1546       }
1547       int modelIndex = linkUrlTable.convertRowIndexToModel(index);
1548
1549       // enable/disable edit and delete link buttons
1550       if (((UrlLinkTableModel) linkUrlTable.getModel())
1551               .isRowDeletable(modelIndex))
1552       {
1553         deleteLink.setEnabled(true);
1554       }
1555       else
1556       {
1557         deleteLink.setEnabled(false);
1558       }
1559
1560       if (((UrlLinkTableModel) linkUrlTable.getModel())
1561               .isRowEditable(modelIndex))
1562       {
1563         editLink.setEnabled(true);
1564       }
1565       else
1566       {
1567         editLink.setEnabled(false);
1568       }
1569     }
1570   }
1571 }