JAL-4257 JAL-4159 JAL-789 tidy up passing of score model to pairwise alignment direct...
[jalview.git] / src / jalview / gui / CalculationChooser.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.TreeBuilder;
24 import jalview.analysis.scoremodels.ScoreModels;
25 import jalview.analysis.scoremodels.SimilarityParams;
26 import jalview.api.analysis.ScoreModelI;
27 import jalview.api.analysis.SimilarityParamsI;
28 import jalview.bin.Cache;
29 import jalview.datamodel.SequenceGroup;
30 import jalview.util.MessageManager;
31 import jalview.viewmodel.AlignmentViewport;
32 import java.awt.BorderLayout;
33 import java.awt.Color;
34 import java.awt.Component;
35 import java.awt.Dimension;
36 import java.awt.FlowLayout;
37 import java.awt.Font;
38 import java.awt.GridLayout;
39 import java.awt.Insets;
40 import java.awt.event.ActionEvent;
41 import java.awt.event.ActionListener;
42 import java.awt.event.FocusEvent;
43 import java.awt.event.FocusListener;
44 import java.awt.event.MouseAdapter;
45 import java.awt.event.MouseEvent;
46 import java.beans.PropertyVetoException;
47 import java.util.ArrayList;
48 import java.util.List;
49
50 import javax.swing.BorderFactory;
51 import javax.swing.ButtonGroup;
52 import javax.swing.DefaultComboBoxModel;
53 import javax.swing.JButton;
54 import javax.swing.JCheckBox;
55 import javax.swing.JComboBox;
56 import javax.swing.JInternalFrame;
57 import javax.swing.JLabel;
58 import javax.swing.JLayeredPane;
59 import javax.swing.JPanel;
60 import javax.swing.JRadioButton;
61 import javax.swing.event.InternalFrameAdapter;
62 import javax.swing.event.InternalFrameEvent;
63
64 import jalview.analysis.AlignmentUtils;
65 import jalview.analysis.TreeBuilder;
66 import jalview.analysis.scoremodels.ScoreMatrix;
67 import jalview.analysis.scoremodels.ScoreModels;
68 import jalview.analysis.scoremodels.SimilarityParams;
69 import jalview.api.analysis.ScoreModelI;
70 import jalview.api.analysis.SimilarityParamsI;
71 import jalview.bin.Cache;
72 import jalview.datamodel.AlignmentAnnotation;
73 import jalview.datamodel.SequenceGroup;
74 import jalview.util.MessageManager;
75
76 /**
77  * A dialog where a user can choose and action Tree or PCA calculation options
78  */
79 public class CalculationChooser extends JPanel
80 {
81   /*
82    * flag for whether gap matches residue in the PID calculation for a Tree
83    * - true gives Jalview 2.10.1 behaviour
84    * - set to false (using Groovy) for a more correct tree
85    * (JAL-374)
86    */
87   private static boolean treeMatchGaps = true;
88
89   private static final Font VERDANA_11PT = new Font("Verdana", 0, 11);
90
91   private static final int MIN_PAIRWISE_SELECTION = 2;
92
93   private static final int MIN_TREE_SELECTION = 3;
94
95   private static final int MIN_PCA_SELECTION = 4;
96
97   private String secondaryStructureModelName;
98
99   private void getSecondaryStructureModelName()
100   {
101
102     ScoreModels scoreModels = ScoreModels.getInstance();
103     for (ScoreModelI sm : scoreModels.getModels())
104     {
105       if (sm.isSecondaryStructure())
106       {
107         secondaryStructureModelName = sm.getName();
108       }
109     }
110
111   }
112
113   /**
114    * minimum number of sequences needed for PASIMAP is 9 (so each has 8
115    * connections)
116    */
117   private static final int MIN_PASIMAP_SELECTION = 9;
118
119   AlignFrame af;
120
121   JRadioButton pairwise;
122
123   JRadioButton pca;
124
125   JRadioButton pasimap;
126
127   JRadioButton neighbourJoining;
128
129   JRadioButton averageDistance;
130
131   JComboBox<String> modelNames;
132
133   JComboBox<String> ssSourceDropdown;
134
135   JButton calculate;
136
137   private JInternalFrame frame;
138
139   private JCheckBox includeGaps;
140
141   private JCheckBox matchGaps;
142
143   private JCheckBox includeGappedColumns;
144
145   private JCheckBox shorterSequence;
146
147   final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer();
148
149   List<String> tips = new ArrayList<>();
150
151   /*
152    * the most recently opened PCA results panel
153    */
154   private PCAPanel pcaPanel;
155
156   private PaSiMapPanel pasimapPanel;
157
158   /**
159    * Constructor
160    * 
161    * @param af
162    */
163   public CalculationChooser(AlignFrame alignFrame)
164   {
165     this.af = alignFrame;
166     init();
167     af.alignPanel.setCalculationDialog(this);
168
169   }
170
171   /**
172    * Lays out the panel and adds it to the desktop
173    */
174   void init()
175   {
176     getSecondaryStructureModelName();
177     setLayout(new BorderLayout());
178     frame = new JInternalFrame();
179     frame.setFrameIcon(null);
180     frame.setContentPane(this);
181     this.setBackground(Color.white);
182     frame.addFocusListener(new FocusListener()
183     {
184
185       @Override
186       public void focusLost(FocusEvent e)
187       {
188       }
189
190       @Override
191       public void focusGained(FocusEvent e)
192       {
193         validateCalcTypes();
194       }
195     });
196     /*
197      * Layout consists of 3 or 4 panels:
198      * - first with choice of PCA or tree method NJ or AV
199      * - second with choice of score model
200      * - third with score model parameter options [suppressed]
201      * - fourth with OK and Cancel
202      */
203     pca = new JRadioButton(
204             MessageManager.getString("label.principal_component_analysis"));
205     pca.setOpaque(false);
206
207     pasimap = new JRadioButton( // create the JRadioButton for pasimap with
208                                 // label.pasimap as its text
209             MessageManager.getString("label.pasimap"));
210     pasimap.setOpaque(false);
211
212     neighbourJoining = new JRadioButton(
213             MessageManager.getString("label.tree_calc_nj"));
214     neighbourJoining.setSelected(true);
215     neighbourJoining.setOpaque(false);
216
217     averageDistance = new JRadioButton(
218             MessageManager.getString("label.tree_calc_av"));
219     averageDistance.setOpaque(false);
220
221     pairwise = new JRadioButton(
222             MessageManager.getString("action.pairwise_alignment"));
223     pairwise.setOpaque(false);
224
225     JPanel calcChoicePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
226     calcChoicePanel.setOpaque(false);
227
228     // first create the Tree calculation's border panel
229     JPanel treePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
230     treePanel.setOpaque(false);
231
232     JvSwingUtils.createTitledBorder(treePanel,
233             MessageManager.getString("label.tree"), true);
234
235     // then copy the inset dimensions for the border-less PCA panel
236     JPanel pcaBorderless = new JPanel(new FlowLayout(FlowLayout.LEFT));
237     Insets b = treePanel.getBorder().getBorderInsets(treePanel);
238     pcaBorderless.setBorder(
239             BorderFactory.createEmptyBorder(2, b.left, 2, b.right));
240     pcaBorderless.setOpaque(false);
241
242     pcaBorderless.add(pca, FlowLayout.LEFT);
243     calcChoicePanel.add(pcaBorderless, FlowLayout.LEFT);
244
245     // create pasimap panel
246     JPanel pasimapBorderless = new JPanel(new FlowLayout(FlowLayout.LEFT)); // create
247                                                                             // new
248                                                                             // JPanel
249                                                                             // (button)
250                                                                             // for
251                                                                             // pasimap
252     pasimapBorderless.setBorder(
253             BorderFactory.createEmptyBorder(2, b.left, 2, b.right)); // set
254                                                                      // border
255                                                                      // (margin)
256                                                                      // for
257                                                                      // button
258                                                                      // (same as
259                                                                      // treePanel
260                                                                      // and pca)
261     pasimapBorderless.setOpaque(false); // false -> stops every pixel inside
262                                         // border from being painted
263     pasimapBorderless.add(pasimap, FlowLayout.LEFT); // add pasimap button to
264                                                      // the JPanel
265     calcChoicePanel.add(pasimapBorderless, FlowLayout.LEFT); // add button with
266                                                              // border and
267                                                              // everything to
268                                                              // the overall
269                                                              // ChoicePanel
270
271     treePanel.add(neighbourJoining);
272     treePanel.add(averageDistance);
273
274     calcChoicePanel.add(treePanel);
275     calcChoicePanel.add(pairwise, FlowLayout.CENTER);
276
277     ButtonGroup calcTypes = new ButtonGroup();
278     calcTypes.add(pca);
279     calcTypes.add(pasimap);
280     calcTypes.add(neighbourJoining);
281     calcTypes.add(averageDistance);
282     calcTypes.add(pairwise);
283
284     ActionListener calcChanged = new ActionListener()
285     {
286       @Override
287       public void actionPerformed(ActionEvent e)
288       {
289         validateCalcTypes();
290       }
291     };
292     pca.addActionListener(calcChanged);
293     pasimap.addActionListener(calcChanged); // add the calcChanged
294                                             // ActionListener to pasimap -->
295                                             // <++> idk
296     neighbourJoining.addActionListener(calcChanged);
297     averageDistance.addActionListener(calcChanged);
298
299     // to do
300     ssSourceDropdown = buildSSSourcesOptionsList();
301     ssSourceDropdown.setVisible(false); // Initially hide the dropdown
302     pairwise.addActionListener(calcChanged);
303     /*
304      * score models drop-down - with added tooltips!
305      */
306     modelNames = buildModelOptionsList();
307
308     // Step 3: Show or Hide Dropdown Based on Selection
309     modelNames.addActionListener(new ActionListener()
310     {
311       @Override
312       public void actionPerformed(ActionEvent e)
313       {
314         String selectedModel = modelNames.getSelectedItem().toString();
315
316         if (selectedModel.equals(secondaryStructureModelName))
317         {
318           ssSourceDropdown.setVisible(true);
319         }
320         else
321         {
322           ssSourceDropdown.setVisible(false);
323         }
324       }
325     });
326
327     JPanel scoreModelPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
328     scoreModelPanel.setOpaque(false);
329     scoreModelPanel.add(modelNames);
330     scoreModelPanel.add(ssSourceDropdown);
331
332     /*
333      * score model parameters
334      */
335     JPanel paramsPanel = new JPanel(new GridLayout(5, 1));
336     paramsPanel.setOpaque(false);
337     includeGaps = new JCheckBox("Include gaps");
338     matchGaps = new JCheckBox("Match gaps");
339     includeGappedColumns = new JCheckBox("Include gapped columns");
340     shorterSequence = new JCheckBox("Match on shorter sequence");
341     paramsPanel.add(new JLabel("Pairwise sequence scoring options"));
342     paramsPanel.add(includeGaps);
343     paramsPanel.add(matchGaps);
344     paramsPanel.add(includeGappedColumns);
345     paramsPanel.add(shorterSequence);
346
347     /*
348      * OK / Cancel buttons
349      */
350     calculate = new JButton(MessageManager.getString("action.calculate"));
351     calculate.setFont(VERDANA_11PT);
352     calculate.addActionListener(new java.awt.event.ActionListener()
353     {
354       @Override
355       public void actionPerformed(ActionEvent e)
356       {
357         calculate_actionPerformed();
358       }
359     });
360     JButton close = new JButton(MessageManager.getString("action.close"));
361     close.setFont(VERDANA_11PT);
362     close.addActionListener(new java.awt.event.ActionListener()
363     {
364       @Override
365       public void actionPerformed(ActionEvent e)
366       {
367         close_actionPerformed();
368       }
369     });
370     JPanel actionPanel = new JPanel();
371     actionPanel.setOpaque(false);
372     actionPanel.add(calculate);
373     actionPanel.add(close);
374
375     boolean includeParams = false;
376     this.add(calcChoicePanel, BorderLayout.CENTER);
377     calcChoicePanel.add(scoreModelPanel);
378     if (includeParams)
379     {
380       scoreModelPanel.add(paramsPanel);
381     }
382     this.add(actionPanel, BorderLayout.SOUTH);
383
384     int width = 365;
385     int height = includeParams ? 420 : 240;
386
387     setMinimumSize(new Dimension(325, height - 10));
388     String title = MessageManager.getString("label.choose_calculation");
389     if (af.getViewport().getViewName() != null)
390     {
391       title = title + " (" + af.getViewport().getViewName() + ")";
392     }
393
394     Desktop.addInternalFrame(frame, title, width, height, false);
395     calcChoicePanel.doLayout();
396     revalidate();
397     /*
398      * null the AlignmentPanel's reference to the dialog when it is closed
399      */
400     frame.addInternalFrameListener(new InternalFrameAdapter()
401     {
402       @Override
403       public void internalFrameClosed(InternalFrameEvent evt)
404       {
405         af.alignPanel.setCalculationDialog(null);
406       };
407     });
408
409     validateCalcTypes();
410     frame.setLayer(JLayeredPane.PALETTE_LAYER);
411   }
412
413   /**
414    * enable calculations applicable for the current alignment or selection.
415    */
416   protected void validateCalcTypes()
417   {
418     int size = af.getViewport().getAlignment().getHeight();
419     if (af.getViewport().getSelectionGroup() != null)
420     {
421       size = af.getViewport().getSelectionGroup().getSize();
422     }
423
424     /*
425      * disable calc options for which there is insufficient input data
426      * return value of true means enabled and selected
427      */
428     boolean checkPca = checkEnabled(pca, size, MIN_PCA_SELECTION);
429     boolean checkPasimap = checkEnabled(pasimap, size,
430             MIN_PASIMAP_SELECTION); // check if pasimap is enabled and min_size
431                                     // is fulfilled
432     boolean checkNeighbourJoining = checkEnabled(neighbourJoining, size,
433             MIN_TREE_SELECTION);
434     boolean checkAverageDistance = checkEnabled(averageDistance, size,
435             MIN_TREE_SELECTION);
436     boolean checkPairwise = checkEnabled(pairwise, size,
437             MIN_PAIRWISE_SELECTION);
438
439     if (checkPca || checkPasimap || checkPca || checkNeighbourJoining
440             || checkAverageDistance || checkPairwise)
441     {
442       calculate.setToolTipText(null);
443       calculate.setEnabled(true);
444     }
445     else
446     {
447       calculate.setEnabled(false);
448     }
449     updateScoreModels(modelNames, tips);
450   }
451
452   /**
453    * Check the input and disable a calculation's radio button if necessary. A
454    * tooltip is shown for disabled calculations.
455    * 
456    * @param calc
457    *          - radio button for the calculation being validated
458    * @param size
459    *          - size of input to calculation
460    * @param minsize
461    *          - minimum size for calculation
462    * @return true if size >= minsize and calc.isSelected
463    */
464   private boolean checkEnabled(JRadioButton calc, int size, int minsize)
465   {
466     String ttip = MessageManager
467             .formatMessage("label.you_need_at_least_n_sequences", minsize);
468
469     calc.setEnabled(size >= minsize);
470     if (!calc.isEnabled())
471     {
472       calc.setToolTipText(ttip);
473     }
474     else
475     {
476       calc.setToolTipText(null);
477     }
478     if (calc.isSelected())
479     {
480       modelNames.setEnabled(calc.isEnabled());
481       if (calc.isEnabled())
482       {
483         return true;
484       }
485       else
486       {
487         calculate.setToolTipText(ttip);
488       }
489     }
490     return false;
491   }
492
493   /**
494    * A rather elaborate helper method (blame Swing, not me) that builds a
495    * drop-down list of score models (by name) with descriptions as tooltips.
496    * There is also a tooltip shown for the currently selected item when hovering
497    * over it (without opening the list).
498    */
499   protected JComboBox<String> buildModelOptionsList()
500   {
501     final JComboBox<String> scoreModelsCombo = new JComboBox<>();
502     scoreModelsCombo.setRenderer(renderer);
503
504     /*
505      * show tooltip on mouse over the combobox
506      * note the listener has to be on the components that make up
507      * the combobox, doesn't work if just on the combobox
508      */
509     final MouseAdapter mouseListener = new MouseAdapter()
510     {
511       @Override
512       public void mouseEntered(MouseEvent e)
513       {
514         scoreModelsCombo.setToolTipText(
515                 tips.get(scoreModelsCombo.getSelectedIndex()));
516       }
517
518       @Override
519       public void mouseExited(MouseEvent e)
520       {
521         scoreModelsCombo.setToolTipText(null);
522       }
523     };
524     for (Component c : scoreModelsCombo.getComponents())
525     {
526       c.addMouseListener(mouseListener);
527     }
528
529     updateScoreModels(scoreModelsCombo, tips);
530
531     /*
532      * set the list of tooltips on the combobox's renderer
533      */
534     renderer.setTooltips(tips);
535
536     return scoreModelsCombo;
537   }
538
539   private JComboBox<String> buildSSSourcesOptionsList()
540   {
541     final JComboBox<String> comboBox = new JComboBox<>();
542     Object curSel = comboBox.getSelectedItem();
543     DefaultComboBoxModel<String> sourcesModel = new DefaultComboBoxModel<>();
544
545     List<String> ssSources = getApplicableSecondaryStructureSources();
546
547     boolean selectedIsPresent = false;
548     for (String source : ssSources)
549     {
550       if (curSel != null && source.equals(curSel))
551       {
552         selectedIsPresent = true;
553         curSel = source;
554       }
555       sourcesModel.addElement(source);
556
557     }
558
559     if (selectedIsPresent)
560     {
561       sourcesModel.setSelectedItem(curSel);
562     }
563     comboBox.setModel(sourcesModel);
564
565     return comboBox;
566   }
567
568   private void updateScoreModels(JComboBox<String> comboBox,
569           List<String> toolTips)
570   {
571     Object curSel = comboBox.getSelectedItem();
572     toolTips.clear();
573     DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
574
575     /*
576      * select the score models applicable to the alignment type
577      */
578     boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
579     AlignmentAnnotation[] alignmentAnnotations = af.getViewport()
580             .getAlignment().getAlignmentAnnotation();
581
582     boolean ssPresent = AlignmentUtils
583             .isSecondaryStructurePresent(alignmentAnnotations);
584
585     List<ScoreModelI> models = getApplicableScoreModels(nucleotide,
586             pca.isSelected(), ssPresent, pasimap.isSelected());
587
588     /*
589      * now we can actually add entries to the combobox,
590      * remembering their descriptions for tooltips
591      */
592     boolean selectedIsPresent = false;
593     for (ScoreModelI sm : models)
594     {
595       if (curSel != null && sm.getName().equals(curSel))
596       {
597         selectedIsPresent = true;
598         curSel = sm.getName();
599       }
600       model.addElement(sm.getName());
601
602       /*
603        * tooltip is description if provided, else text lookup with
604        * fallback on the model name
605        */
606       String tooltip = sm.getDescription();
607       if (tooltip == null)
608       {
609         tooltip = MessageManager.getStringOrReturn("label.score_model_",
610                 sm.getName());
611       }
612       toolTips.add(tooltip);
613     }
614
615     if (selectedIsPresent)
616     {
617       model.setSelectedItem(curSel);
618     }
619     // finally, update the model
620     comboBox.setModel(model);
621     comboBox.setEnabled(model.getSize() > 0);
622
623   }
624
625   /**
626    * Builds a list of score models which are applicable for the alignment and
627    * calculation type (peptide or generic models for protein, nucleotide or
628    * generic models for nucleotide).
629    * <p>
630    * As a special case, includes BLOSUM62 as an extra option for nucleotide PCA.
631    * This is for backwards compatibility with Jalview prior to 2.8 when BLOSUM62
632    * was the only score matrix supported. This is included if property
633    * BLOSUM62_PCA_FOR_NUCLEOTIDE is set to true in the Jalview properties file.
634    * 
635    * @param nucleotide
636    * @param forPca
637    * @param ssPresent - include secondary structure similarity model
638    * @param forPasimap - limit to ScoreMatrix based models - allows use of AlignSeq
639    * @return
640    */
641   protected static List<ScoreModelI> getApplicableScoreModels(
642           boolean nucleotide, boolean forPca, boolean ssPresent,
643           boolean forPasimap)
644   {
645     List<ScoreModelI> filtered = new ArrayList<>();
646
647     ScoreModels scoreModels = ScoreModels.getInstance();
648     for (ScoreModelI sm : scoreModels.getModels())
649     {
650       if ((!forPasimap || sm instanceof ScoreMatrix)  
651               && (!nucleotide && sm.isProtein() || nucleotide && sm.isDNA()
652                       || sm.isSecondaryStructure() && ssPresent))
653
654       {
655         filtered.add(sm);
656       }
657     }
658
659     /*
660      * special case: add BLOSUM62 as last option for nucleotide PCA, 
661      * for backwards compatibility with Jalview < 2.8 (JAL-2962)
662      */
663     if (!forPasimap && nucleotide && forPca
664             && Cache.getDefault("BLOSUM62_PCA_FOR_NUCLEOTIDE", false))
665     {
666       filtered.add(scoreModels.getBlosum62());
667     }
668
669     return filtered;
670   }
671
672   protected List<String> getApplicableSecondaryStructureSources()
673   {
674     AlignmentAnnotation[] annotations = af.getViewport().getAlignment()
675             .getAlignmentAnnotation();
676
677     List<String> ssSources = AlignmentUtils
678             .getSecondaryStructureSources(annotations);
679     // List<String> ssSources =
680     // AlignmentUtils.extractSSSourceInAlignmentAnnotation(annotations);
681
682     return ssSources;
683   }
684
685   /**
686    * Open and calculate the selected tree or PCA on 'OK'
687    */
688   protected void calculate_actionPerformed()
689   {
690     boolean doPCA = pca.isSelected();
691     boolean doPaSiMap = pasimap.isSelected();
692     boolean doPairwise = pairwise.isSelected();
693     String modelName = modelNames.getSelectedItem() == null ? ""
694             : modelNames.getSelectedItem().toString();
695     String ssSource = "";
696     Object selectedItem = ssSourceDropdown.getSelectedItem();
697     if (selectedItem != null)
698     {
699       ssSource = selectedItem.toString();
700     }
701     SimilarityParams params = getSimilarityParameters(doPCA);
702     if (ssSource.length() > 0)
703     {
704       params.setSecondaryStructureSource(ssSource);
705     }
706
707     if (doPCA)
708     {
709       openPcaPanel(modelName, params);
710     }
711     else if (doPaSiMap)
712     {
713       openPasimapPanel(modelName, params);
714     }
715     else if (doPairwise)
716     {
717       openPairwisePanel(modelName, params);
718     }
719     else
720     {
721       openTreePanel(modelName, params);
722     }
723
724     closeFrame();
725   }
726
727   private void openPairwisePanel(String modelName, SimilarityParamsI params)
728   {
729     ScoreModelI sm = ScoreModels.getInstance().getScoreModel(modelName,
730             af.alignPanel);
731     if (sm == null || !(sm instanceof ScoreMatrix))
732     {
733       return;
734     }
735     new Thread(new Runnable()
736     {
737       @Override
738       public void run()
739       {
740         JInternalFrame frame = new JInternalFrame();
741         frame.setFrameIcon(null);
742         frame.setContentPane(
743                 new PairwiseAlignPanel(af.getViewport(), (ScoreMatrix) sm));
744         Desktop.addInternalFrame(frame,
745                 MessageManager.getString("action.pairwise_alignment"), 600,
746                 500);
747       }
748     }).start();
749   }
750
751   /**
752    * Open a new Tree panel on the desktop
753    * 
754    * @param modelName
755    * @param params
756    */
757   protected void openTreePanel(String modelName, SimilarityParamsI params)
758   {
759     /*
760      * gui validation shouldn't allow insufficient sequences here, but leave
761      * this check in in case this method gets exposed programmatically in future
762      */
763     AlignViewport viewport = af.getViewport();
764     SequenceGroup sg = viewport.getSelectionGroup();
765     if (sg != null && sg.getSize() < MIN_TREE_SELECTION)
766     {
767       JvOptionPane.showMessageDialog(Desktop.desktop,
768               MessageManager.formatMessage(
769                       "label.you_need_at_least_n_sequences",
770                       MIN_TREE_SELECTION),
771               MessageManager.getString("label.not_enough_sequences"),
772               JvOptionPane.WARNING_MESSAGE);
773       return;
774     }
775
776     String treeType = neighbourJoining.isSelected()
777             ? TreeBuilder.NEIGHBOUR_JOINING
778             : TreeBuilder.AVERAGE_DISTANCE;
779     af.newTreePanel(treeType, modelName, params);
780   }
781
782   /**
783    * Open a new PCA panel on the desktop
784    * 
785    * @param modelName
786    * @param params
787    */
788   protected void openPcaPanel(String modelName, SimilarityParamsI params)
789   {
790     AlignViewport viewport = af.getViewport();
791
792     /*
793      * gui validation shouldn't allow insufficient sequences here, but leave
794      * this check in in case this method gets exposed programmatically in future
795      */
796     if (((viewport.getSelectionGroup() != null)
797             && (viewport.getSelectionGroup().getSize() < MIN_PCA_SELECTION)
798             && (viewport.getSelectionGroup().getSize() > 0))
799             || (viewport.getAlignment().getHeight() < MIN_PCA_SELECTION))
800     {
801       JvOptionPane.showInternalMessageDialog(this,
802               MessageManager.formatMessage(
803                       "label.you_need_at_least_n_sequences",
804                       MIN_PCA_SELECTION),
805               MessageManager
806                       .getString("label.sequence_selection_insufficient"),
807               JvOptionPane.WARNING_MESSAGE);
808       return;
809     }
810
811     /*
812      * construct the panel and kick off its calculation thread
813      */
814     pcaPanel = new PCAPanel(af.alignPanel, modelName, params);
815     new Thread(pcaPanel).start();
816
817   }
818
819   /**
820    * Open a new PaSiMap panel on the desktop
821    * 
822    * @param modelName
823    * @param params
824    */
825   protected void openPasimapPanel(String modelName,
826           SimilarityParamsI params)
827   {
828     AlignViewport viewport = af.getViewport();
829
830     /*
831      * gui validation shouldn't allow insufficient sequences here, but leave
832      * this check in in case this method gets exposed programmatically in future
833      */
834     if (((viewport.getSelectionGroup() != null)
835             && (viewport.getSelectionGroup()
836                     .getSize() < MIN_PASIMAP_SELECTION)
837             && (viewport.getSelectionGroup().getSize() > 0))
838             || (viewport.getAlignment()
839                     .getHeight() < MIN_PASIMAP_SELECTION))
840     {
841       JvOptionPane.showInternalMessageDialog(this,
842               MessageManager.formatMessage(
843                       "label.you_need_at_least_n_sequences",
844                       MIN_PASIMAP_SELECTION),
845               MessageManager
846                       .getString("label.sequence_selection_insufficient"),
847               JvOptionPane.WARNING_MESSAGE);
848       return;
849     }
850
851     /*
852      * construct the panel and kick off its calculation thread
853      */
854     pasimapPanel = new PaSiMapPanel(af.alignPanel, modelName);
855     new Thread(pasimapPanel).start();
856
857   }
858
859   /**
860    * 
861    */
862   protected void closeFrame()
863   {
864     try
865     {
866       frame.setClosed(true);
867     } catch (PropertyVetoException ex)
868     {
869     }
870   }
871
872   /**
873    * Returns a data bean holding parameters for similarity (or distance) model
874    * calculation
875    * 
876    * @param doPCA
877    * @return
878    */
879   protected SimilarityParams getSimilarityParameters(boolean doPCA)
880   {
881     // commented out: parameter choices read from gui widgets
882     // SimilarityParamsI params = new SimilarityParams(
883     // includeGappedColumns.isSelected(), matchGaps.isSelected(),
884     // includeGaps.isSelected(), shorterSequence.isSelected());
885
886     boolean includeGapGap = true;
887     boolean includeGapResidue = true;
888     boolean matchOnShortestLength = false;
889
890     /*
891      * 'matchGaps' flag is only used in the PID calculation
892      * - set to false for PCA so that PCA using PID reproduces SeqSpace PCA
893      * - set to true for Tree to reproduce Jalview 2.10.1 calculation
894      * - set to false for Tree for a more correct calculation (JAL-374)
895      */
896     boolean matchGap = doPCA ? false : treeMatchGaps;
897
898     return new SimilarityParams(includeGapGap, matchGap, includeGapResidue,
899             matchOnShortestLength);
900   }
901
902   /**
903    * Closes dialog on Close button press
904    */
905   protected void close_actionPerformed()
906   {
907     try
908     {
909       frame.setClosed(true);
910     } catch (Exception ex)
911     {
912     }
913   }
914
915   public PCAPanel getPcaPanel()
916   {
917     return pcaPanel;
918   }
919 }