Clover icon

Coverage Report

  1. Project Clover database Wed Jan 7 2026 02:39:37 GMT
  2. Package jalview.gui

File CalculationChooser.java

 

Coverage histogram

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

Code metrics

92
303
33
1
1,135
710
112
0.37
9.18
33
3.39

Classes

Class Line # Actions
CalculationChooser 79 303 112
0.0303738323%
 

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