Clover icon

Coverage Report

  1. Project Clover database Wed Feb 4 2026 17:46:51 GMT
  2. Package jalview.gui

File CalculationChooser.java

 

Coverage histogram

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

Code metrics

98
327
33
1
1,197
751
123
0.38
9.91
33
3.73

Classes

Class Line # Actions
CalculationChooser 80 327 123
0.028384282.8%
 

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