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