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