Clover icon

Coverage Report

  1. Project Clover database Fri Dec 12 2025 14:18:41 GMT
  2. Package jalview.gui

File CalculationChooser.java

 

Coverage histogram

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

Code metrics

84
293
29
1
1,062
693
112
0.38
10.1
29
3.86

Classes

Class Line # Actions
CalculationChooser 73 293 112
0.0320197053.2%
 

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 int minPca = af.getViewport().getAlignment()
436    .isSecondaryStructurePresent() ? 1 : MIN_PCA_SELECTION;
437  0 boolean checkPca = checkEnabled(pca, size, minPca);
438  0 boolean checkPasimap = checkEnabled(pasimap, size,
439    MIN_PASIMAP_SELECTION); // check if pasimap is enabled and min_size
440    // is fulfilled
441  0 int minTree = af.getViewport().getAlignment()
442    .isSecondaryStructurePresent() ? 1 : MIN_TREE_SELECTION;
443  0 boolean checkNeighbourJoining = checkEnabled(neighbourJoining, size,
444    minTree);
445  0 boolean checkAverageDistance = checkEnabled(averageDistance, size,
446    minTree);
447  0 boolean checkPairwise = checkEnabled(pairwise, size,
448    MIN_PAIRWISE_SELECTION);
449   
450  0 if (checkPca || checkPasimap || checkPca || checkNeighbourJoining
451    || checkAverageDistance || checkPairwise)
452    {
453  0 calculate.setToolTipText(null);
454  0 calculate.setEnabled(true);
455    }
456    else
457    {
458  0 calculate.setEnabled(false);
459    }
460  0 updateScoreModels(modelNames, tips);
461    }
462   
463    /**
464    * Check the input and disable a calculation's radio button if necessary. A
465    * tooltip is shown for disabled calculations.
466    *
467    * @param calc
468    * - radio button for the calculation being validated
469    * @param size
470    * - size of input to calculation
471    * @param minsize
472    * - minimum size for calculation
473    * @return true if size >= minsize and calc.isSelected
474    */
 
475  0 toggle private boolean checkEnabled(JRadioButton calc, int size, int minsize)
476    {
477  0 String ttip = MessageManager
478    .formatMessage("label.you_need_at_least_n_sequences", minsize);
479   
480  0 calc.setEnabled(size >= minsize);
481  0 if (!calc.isEnabled())
482    {
483  0 calc.setToolTipText(ttip);
484    }
485    else
486    {
487  0 calc.setToolTipText(null);
488    }
489  0 if (calc.isSelected())
490    {
491  0 modelNames.setEnabled(calc.isEnabled());
492  0 if (calc.isEnabled())
493    {
494  0 return true;
495    }
496    else
497    {
498  0 calculate.setToolTipText(ttip);
499    }
500    }
501  0 return false;
502    }
503   
504    /**
505    * A rather elaborate helper method (blame Swing, not me) that builds a
506    * drop-down list of score models (by name) with descriptions as tooltips.
507    * There is also a tooltip shown for the currently selected item when hovering
508    * over it (without opening the list).
509    */
 
510  0 toggle protected JComboBox<String> buildModelOptionsList()
511    {
512  0 final JComboBox<String> scoreModelsCombo = new JComboBox<>();
513  0 scoreModelsCombo.setRenderer(renderer);
514   
515    /*
516    * show tooltip on mouse over the combobox
517    * note the listener has to be on the components that make up
518    * the combobox, doesn't work if just on the combobox
519    */
520  0 final MouseAdapter mouseListener = new MouseAdapter()
521    {
 
522  0 toggle @Override
523    public void mouseEntered(MouseEvent e)
524    {
525  0 scoreModelsCombo.setToolTipText(
526    tips.get(scoreModelsCombo.getSelectedIndex()));
527    }
528   
 
529  0 toggle @Override
530    public void mouseExited(MouseEvent e)
531    {
532  0 scoreModelsCombo.setToolTipText(null);
533    }
534    };
535  0 for (Component c : scoreModelsCombo.getComponents())
536    {
537  0 c.addMouseListener(mouseListener);
538    }
539   
540  0 updateScoreModels(scoreModelsCombo, tips);
541   
542    /*
543    * set the list of tooltips on the combobox's renderer
544    */
545  0 renderer.setTooltips(tips);
546   
547  0 return scoreModelsCombo;
548    }
549   
 
550  0 toggle private JComboBox<String> buildSSSourcesOptionsList()
551    {
552  0 final JComboBox<String> comboBox = new JComboBox<>();
553  0 Object curSel = comboBox.getSelectedItem();
554  0 DefaultComboBoxModel<String> sourcesModel = new DefaultComboBoxModel<>();
555   
556  0 List<String> ssSources = getApplicableSecondaryStructureSources();
557   
558  0 if (ssSources == null)
559    {
560  0 return comboBox;
561    }
562  0 ssSources.add(0, MessageManager.getString("option.ss_providers_all"));
563   
564  0 boolean selectedIsPresent = false;
565  0 for (String source : ssSources)
566    {
567  0 if (curSel != null && source.equals(curSel))
568    {
569  0 selectedIsPresent = true;
570  0 curSel = source;
571    }
572  0 sourcesModel.addElement(source);
573   
574    }
575   
576  0 if (selectedIsPresent)
577    {
578  0 sourcesModel.setSelectedItem(curSel);
579    }
580  0 comboBox.setModel(sourcesModel);
581   
582  0 return comboBox;
583    }
584   
 
585  0 toggle private void updateScoreModels(JComboBox<String> comboBox,
586    List<String> toolTips)
587    {
588  0 Object curSel = comboBox.getSelectedItem();
589  0 toolTips.clear();
590  0 DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
591   
592    /*
593    * select the score models applicable to the alignment type
594    */
595  0 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
596   
597  0 boolean ssPresent = af.getViewport().getAlignment()
598    .isSecondaryStructurePresent();
599   
600  0 List<ScoreModelI> models = getApplicableScoreModels(nucleotide,
601    pca.isSelected(), ssPresent,
602    (pasimap.isSelected() || pairwise.isSelected()));
603   
604    /*
605    * now we can actually add entries to the combobox,
606    * remembering their descriptions for tooltips
607    */
608  0 boolean selectedIsPresent = false;
609  0 for (ScoreModelI sm : models)
610    {
611  0 if (curSel != null && sm.getName().equals(curSel))
612    {
613  0 selectedIsPresent = true;
614  0 curSel = sm.getName();
615    }
616  0 model.addElement(sm.getName());
617   
618    /*
619    * tooltip is description if provided, else text lookup with
620    * fallback on the model name
621    */
622  0 String tooltip = sm.getDescription();
623  0 if (tooltip == null)
624    {
625  0 tooltip = MessageManager.getStringOrReturn("label.score_model_",
626    sm.getName());
627    }
628  0 toolTips.add(tooltip);
629    }
630   
631  0 if (selectedIsPresent)
632    {
633  0 model.setSelectedItem(curSel);
634    }
635    // finally, update the model
636  0 comboBox.setModel(model);
637  0 comboBox.setEnabled(model.getSize() > 0);
638   
639    }
640   
641    /**
642    * Builds a list of score models which are applicable for the alignment and
643    * calculation type (peptide or generic models for protein, nucleotide or
644    * generic models for nucleotide).
645    * <p>
646    * As a special case, includes BLOSUM62 as an extra option for nucleotide PCA.
647    * This is for backwards compatibility with Jalview prior to 2.8 when BLOSUM62
648    * was the only score matrix supported. This is included if property
649    * BLOSUM62_PCA_FOR_NUCLEOTIDE is set to true in the Jalview properties file.
650    *
651    * @param nucleotide
652    * @param forPca
653    * @param ssPresent
654    * - include secondary structure similarity model
655    * @param forPasimap
656    * - limit to ScoreMatrix based models - allows use of AlignSeq
657    * @return
658    */
 
659  16 toggle protected static List<ScoreModelI> getApplicableScoreModels(
660    boolean nucleotide, boolean forPca, boolean ssPresent,
661    boolean forPasimap)
662    {
663  16 List<ScoreModelI> filtered = new ArrayList<>();
664   
665  16 ScoreModels scoreModels = ScoreModels.getInstance();
666  16 for (ScoreModelI sm : scoreModels.getModels())
667    {
668  96 if ((!forPasimap || sm instanceof ScoreMatrix)
669    && (!nucleotide && sm.isProtein() || nucleotide && sm.isDNA()
670    || sm.isSecondaryStructure() && ssPresent))
671   
672    {
673  38 filtered.add(sm);
674    }
675    }
676   
677    /*
678    * special case: add BLOSUM62 as last option for nucleotide PCA,
679    * for backwards compatibility with Jalview < 2.8 (JAL-2962)
680    */
681  16 if (!forPasimap && nucleotide && forPca
682    && Cache.getDefault("BLOSUM62_PCA_FOR_NUCLEOTIDE", false))
683    {
684  1 filtered.add(scoreModels.getBlosum62());
685    }
686   
687  16 return filtered;
688    }
689   
690    /*
691    * This method returns the secondary structure sources of the
692    * alignment or selection group
693    */
 
694  0 toggle protected List<String> getApplicableSecondaryStructureSources()
695    {
696  0 AlignmentAnnotation[] annotations = null;
697   
698    // If there is a selection group, then find sources from those
699    // sequences
700  0 if (af.getViewport().getSelectionGroup() != null)
701    {
702  0 SequenceGroup sg = af.getViewport().getSelectionGroup();
703  0 List<SequenceI> sequences = sg.getSequences();
704   
705    // Iterate through each sequence to get the annotations
706  0 AlignmentAnnotation[] alignAnnot = new AlignmentAnnotation[0];
707  0 if (sequences != null)
708    {
709  0 List<AlignmentAnnotation> tempList = new ArrayList<>();
710  0 for (SequenceI seq : sequences)
711    {
712  0 AlignmentAnnotation[] seqAnnotations = seq.getAnnotation();
713  0 if (seqAnnotations != null)
714    {
715  0 Collections.addAll(tempList, seqAnnotations);
716    }
717    }
718  0 alignAnnot = tempList.toArray(new AlignmentAnnotation[0]);
719    }
720   
721    // Get list of secondary structure annotations from the list of
722    // annotations
723  0 List<AlignmentAnnotation> ssAnnotations = AlignmentUtils
724    .getSecondaryStructureAnnots(alignAnnot);
725   
726  0 if (ssAnnotations != null)
727    {
728  0 annotations = ssAnnotations.toArray(new AlignmentAnnotation[0]);
729  0 List<String> ssSources = AlignmentUtils
730    .extractSSSourceInAlignmentAnnotation(annotations);
731  0 return ssSources;
732    }
733    }
734   
735    else
736    {
737  0 annotations = af.getViewport().getAlignment()
738    .getAlignmentAnnotation();
739  0 List<String> ssSources = AlignmentUtils
740    .extractSSSourceInAlignmentAnnotation(annotations);
741  0 return ssSources;
742    }
743   
744  0 return null;
745    }
746   
747    /**
748    * Open and calculate the selected tree or PCA on 'OK'
749    */
 
750  0 toggle protected void calculate_actionPerformed()
751    {
752  0 boolean doPCA = pca.isSelected();
753  0 boolean doPaSiMap = pasimap.isSelected();
754  0 boolean doPairwise = pairwise.isSelected();
755  0 String modelName = modelNames.getSelectedItem() == null ? ""
756    : modelNames.getSelectedItem().toString();
757   
758  0 String ssSource = null;
759   
760  0 if (modelName.equals(secondaryStructureModelName))
761    {
762  0 Object selectedItem = ssSourceDropdown.getSelectedItem();
763  0 if (selectedItem != null)
764    {
765  0 ssSource = selectedItem.toString();
766    }
767    }
768  0 SimilarityParams params = getSimilarityParameters(doPCA);
769  0 params.setSecondaryStructureSource(ssSource);
770   
771  0 if (doPCA)
772    {
773  0 openPcaPanel(modelName, params);
774    }
775  0 else if (doPaSiMap)
776    {
777  0 openPasimapPanel(modelName, params);
778    }
779  0 else if (doPairwise)
780    {
781  0 openPairwisePanel(modelName, params);
782    }
783    else
784    {
785  0 openTreePanel(modelName, params);
786    }
787   
788  0 closeFrame();
789    }
790   
 
791  0 toggle private void openPairwisePanel(String modelName, SimilarityParamsI params)
792    {
793  0 ScoreModelI sm = ScoreModels.getInstance().getScoreModel(modelName,
794    af.alignPanel);
795  0 if (sm == null || !(sm instanceof ScoreMatrix))
796    {
797  0 return;
798    }
799  0 new Thread(new Runnable()
800    {
 
801  0 toggle @Override
802    public void run()
803    {
804  0 String pairwise_alignment_title = af.formCalculationTitle(
805    MessageManager.getString("action.pairwise_alignment")
806    + " with " + sm.getName(),
807    af.getViewport().getSelectionGroup() != null,
808    af.getTitle());
809  0 JInternalFrame frame = new JInternalFrame();
810  0 frame.setFrameIcon(null);
811  0 frame.setContentPane(
812    new PairwiseAlignPanel(af.getViewport(), (ScoreMatrix) sm));
813  0 Desktop.addInternalFrame(frame, pairwise_alignment_title, 600, 500);
814    }
815    }).start();
816    }
817   
818    /**
819    * Open a new Tree panel on the desktop
820    *
821    * @param modelName
822    * @param params
823    */
 
824  0 toggle protected void openTreePanel(String modelName, SimilarityParamsI params)
825    {
826    /*
827    * gui validation shouldn't allow insufficient sequences here, but leave
828    * this check in in case this method gets exposed programmatically in future
829    */
830  0 AlignViewport viewport = af.getViewport();
831  0 SequenceGroup sg = viewport.getSelectionGroup();
832  0 boolean isAnnotationBased = modelName.equals(secondaryStructureModelName);
833    // FIXME @RENIA - CURRENTLY WE ALLOW ANY SIZE SELECTION TO BE USED FOR
834    // SECONDARY STRUCTURE SIMILARITY - WE SHOULD INSTEAD CHECK HOW MANY
835    // ANNOTATION ROWS THERE ARE
836  0 if (sg != null && sg.getSize() < MIN_TREE_SELECTION
837    && !isAnnotationBased)
838    {
839  0 JvOptionPane.showMessageDialog(Desktop.desktop,
840    MessageManager.formatMessage(
841    "label.you_need_at_least_n_sequences",
842    MIN_TREE_SELECTION),
843    MessageManager.getString("label.not_enough_sequences"),
844    JvOptionPane.WARNING_MESSAGE);
845  0 return;
846    }
847   
848    // Check min no of annotations are present for tree calculation
849  0 if(isAnnotationBased
850    && sg != null
851    && sg.noOfSecondaryStructureAnnots() < MIN_TREE_SELECTION)
852    {
853  0 JvOptionPane.showMessageDialog(Desktop.desktop,
854    MessageManager.formatMessage(
855    "label.you_need_at_least_n_annotations",
856    MIN_TREE_SELECTION),
857    MessageManager.getString("label.not_enough_annotations"),
858    JvOptionPane.WARNING_MESSAGE);
859  0 return;
860    }
861   
862  0 AlignmentAnnotation[] alignAnnots = viewport.getAlignment()
863    .getAlignmentAnnotation();
864  0 List<AlignmentAnnotation> ssAnnots = AlignmentUtils
865    .getSecondaryStructureAnnots(alignAnnots);
866   
867    // Check min no of annotations are present in the alignment for tree
868    // calculation when selection group is null
869  0 if (sg == null && isAnnotationBased
870    && (ssAnnots == null || ssAnnots.size() < MIN_TREE_SELECTION))
871    {
872  0 JvOptionPane.showMessageDialog(Desktop.desktop,
873    MessageManager.formatMessage(
874    "label.you_need_seq_with_at_least_n_annotations",
875    MIN_TREE_SELECTION),
876    MessageManager.getString("label.not_enough_annotations"),
877    JvOptionPane.WARNING_MESSAGE);
878  0 return;
879    }
880   
881  0 String treeType = neighbourJoining.isSelected()
882    ? TreeBuilder.NEIGHBOUR_JOINING
883    : TreeBuilder.AVERAGE_DISTANCE;
884  0 af.newTreePanel(treeType, modelName, params, isAnnotationBased);
885    }
886   
887    /**
888    * Open a new PCA panel on the desktop
889    *
890    * @param modelName
891    * @param params
892    */
 
893  0 toggle protected void openPcaPanel(String modelName, SimilarityParamsI params)
894    {
895  0 AlignViewport viewport = af.getViewport();
896  0 SequenceGroup sg = viewport.getSelectionGroup();
897   
898  0 boolean isAnnotationBased = modelName.equals(secondaryStructureModelName);
899   
900    /*
901    * gui validation shouldn't allow insufficient sequences here, but leave
902    * this check in in case this method gets exposed programmatically in future
903    */
904  0 if ((((sg != null)
905    && (sg.getSize() < MIN_PCA_SELECTION)
906    && (sg.getSize() > 0))
907    || (viewport.getAlignment().getHeight() < MIN_PCA_SELECTION))
908    && !isAnnotationBased)
909    {
910  0 JvOptionPane.showInternalMessageDialog(this,
911    MessageManager.formatMessage(
912    "label.you_need_at_least_n_sequences",
913    MIN_PCA_SELECTION),
914    MessageManager
915    .getString("label.sequence_selection_insufficient"),
916    JvOptionPane.WARNING_MESSAGE);
917  0 return;
918    }
919   
920  0 if ( isAnnotationBased
921    && sg != null
922    && sg.noOfSecondaryStructureAnnots() < MIN_PCA_SELECTION
923    && sg.getSize() > 0 )
924    {
925  0 JvOptionPane.showInternalMessageDialog(this,
926    MessageManager.formatMessage(
927    "label.you_need_at_least_n_annotations",
928    MIN_PCA_SELECTION),
929    MessageManager
930    .getString("label.not_enough_annotations"),
931    JvOptionPane.WARNING_MESSAGE);
932  0 return;
933    }
934   
935  0 AlignmentAnnotation[] alignAnnots = viewport.getAlignment()
936    .getAlignmentAnnotation();
937  0 List<AlignmentAnnotation> ssAnnots = AlignmentUtils
938    .getSecondaryStructureAnnots(alignAnnots);
939   
940  0 if ( isAnnotationBased
941    && sg == null
942    && (ssAnnots == null || ssAnnots.size() < MIN_PCA_SELECTION)
943    && viewport.getAlignment().getHeight() < MIN_PCA_SELECTION )
944    {
945  0 JvOptionPane.showInternalMessageDialog(this,
946    MessageManager.formatMessage(
947    "label.you_need_at_least_n_annotations",
948    MIN_PCA_SELECTION),
949    MessageManager
950    .getString("label.not_enough_annotations"),
951    JvOptionPane.WARNING_MESSAGE);
952  0 return;
953    }
954    /*
955    * construct the panel and kick off its calculation thread
956    */
957  0 pcaPanel = new PCAPanel(af.alignPanel, modelName, params);
958  0 new Thread(pcaPanel).start();
959   
960    }
961   
962    /**
963    * Open a new PaSiMap panel on the desktop
964    *
965    * @param modelName
966    * @param params
967    */
 
968  0 toggle protected void openPasimapPanel(String modelName,
969    SimilarityParamsI params)
970    {
971  0 AlignViewport viewport = af.getViewport();
972   
973    /*
974    * gui validation shouldn't allow insufficient sequences here, but leave
975    * this check in in case this method gets exposed programmatically in future
976    */
977  0 if (((viewport.getSelectionGroup() != null)
978    && (viewport.getSelectionGroup()
979    .getSize() < MIN_PASIMAP_SELECTION)
980    && (viewport.getSelectionGroup().getSize() > 0))
981    || (viewport.getAlignment()
982    .getHeight() < MIN_PASIMAP_SELECTION))
983    {
984  0 JvOptionPane.showInternalMessageDialog(this,
985    MessageManager.formatMessage(
986    "label.you_need_at_least_n_sequences",
987    MIN_PASIMAP_SELECTION),
988    MessageManager
989    .getString("label.sequence_selection_insufficient"),
990    JvOptionPane.WARNING_MESSAGE);
991  0 return;
992    }
993   
994    /*
995    * construct the panel and kick off its calculation thread
996    */
997  0 pasimapPanel = new PaSiMapPanel(af.alignPanel, modelName);
998  0 new Thread(pasimapPanel).start();
999   
1000    }
1001   
1002    /**
1003    *
1004    */
 
1005  0 toggle protected void closeFrame()
1006    {
1007  0 try
1008    {
1009  0 frame.setClosed(true);
1010    } catch (PropertyVetoException ex)
1011    {
1012    }
1013    }
1014   
1015    /**
1016    * Returns a data bean holding parameters for similarity (or distance) model
1017    * calculation
1018    *
1019    * @param doPCA
1020    * @return
1021    */
 
1022  0 toggle protected SimilarityParams getSimilarityParameters(boolean doPCA)
1023    {
1024    // commented out: parameter choices read from gui widgets
1025    // SimilarityParamsI params = new SimilarityParams(
1026    // includeGappedColumns.isSelected(), matchGaps.isSelected(),
1027    // includeGaps.isSelected(), shorterSequence.isSelected());
1028   
1029  0 boolean includeGapGap = true;
1030  0 boolean includeGapResidue = true;
1031  0 boolean matchOnShortestLength = false;
1032   
1033    /*
1034    * 'matchGaps' flag is only used in the PID calculation
1035    * - set to false for PCA so that PCA using PID reproduces SeqSpace PCA
1036    * - set to true for Tree to reproduce Jalview 2.10.1 calculation
1037    * - set to false for Tree for a more correct calculation (JAL-374)
1038    */
1039  0 boolean matchGap = doPCA ? false : treeMatchGaps;
1040   
1041  0 return new SimilarityParams(includeGapGap, matchGap, includeGapResidue,
1042    matchOnShortestLength);
1043    }
1044   
1045    /**
1046    * Closes dialog on Close button press
1047    */
 
1048  0 toggle protected void close_actionPerformed()
1049    {
1050  0 try
1051    {
1052  0 frame.setClosed(true);
1053    } catch (Exception ex)
1054    {
1055    }
1056    }
1057   
 
1058  0 toggle public PCAPanel getPcaPanel()
1059    {
1060  0 return pcaPanel;
1061    }
1062    }