Clover icon

Coverage Report

  1. Project Clover database Fri Jan 16 2026 12:43:44 GMT
  2. Package jalview.gui

File CalculationChooser.java

 

Coverage histogram

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

Code metrics

98
312
33
1
1,167
734
123
0.39
9.45
33
3.73

Classes

Class Line # Actions
CalculationChooser 79 312 123
0.0293453732.9%
 

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