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