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