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