Clover icon

Coverage Report

  1. Project Clover database Mon Dec 1 2025 13:17:41 GMT
  2. Package jalview.gui

File CalculationChooser.java

 

Coverage histogram

../../img/srcFileCovDistChart1.png
57% of files have more coverage

Code metrics

78
282
29
1
1,022
656
101
0.36
9.72
29
3.48

Classes

Class Line # Actions
CalculationChooser 73 282 101
0.0334190243.3%
 

Contributing tests

This file is covered by 1 test. .

Source view

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