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