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