Clover icon

Coverage Report

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