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