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