JAL-2527 Started adding overview preferences
[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.JButton;
58 import javax.swing.JColorChooser;
59 import javax.swing.JFileChooser;
60 import javax.swing.JInternalFrame;
61 import javax.swing.JPanel;
62 import javax.swing.ListSelectionModel;
63 import javax.swing.RowFilter;
64 import javax.swing.RowSorter;
65 import javax.swing.SortOrder;
66 import javax.swing.event.DocumentEvent;
67 import javax.swing.event.DocumentListener;
68 import javax.swing.event.ListSelectionEvent;
69 import javax.swing.event.ListSelectionListener;
70 import javax.swing.table.TableCellRenderer;
71 import javax.swing.table.TableColumn;
72 import javax.swing.table.TableModel;
73 import javax.swing.table.TableRowSorter;
74
75 import ext.edu.ucsf.rbvi.strucviz2.StructureManager;
76
77 /**
78  * DOCUMENT ME!
79  * 
80  * @author $author$
81  * @version $Revision$
82  */
83 public class Preferences extends GPreferences
84 {
85   public static final String ENABLE_SPLIT_FRAME = "ENABLE_SPLIT_FRAME";
86
87   public static final String SCALE_PROTEIN_TO_CDNA = "SCALE_PROTEIN_TO_CDNA";
88
89   public static final String DEFAULT_COLOUR = "DEFAULT_COLOUR";
90
91   public static final String DEFAULT_COLOUR_PROT = "DEFAULT_COLOUR_PROT";
92
93   public static final String DEFAULT_COLOUR_NUC = "DEFAULT_COLOUR_NUC";
94
95   public static final String ADD_TEMPFACT_ANN = "ADD_TEMPFACT_ANN";
96
97   public static final String ADD_SS_ANN = "ADD_SS_ANN";
98
99   public static final String USE_RNAVIEW = "USE_RNAVIEW";
100
101   public static final String STRUCT_FROM_PDB = "STRUCT_FROM_PDB";
102
103   public static final String STRUCTURE_DISPLAY = "STRUCTURE_DISPLAY";
104
105   public static final String CHIMERA_PATH = "CHIMERA_PATH";
106
107   public static final String SORT_ANNOTATIONS = "SORT_ANNOTATIONS";
108
109   public static final String SHOW_AUTOCALC_ABOVE = "SHOW_AUTOCALC_ABOVE";
110
111   public static final String SHOW_OCCUPANCY = "SHOW_OCCUPANCY";
112
113   private static final int MIN_FONT_SIZE = 1;
114
115   private static final int MAX_FONT_SIZE = 30;
116
117   /**
118    * Holds name and link separated with | character. Sequence ID must be
119    * $SEQUENCE_ID$ or $SEQUENCE_ID=/.possible | chars ./=$
120    */
121   public static UrlProviderI sequenceUrlLinks;
122
123   public static UrlLinkTableModel dataModel;
124
125   /**
126    * Holds name and link separated with | character. Sequence IDS and Sequences
127    * must be $SEQUENCEIDS$ or $SEQUENCEIDS=/.possible | chars ./=$ and
128    * $SEQUENCES$ or $SEQUENCES=/.possible | chars ./=$ and separation character
129    * for first and second token specified after a pipe character at end |,|.
130    * (TODO: proper escape for using | to separate ids or sequences
131    */
132
133   public static List<String> groupURLLinks;
134   static
135   {
136     // get links selected to be in the menu (SEQUENCE_LINKS)
137     // and links entered by the user but not selected (STORED_LINKS)
138     String inMenuString = Cache.getDefault("SEQUENCE_LINKS", "");
139     String notInMenuString = Cache.getDefault("STORED_LINKS", "");
140     String defaultUrl = Cache.getDefault("DEFAULT_URL",
141             UrlConstants.DEFAULT_LABEL);
142
143     // if both links lists are empty, add the DEFAULT_URL link
144     // otherwise we assume the default link is in one of the lists
145     if (inMenuString.isEmpty() && notInMenuString.isEmpty())
146     {
147       inMenuString = UrlConstants.DEFAULT_STRING;
148     }
149     UrlProviderFactoryI factory = new DesktopUrlProviderFactory(defaultUrl,
150             inMenuString, notInMenuString);
151     sequenceUrlLinks = factory.createUrlProvider();
152     dataModel = new UrlLinkTableModel(sequenceUrlLinks);
153
154     /**
155      * TODO: reformulate groupURL encoding so two or more can be stored in the
156      * .properties file as '|' separated strings
157      */
158
159     groupURLLinks = new ArrayList<>();
160   }
161
162   JInternalFrame frame;
163
164   DasSourceBrowser dasSource;
165
166   private WsPreferences wsPrefs;
167
168   private OptionsParam promptEachTimeOpt = new OptionsParam(
169           MessageManager.getString("label.prompt_each_time"),
170           "Prompt each time");
171
172   private OptionsParam lineArtOpt = new OptionsParam(
173           MessageManager.getString("label.lineart"), "Lineart");
174
175   private OptionsParam textOpt = new OptionsParam(
176           MessageManager.getString("action.text"), "Text");
177
178   /**
179    * Creates a new Preferences object.
180    */
181   public Preferences()
182   {
183     super();
184     frame = new JInternalFrame();
185     frame.setContentPane(this);
186     dasSource = new DasSourceBrowser();
187     dasTab.add(dasSource, BorderLayout.CENTER);
188     wsPrefs = new WsPreferences();
189     wsTab.add(wsPrefs, BorderLayout.CENTER);
190     int width = 500, height = 450;
191     new jalview.util.Platform();
192     if (Platform.isAMac())
193     {
194       width = 570;
195       height = 480;
196     }
197
198     Desktop.addInternalFrame(frame,
199             MessageManager.getString("label.preferences"), width, height);
200     frame.setMinimumSize(new Dimension(width, height));
201
202     /*
203      * Set Visual tab defaults
204      */
205     seqLimit.setSelected(Cache.getDefault("SHOW_JVSUFFIX", true));
206     rightAlign.setSelected(Cache.getDefault("RIGHT_ALIGN_IDS", false));
207     fullScreen.setSelected(Cache.getDefault("SHOW_FULLSCREEN", false));
208     annotations.setSelected(Cache.getDefault("SHOW_ANNOTATIONS", true));
209
210     conservation.setSelected(Cache.getDefault("SHOW_CONSERVATION", true));
211     quality.setSelected(Cache.getDefault("SHOW_QUALITY", true));
212     identity.setSelected(Cache.getDefault("SHOW_IDENTITY", true));
213     openoverv.setSelected(Cache.getDefault("SHOW_OVERVIEW", false));
214     showUnconserved
215             .setSelected(Cache.getDefault("SHOW_UNCONSERVED", false));
216     showOccupancy.setSelected(Cache.getDefault(SHOW_OCCUPANCY, false));
217     showGroupConsensus
218             .setSelected(Cache.getDefault("SHOW_GROUP_CONSENSUS", false));
219     showGroupConservation.setSelected(
220             Cache.getDefault("SHOW_GROUP_CONSERVATION", false));
221     showConsensHistogram.setSelected(
222             Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM", true));
223     showConsensLogo
224             .setSelected(Cache.getDefault("SHOW_CONSENSUS_LOGO", false));
225     showNpTooltip
226             .setSelected(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true));
227     showDbRefTooltip
228             .setSelected(Cache.getDefault("SHOW_DBREFS_TOOLTIP", true));
229
230     String[] fonts = java.awt.GraphicsEnvironment
231             .getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
232     for (int i = 0; i < fonts.length; i++)
233     {
234       fontNameCB.addItem(fonts[i]);
235     }
236
237     for (int i = MIN_FONT_SIZE; i <= MAX_FONT_SIZE; i++)
238     {
239       fontSizeCB.addItem(i + "");
240     }
241
242     fontStyleCB.addItem("plain");
243     fontStyleCB.addItem("bold");
244     fontStyleCB.addItem("italic");
245
246     fontNameCB.setSelectedItem(Cache.getDefault("FONT_NAME", "SansSerif"));
247     fontSizeCB.setSelectedItem(Cache.getDefault("FONT_SIZE", "10"));
248     fontStyleCB.setSelectedItem(
249             Cache.getDefault("FONT_STYLE", Font.PLAIN + ""));
250
251     smoothFont.setSelected(Cache.getDefault("ANTI_ALIAS", false));
252     scaleProteinToCdna
253             .setSelected(Cache.getDefault(SCALE_PROTEIN_TO_CDNA, false));
254
255     idItalics.setSelected(Cache.getDefault("ID_ITALICS", true));
256
257     wrap.setSelected(Cache.getDefault("WRAP_ALIGNMENT", false));
258
259     gapSymbolCB.addItem("-");
260     gapSymbolCB.addItem(".");
261
262     gapSymbolCB.setSelectedItem(Cache.getDefault("GAP_SYMBOL", "-"));
263
264     sortby.addItem("No sort");
265     sortby.addItem("Id");
266     sortby.addItem("Pairwise Identity");
267     sortby.setSelectedItem(Cache.getDefault("SORT_ALIGNMENT", "No sort"));
268
269     sortAnnBy.addItem(SequenceAnnotationOrder.NONE.toString());
270     sortAnnBy
271             .addItem(SequenceAnnotationOrder.SEQUENCE_AND_LABEL.toString());
272     sortAnnBy
273             .addItem(SequenceAnnotationOrder.LABEL_AND_SEQUENCE.toString());
274     SequenceAnnotationOrder savedSort = SequenceAnnotationOrder
275             .valueOf(Cache.getDefault(SORT_ANNOTATIONS,
276                     SequenceAnnotationOrder.NONE.name()));
277     sortAnnBy.setSelectedItem(savedSort.toString());
278
279     sortAutocalc.addItem("Autocalculated first");
280     sortAutocalc.addItem("Autocalculated last");
281     final boolean showAbove = Cache.getDefault(SHOW_AUTOCALC_ABOVE, true);
282     sortAutocalc.setSelectedItem(showAbove ? sortAutocalc.getItemAt(0)
283             : sortAutocalc.getItemAt(1));
284     startupCheckbox
285             .setSelected(Cache.getDefault("SHOW_STARTUP_FILE", true));
286     startupFileTextfield.setText(Cache.getDefault("STARTUP_FILE",
287             Cache.getDefault("www.jalview.org", "http://www.jalview.org")
288                     + "/examples/exampleFile_2_3.jar"));
289
290     /*
291      * Set Colours tab defaults
292      */
293     protColour.addItem(ResidueColourScheme.NONE);
294     nucColour.addItem(ResidueColourScheme.NONE);
295     for (ColourSchemeI cs : ColourSchemes.getInstance().getColourSchemes())
296     {
297       String name = cs.getSchemeName();
298       protColour.addItem(name);
299       nucColour.addItem(name);
300     }
301     String oldProp = Cache.getDefault(DEFAULT_COLOUR,
302             ResidueColourScheme.NONE);
303     String newProp = Cache.getDefault(DEFAULT_COLOUR_PROT, null);
304     protColour.setSelectedItem(newProp != null ? newProp : oldProp);
305     newProp = Cache.getDefault(DEFAULT_COLOUR_NUC, null);
306     nucColour.setSelectedItem(newProp != null ? newProp : oldProp);
307     minColour.setBackground(
308             Cache.getDefaultColour("ANNOTATIONCOLOUR_MIN", Color.orange));
309     maxColour.setBackground(
310             Cache.getDefaultColour("ANNOTATIONCOLOUR_MAX", Color.red));
311     gapColour.setBackground(
312             Cache.getDefaultColour("GAP_COLOUR", Color.lightGray));
313     hiddenColour.setBackground(
314             Cache.getDefaultColour("HIDDEN_COLOUR", Color.darkGray));
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(
328             Cache.getDefault(STRUCTURE_DISPLAY, 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(
368             new RowSorter.SortKey(m.getNameColumn(), 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
422                 .regexFilter(caseInsensitiveFlag + filterTB.getText()));
423       }
424
425       @Override
426       public void removeUpdate(DocumentEvent e)
427       {
428         sorter.setRowFilter(RowFilter
429                 .regexFilter(caseInsensitiveFlag + filterTB.getText()));
430       }
431
432       @Override
433       public void insertUpdate(DocumentEvent e)
434       {
435         sorter.setRowFilter(RowFilter
436                 .regexFilter(caseInsensitiveFlag + filterTB.getText()));
437       }
438     });
439
440     // set up list selection functionality
441     linkUrlTable.getSelectionModel()
442             .addListSelectionListener(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)
449             .setCellRenderer(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()
460                 .getColumn(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
521             .setSelected(Cache.getDefault("EXPORT_EMBBED_BIOJSON", true));
522
523     /*
524      * Set Editing tab defaults
525      */
526     autoCalculateConsCheck
527             .setSelected(Cache.getDefault("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",
570             gapSymbolCB.getSelectedItem().toString());
571
572     Cache.applicationProperties.setProperty("FONT_NAME",
573             fontNameCB.getSelectedItem().toString());
574     Cache.applicationProperties.setProperty("FONT_STYLE",
575             fontStyleCB.getSelectedItem().toString());
576     Cache.applicationProperties.setProperty("FONT_SIZE",
577             fontSizeCB.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("ANTI_ALIAS",
594             Boolean.toString(smoothFont.isSelected()));
595     Cache.applicationProperties.setProperty(SCALE_PROTEIN_TO_CDNA,
596             Boolean.toString(scaleProteinToCdna.isSelected()));
597     Cache.applicationProperties.setProperty("SHOW_NPFEATS_TOOLTIP",
598             Boolean.toString(showNpTooltip.isSelected()));
599     Cache.applicationProperties.setProperty("SHOW_DBREFS_TOOLTIP",
600             Boolean.toString(showDbRefTooltip.isSelected()));
601
602     Cache.applicationProperties.setProperty("WRAP_ALIGNMENT",
603             Boolean.toString(wrap.isSelected()));
604
605     Cache.applicationProperties.setProperty("STARTUP_FILE",
606             startupFileTextfield.getText());
607     Cache.applicationProperties.setProperty("SHOW_STARTUP_FILE",
608             Boolean.toString(startupCheckbox.isSelected()));
609
610     Cache.applicationProperties.setProperty("SORT_ALIGNMENT",
611             sortby.getSelectedItem().toString());
612
613     // convert description of sort order to enum name for save
614     SequenceAnnotationOrder annSortOrder = SequenceAnnotationOrder
615             .forDescription(sortAnnBy.getSelectedItem().toString());
616     if (annSortOrder != null)
617     {
618       Cache.applicationProperties.setProperty(SORT_ANNOTATIONS,
619               annSortOrder.name());
620     }
621
622     final boolean showAutocalcFirst = sortAutocalc.getSelectedIndex() == 0;
623     Cache.applicationProperties.setProperty(SHOW_AUTOCALC_ABOVE,
624             Boolean.valueOf(showAutocalcFirst).toString());
625
626     /*
627      * Save Colours settings
628      */
629     Cache.applicationProperties.setProperty(DEFAULT_COLOUR_PROT,
630             protColour.getSelectedItem().toString());
631     Cache.applicationProperties.setProperty(DEFAULT_COLOUR_NUC,
632             nucColour.getSelectedItem().toString());
633     Cache.setColourProperty("ANNOTATIONCOLOUR_MIN",
634             minColour.getBackground());
635     Cache.setColourProperty("ANNOTATIONCOLOUR_MAX",
636             maxColour.getBackground());
637
638     /*
639      * Save Structure settings
640      */
641     Cache.applicationProperties.setProperty(ADD_TEMPFACT_ANN,
642             Boolean.toString(addTempFactor.isSelected()));
643     Cache.applicationProperties.setProperty(ADD_SS_ANN,
644             Boolean.toString(addSecondaryStructure.isSelected()));
645     Cache.applicationProperties.setProperty(USE_RNAVIEW,
646             Boolean.toString(useRnaView.isSelected()));
647     Cache.applicationProperties.setProperty(STRUCT_FROM_PDB,
648             Boolean.toString(structFromPdb.isSelected()));
649     Cache.applicationProperties.setProperty(STRUCTURE_DISPLAY,
650             structViewer.getSelectedItem().toString());
651     Cache.setOrRemove(CHIMERA_PATH, chimeraPath.getText());
652     Cache.applicationProperties.setProperty("MAP_WITH_SIFTS",
653             Boolean.toString(siftsMapping.isSelected()));
654     SiftsSettings.setMapWithSifts(siftsMapping.isSelected());
655
656     /*
657      * Save Output settings
658      */
659     Cache.applicationProperties.setProperty("EPS_RENDERING",
660             ((OptionsParam) epsRendering.getSelectedItem()).getCode());
661
662     /*
663      * Save Connections settings
664      */
665     Cache.setOrRemove("DEFAULT_BROWSER", defaultBrowser.getText());
666
667     jalview.util.BrowserLauncher.resetBrowser();
668
669     // save user-defined and selected links
670     String menuLinks = sequenceUrlLinks.writeUrlsAsString(true);
671     if (menuLinks.isEmpty())
672     {
673       Cache.applicationProperties.remove("SEQUENCE_LINKS");
674     }
675     else
676     {
677       Cache.applicationProperties.setProperty("SEQUENCE_LINKS",
678               menuLinks.toString());
679     }
680
681     String nonMenuLinks = sequenceUrlLinks.writeUrlsAsString(false);
682     if (nonMenuLinks.isEmpty())
683     {
684       Cache.applicationProperties.remove("STORED_LINKS");
685     }
686     else
687     {
688       Cache.applicationProperties.setProperty("STORED_LINKS",
689               nonMenuLinks.toString());
690     }
691
692     Cache.applicationProperties.setProperty("DEFAULT_URL",
693             sequenceUrlLinks.getPrimaryUrlId());
694
695     Cache.applicationProperties.setProperty("USE_PROXY",
696             Boolean.toString(useProxy.isSelected()));
697
698     Cache.setOrRemove("PROXY_SERVER", proxyServerTB.getText());
699
700     Cache.setOrRemove("PROXY_PORT", proxyPortTB.getText());
701
702     if (useProxy.isSelected())
703     {
704       System.setProperty("http.proxyHost", proxyServerTB.getText());
705       System.setProperty("http.proxyPort", proxyPortTB.getText());
706     }
707     else
708     {
709       System.setProperty("http.proxyHost", "");
710       System.setProperty("http.proxyPort", "");
711     }
712     Cache.setProperty("VERSION_CHECK",
713             Boolean.toString(versioncheck.isSelected()));
714     if (Cache.getProperty("USAGESTATS") != null || usagestats.isSelected())
715     {
716       // default is false - we only set this if the user has actively agreed
717       Cache.setProperty("USAGESTATS",
718               Boolean.toString(usagestats.isSelected()));
719     }
720     if (!questionnaire.isSelected())
721     {
722       Cache.setProperty("NOQUESTIONNAIRES", "true");
723     }
724     else
725     {
726       // special - made easy to edit a property file to disable questionnaires
727       // by just adding the given line
728       Cache.removeProperty("NOQUESTIONNAIRES");
729     }
730
731     /*
732      * Save Output settings
733      */
734     Cache.applicationProperties.setProperty("BLC_JVSUFFIX",
735             Boolean.toString(blcjv.isSelected()));
736     Cache.applicationProperties.setProperty("CLUSTAL_JVSUFFIX",
737             Boolean.toString(clustaljv.isSelected()));
738     Cache.applicationProperties.setProperty("FASTA_JVSUFFIX",
739             Boolean.toString(fastajv.isSelected()));
740     Cache.applicationProperties.setProperty("MSF_JVSUFFIX",
741             Boolean.toString(msfjv.isSelected()));
742     Cache.applicationProperties.setProperty("PFAM_JVSUFFIX",
743             Boolean.toString(pfamjv.isSelected()));
744     Cache.applicationProperties.setProperty("PILEUP_JVSUFFIX",
745             Boolean.toString(pileupjv.isSelected()));
746     Cache.applicationProperties.setProperty("PIR_JVSUFFIX",
747             Boolean.toString(pirjv.isSelected()));
748     Cache.applicationProperties.setProperty("PIR_MODELLER",
749             Boolean.toString(modellerOutput.isSelected()));
750     Cache.applicationProperties.setProperty("EXPORT_EMBBED_BIOJSON",
751             Boolean.toString(embbedBioJSON.isSelected()));
752     jalview.io.PIRFile.useModellerOutput = modellerOutput.isSelected();
753
754     Cache.applicationProperties.setProperty("FIGURE_AUTOIDWIDTH",
755             Boolean.toString(autoIdWidth.isSelected()));
756     userIdWidth_actionPerformed();
757     Cache.applicationProperties.setProperty("FIGURE_USERIDWIDTH",
758             userIdWidth.getText());
759
760     /*
761      * Save Editing settings
762      */
763     Cache.applicationProperties.setProperty("AUTO_CALC_CONSENSUS",
764             Boolean.toString(autoCalculateConsCheck.isSelected()));
765     Cache.applicationProperties.setProperty("SORT_BY_TREE",
766             Boolean.toString(sortByTree.isSelected()));
767     Cache.applicationProperties.setProperty("PAD_GAPS",
768             Boolean.toString(padGaps.isSelected()));
769
770     dasSource.saveProperties(Cache.applicationProperties);
771     wsPrefs.updateAndRefreshWsMenuConfig(false);
772     Cache.saveProperties();
773     Desktop.instance.doConfigureStructurePrefs();
774     try
775     {
776       frame.setClosed(true);
777     } catch (Exception ex)
778     {
779     }
780   }
781
782   /**
783    * Do any necessary validation before saving settings. Return focus to the
784    * first tab which fails validation.
785    * 
786    * @return
787    */
788   private boolean validateSettings()
789   {
790     if (!validateStructure())
791     {
792       structureTab.requestFocusInWindow();
793       return false;
794     }
795     return true;
796   }
797
798   @Override
799   protected boolean validateStructure()
800   {
801     return validateChimeraPath();
802
803   }
804
805   /**
806    * DOCUMENT ME!
807    */
808   @Override
809   public void startupFileTextfield_mouseClicked()
810   {
811     String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
812     JalviewFileChooser chooser = JalviewFileChooser
813             .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat);
814     chooser.setFileView(new JalviewFileView());
815     chooser.setDialogTitle(
816             MessageManager.getString("label.select_startup_file"));
817
818     int value = chooser.showOpenDialog(this);
819
820     if (value == JalviewFileChooser.APPROVE_OPTION)
821     {
822       FileFormatI format = chooser.getSelectedFormat();
823       if (format != null)
824       {
825         Cache.applicationProperties.setProperty("DEFAULT_FILE_FORMAT",
826                 format.getName());
827       }
828       startupFileTextfield
829               .setText(chooser.getSelectedFile().getAbsolutePath());
830     }
831   }
832
833   /**
834    * DOCUMENT ME!
835    * 
836    * @param e
837    *          DOCUMENT ME!
838    */
839   @Override
840   public void cancel_actionPerformed(ActionEvent e)
841   {
842     try
843     {
844       wsPrefs.updateWsMenuConfig(true);
845       wsPrefs.refreshWs_actionPerformed(e);
846       frame.setClosed(true);
847     } catch (Exception ex)
848     {
849     }
850   }
851
852   /**
853    * DOCUMENT ME!
854    * 
855    * @param e
856    *          DOCUMENT ME!
857    */
858   @Override
859   public void annotations_actionPerformed(ActionEvent e)
860   {
861     conservation.setEnabled(annotations.isSelected());
862     quality.setEnabled(annotations.isSelected());
863     identity.setEnabled(annotations.isSelected());
864     showOccupancy.setEnabled(annotations.isSelected());
865     showGroupConsensus.setEnabled(annotations.isSelected());
866     showGroupConservation.setEnabled(annotations.isSelected());
867     showConsensHistogram.setEnabled(annotations.isSelected()
868             && (identity.isSelected() || showGroupConsensus.isSelected()));
869     showConsensLogo.setEnabled(annotations.isSelected()
870             && (identity.isSelected() || showGroupConsensus.isSelected()));
871   }
872
873   @Override
874   public void newLink_actionPerformed(ActionEvent e)
875   {
876     GSequenceLink link = new GSequenceLink();
877     boolean valid = false;
878     while (!valid)
879     {
880       if (JvOptionPane.showInternalConfirmDialog(Desktop.desktop, link,
881               MessageManager.getString("label.new_sequence_url_link"),
882               JvOptionPane.OK_CANCEL_OPTION, -1,
883               null) == JvOptionPane.OK_OPTION)
884       {
885         if (link.checkValid())
886         {
887           if (((UrlLinkTableModel) linkUrlTable.getModel())
888                   .isUniqueName(link.getName()))
889           {
890             ((UrlLinkTableModel) linkUrlTable.getModel())
891                     .insertRow(link.getName(), link.getURL());
892             valid = true;
893           }
894           else
895           {
896             link.notifyDuplicate();
897             continue;
898           }
899         }
900       }
901       else
902       {
903         break;
904       }
905     }
906   }
907
908   @Override
909   public void editLink_actionPerformed(ActionEvent e)
910   {
911     GSequenceLink link = new GSequenceLink();
912
913     int index = linkUrlTable.getSelectedRow();
914     if (index == -1)
915     {
916       // button no longer enabled if row is not selected
917       Cache.log.debug("Edit with no row selected in linkUrlTable");
918       return;
919     }
920
921     int nameCol = ((UrlLinkTableModel) linkUrlTable.getModel())
922             .getNameColumn();
923     int urlCol = ((UrlLinkTableModel) linkUrlTable.getModel())
924             .getUrlColumn();
925     String oldName = linkUrlTable.getValueAt(index, nameCol).toString();
926     link.setName(oldName);
927     link.setURL(linkUrlTable.getValueAt(index, urlCol).toString());
928
929     boolean valid = false;
930     while (!valid)
931     {
932       if (JvOptionPane.showInternalConfirmDialog(Desktop.desktop, link,
933               MessageManager.getString("label.edit_sequence_url_link"),
934               JvOptionPane.OK_CANCEL_OPTION, -1,
935               null) == JvOptionPane.OK_OPTION)
936       {
937         if (link.checkValid())
938         {
939           if ((oldName.equals(link.getName()))
940                   || (((UrlLinkTableModel) linkUrlTable.getModel())
941                           .isUniqueName(link.getName())))
942           {
943             linkUrlTable.setValueAt(link.getName(), index, nameCol);
944             linkUrlTable.setValueAt(link.getURL(), index, urlCol);
945             valid = true;
946           }
947           else
948           {
949             link.notifyDuplicate();
950             continue;
951           }
952         }
953       }
954       else
955       {
956         break;
957       }
958     }
959   }
960
961   @Override
962   public void deleteLink_actionPerformed(ActionEvent e)
963   {
964     int index = linkUrlTable.getSelectedRow();
965     int modelIndex = -1;
966     if (index == -1)
967     {
968       // button no longer enabled if row is not selected
969       Cache.log.debug("Delete with no row selected in linkUrlTable");
970       return;
971     }
972     else
973     {
974       modelIndex = linkUrlTable.convertRowIndexToModel(index);
975     }
976
977     // make sure we use the model index to delete, and not the table index
978     ((UrlLinkTableModel) linkUrlTable.getModel()).removeRow(modelIndex);
979   }
980
981   @Override
982   public void defaultBrowser_mouseClicked(MouseEvent e)
983   {
984     JFileChooser chooser = new JFileChooser(".");
985     chooser.setDialogTitle(
986             MessageManager.getString("label.select_default_browser"));
987
988     int value = chooser.showOpenDialog(this);
989
990     if (value == JFileChooser.APPROVE_OPTION)
991     {
992       defaultBrowser.setText(chooser.getSelectedFile().getAbsolutePath());
993     }
994
995   }
996
997   /*
998    * (non-Javadoc)
999    * 
1000    * @see
1001    * jalview.jbgui.GPreferences#showunconserved_actionPerformed(java.awt.event
1002    * .ActionEvent)
1003    */
1004   @Override
1005   protected void showunconserved_actionPerformed(ActionEvent e)
1006   {
1007     // TODO Auto-generated method stub
1008     super.showunconserved_actionPerformed(e);
1009   }
1010
1011   public static List<String> getGroupURLLinks()
1012   {
1013     return groupURLLinks;
1014   }
1015
1016   @Override
1017   public void minColour_actionPerformed(JPanel panel)
1018   {
1019     Color col = JColorChooser.showDialog(this,
1020             MessageManager.getString("label.select_colour_minimum_value"),
1021             minColour.getBackground());
1022     if (col != null)
1023     {
1024       panel.setBackground(col);
1025     }
1026     panel.repaint();
1027   }
1028
1029   @Override
1030   public void maxColour_actionPerformed(JPanel panel)
1031   {
1032     Color col = JColorChooser.showDialog(this,
1033             MessageManager.getString("label.select_colour_maximum_value"),
1034             maxColour.getBackground());
1035     if (col != null)
1036     {
1037       panel.setBackground(col);
1038     }
1039     panel.repaint();
1040   }
1041
1042   @Override
1043   public void gapColour_actionPerformed(JButton btn)
1044   {
1045     Color col = JColorChooser.showDialog(this,
1046             MessageManager.getString("label.select_gap_colour"),
1047             gapColour.getForeground());
1048     if (col != null)
1049     {
1050       btn.setForeground(col);
1051     }
1052     btn.repaint();
1053   }
1054
1055   @Override
1056   public void hiddenColour_actionPerformed(JButton btn)
1057   {
1058     Color col = JColorChooser.showDialog(this,
1059             MessageManager.getString("label.select_hidden_colour"),
1060             hiddenColour.getForeground());
1061     if (col != null)
1062     {
1063       btn.setForeground(col);
1064     }
1065     btn.repaint();
1066   }
1067
1068   @Override
1069   protected void userIdWidth_actionPerformed()
1070   {
1071     try
1072     {
1073       String val = userIdWidth.getText().trim();
1074       if (val.length() > 0)
1075       {
1076         Integer iw = Integer.parseInt(val);
1077         if (iw.intValue() < 12)
1078         {
1079           throw new NumberFormatException();
1080         }
1081         userIdWidth.setText(iw.toString());
1082       }
1083     } catch (NumberFormatException x)
1084     {
1085       JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1086               MessageManager
1087                       .getString("warn.user_defined_width_requirements"),
1088               MessageManager.getString("label.invalid_id_column_width"),
1089               JvOptionPane.WARNING_MESSAGE);
1090       userIdWidth.setText("");
1091     }
1092   }
1093
1094   @Override
1095   protected void autoIdWidth_actionPerformed()
1096   {
1097     userIdWidth.setEnabled(!autoIdWidth.isSelected());
1098     userIdWidthlabel.setEnabled(!autoIdWidth.isSelected());
1099   }
1100
1101   /**
1102    * Returns true if chimera path is to a valid executable, else show an error
1103    * dialog.
1104    */
1105   private boolean validateChimeraPath()
1106   {
1107     if (chimeraPath.getText().trim().length() > 0)
1108     {
1109       File f = new File(chimeraPath.getText());
1110       if (!f.canExecute())
1111       {
1112         JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1113                 MessageManager.getString("label.invalid_chimera_path"),
1114                 MessageManager.getString("label.invalid_name"),
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(Desktop.desktop,
1152               JvSwingUtils.wrapTooltip(true,
1153                       MessageManager.getString("label.chimera_missing")),
1154               "", JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
1155               null, options, options[0]);
1156       if (showHelp == JvOptionPane.NO_OPTION)
1157       {
1158         try
1159         {
1160           Help.showHelpWindow(HelpId.StructureViewer);
1161         } catch (HelpSetException e)
1162         {
1163           e.printStackTrace();
1164         }
1165       }
1166     }
1167   }
1168
1169   public class OptionsParam
1170   {
1171     private String name;
1172
1173     private String code;
1174
1175     public OptionsParam(String name, String code)
1176     {
1177       this.name = name;
1178       this.code = code;
1179     }
1180
1181     public String getName()
1182     {
1183       return name;
1184     }
1185
1186     public void setName(String name)
1187     {
1188       this.name = name;
1189     }
1190
1191     public String getCode()
1192     {
1193       return code;
1194     }
1195
1196     public void setCode(String code)
1197     {
1198       this.code = code;
1199     }
1200
1201     @Override
1202     public String toString()
1203     {
1204       return name;
1205     }
1206
1207     @Override
1208     public boolean equals(Object that)
1209     {
1210       if (!(that instanceof OptionsParam))
1211       {
1212         return false;
1213       }
1214       return this.code.equalsIgnoreCase(((OptionsParam) that).code);
1215     }
1216
1217     @Override
1218     public int hashCode()
1219     {
1220       return name.hashCode() + code.hashCode();
1221     }
1222   }
1223
1224   private class UrlListSelectionHandler implements ListSelectionListener
1225   {
1226
1227     @Override
1228     public void valueChanged(ListSelectionEvent e)
1229     {
1230       ListSelectionModel lsm = (ListSelectionModel) e.getSource();
1231
1232       int index = lsm.getMinSelectionIndex();
1233       if (index == -1)
1234       {
1235         // no selection, so disable delete/edit buttons
1236         editLink.setEnabled(false);
1237         deleteLink.setEnabled(false);
1238         return;
1239       }
1240       int modelIndex = linkUrlTable.convertRowIndexToModel(index);
1241
1242       // enable/disable edit and delete link buttons
1243       if (((UrlLinkTableModel) linkUrlTable.getModel())
1244               .isRowDeletable(modelIndex))
1245       {
1246         deleteLink.setEnabled(true);
1247       }
1248       else
1249       {
1250         deleteLink.setEnabled(false);
1251       }
1252
1253       if (((UrlLinkTableModel) linkUrlTable.getModel())
1254               .isRowEditable(modelIndex))
1255       {
1256         editLink.setEnabled(true);
1257       }
1258       else
1259       {
1260         editLink.setEnabled(false);
1261       }
1262     }
1263   }
1264 }