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