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