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