Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.gui

File CalculationChooser.java

 

Coverage histogram

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

Code metrics

36
172
22
1
660
405
54
0.31
7.82
22
2.45

Classes

Class Line # Actions
CalculationChooser 67 172 54
0.056521745.7%
 

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 jalview.analysis.TreeBuilder;
24    import jalview.analysis.scoremodels.ScoreModels;
25    import jalview.analysis.scoremodels.SimilarityParams;
26    import jalview.api.analysis.ScoreModelI;
27    import jalview.api.analysis.SimilarityParamsI;
28    import jalview.bin.Cache;
29    import jalview.datamodel.SequenceGroup;
30    import jalview.util.MessageManager;
31   
32    import java.awt.BorderLayout;
33    import java.awt.Color;
34    import java.awt.Component;
35    import java.awt.Dimension;
36    import java.awt.FlowLayout;
37    import java.awt.Font;
38    import java.awt.GridLayout;
39    import java.awt.Insets;
40    import java.awt.event.ActionEvent;
41    import java.awt.event.ActionListener;
42    import java.awt.event.FocusEvent;
43    import java.awt.event.FocusListener;
44    import java.awt.event.MouseAdapter;
45    import java.awt.event.MouseEvent;
46    import java.beans.PropertyVetoException;
47    import java.util.ArrayList;
48    import java.util.List;
49   
50    import javax.swing.BorderFactory;
51    import javax.swing.ButtonGroup;
52    import javax.swing.DefaultComboBoxModel;
53    import javax.swing.JButton;
54    import javax.swing.JCheckBox;
55    import javax.swing.JComboBox;
56    import javax.swing.JInternalFrame;
57    import javax.swing.JLabel;
58    import javax.swing.JLayeredPane;
59    import javax.swing.JPanel;
60    import javax.swing.JRadioButton;
61    import javax.swing.event.InternalFrameAdapter;
62    import javax.swing.event.InternalFrameEvent;
63   
64    /**
65    * A dialog where a user can choose and action Tree or PCA calculation options
66    */
 
67    public class CalculationChooser extends JPanel
68    {
69    /*
70    * flag for whether gap matches residue in the PID calculation for a Tree
71    * - true gives Jalview 2.10.1 behaviour
72    * - set to false (using Groovy) for a more correct tree
73    * (JAL-374)
74    */
75    private static boolean treeMatchGaps = true;
76   
77    private static final Font VERDANA_11PT = new Font("Verdana", 0, 11);
78   
79    private static final int MIN_TREE_SELECTION = 3;
80   
81    private static final int MIN_PCA_SELECTION = 4;
82   
83    AlignFrame af;
84   
85    JRadioButton pca;
86   
87    JRadioButton neighbourJoining;
88   
89    JRadioButton averageDistance;
90   
91    JComboBox<String> modelNames;
92   
93    JButton calculate;
94   
95    private JInternalFrame frame;
96   
97    private JCheckBox includeGaps;
98   
99    private JCheckBox matchGaps;
100   
101    private JCheckBox includeGappedColumns;
102   
103    private JCheckBox shorterSequence;
104   
105    final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer();
106   
107    List<String> tips = new ArrayList<>();
108   
109    /*
110    * the most recently opened PCA results panel
111    */
112    private PCAPanel pcaPanel;
113   
114    /**
115    * Constructor
116    *
117    * @param af
118    */
 
119  0 toggle public CalculationChooser(AlignFrame alignFrame)
120    {
121  0 this.af = alignFrame;
122  0 init();
123  0 af.alignPanel.setCalculationDialog(this);
124    }
125   
126    /**
127    * Lays out the panel and adds it to the desktop
128    */
 
129  0 toggle void init()
130    {
131  0 setLayout(new BorderLayout());
132  0 frame = new JInternalFrame();
133  0 frame.setContentPane(this);
134  0 this.setBackground(Color.white);
135  0 frame.addFocusListener(new FocusListener()
136    {
137   
 
138  0 toggle @Override
139    public void focusLost(FocusEvent e)
140    {
141    }
142   
 
143  0 toggle @Override
144    public void focusGained(FocusEvent e)
145    {
146  0 validateCalcTypes();
147    }
148    });
149    /*
150    * Layout consists of 3 or 4 panels:
151    * - first with choice of PCA or tree method NJ or AV
152    * - second with choice of score model
153    * - third with score model parameter options [suppressed]
154    * - fourth with OK and Cancel
155    */
156  0 pca = new JRadioButton(
157    MessageManager.getString("label.principal_component_analysis"));
158  0 pca.setOpaque(false);
159   
160  0 neighbourJoining = new JRadioButton(
161    MessageManager.getString("label.tree_calc_nj"));
162  0 neighbourJoining.setSelected(true);
163  0 neighbourJoining.setOpaque(false);
164   
165  0 averageDistance = new JRadioButton(
166    MessageManager.getString("label.tree_calc_av"));
167  0 averageDistance.setOpaque(false);
168   
169  0 JPanel calcChoicePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
170  0 calcChoicePanel.setOpaque(false);
171   
172    // first create the Tree calculation's border panel
173  0 JPanel treePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
174  0 treePanel.setOpaque(false);
175   
176  0 JvSwingUtils.createTitledBorder(treePanel,
177    MessageManager.getString("label.tree"), true);
178   
179    // then copy the inset dimensions for the border-less PCA panel
180  0 JPanel pcaBorderless = new JPanel(new FlowLayout(FlowLayout.LEFT));
181  0 Insets b = treePanel.getBorder().getBorderInsets(treePanel);
182  0 pcaBorderless.setBorder(
183    BorderFactory.createEmptyBorder(2, b.left, 2, b.right));
184  0 pcaBorderless.setOpaque(false);
185   
186  0 pcaBorderless.add(pca, FlowLayout.LEFT);
187  0 calcChoicePanel.add(pcaBorderless, FlowLayout.LEFT);
188   
189  0 treePanel.add(neighbourJoining);
190  0 treePanel.add(averageDistance);
191   
192  0 calcChoicePanel.add(treePanel);
193   
194  0 ButtonGroup calcTypes = new ButtonGroup();
195  0 calcTypes.add(pca);
196  0 calcTypes.add(neighbourJoining);
197  0 calcTypes.add(averageDistance);
198   
199  0 ActionListener calcChanged = new ActionListener()
200    {
 
201  0 toggle @Override
202    public void actionPerformed(ActionEvent e)
203    {
204  0 validateCalcTypes();
205    }
206    };
207  0 pca.addActionListener(calcChanged);
208  0 neighbourJoining.addActionListener(calcChanged);
209  0 averageDistance.addActionListener(calcChanged);
210   
211    /*
212    * score models drop-down - with added tooltips!
213    */
214  0 modelNames = buildModelOptionsList();
215   
216  0 JPanel scoreModelPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
217  0 scoreModelPanel.setOpaque(false);
218  0 scoreModelPanel.add(modelNames);
219   
220    /*
221    * score model parameters
222    */
223  0 JPanel paramsPanel = new JPanel(new GridLayout(5, 1));
224  0 paramsPanel.setOpaque(false);
225  0 includeGaps = new JCheckBox("Include gaps");
226  0 matchGaps = new JCheckBox("Match gaps");
227  0 includeGappedColumns = new JCheckBox("Include gapped columns");
228  0 shorterSequence = new JCheckBox("Match on shorter sequence");
229  0 paramsPanel.add(new JLabel("Pairwise sequence scoring options"));
230  0 paramsPanel.add(includeGaps);
231  0 paramsPanel.add(matchGaps);
232  0 paramsPanel.add(includeGappedColumns);
233  0 paramsPanel.add(shorterSequence);
234   
235    /*
236    * OK / Cancel buttons
237    */
238  0 calculate = new JButton(MessageManager.getString("action.calculate"));
239  0 calculate.setFont(VERDANA_11PT);
240  0 calculate.addActionListener(new java.awt.event.ActionListener()
241    {
 
242  0 toggle @Override
243    public void actionPerformed(ActionEvent e)
244    {
245  0 calculate_actionPerformed();
246    }
247    });
248  0 JButton close = new JButton(MessageManager.getString("action.close"));
249  0 close.setFont(VERDANA_11PT);
250  0 close.addActionListener(new java.awt.event.ActionListener()
251    {
 
252  0 toggle @Override
253    public void actionPerformed(ActionEvent e)
254    {
255  0 close_actionPerformed();
256    }
257    });
258  0 JPanel actionPanel = new JPanel();
259  0 actionPanel.setOpaque(false);
260  0 actionPanel.add(calculate);
261  0 actionPanel.add(close);
262   
263  0 boolean includeParams = false;
264  0 this.add(calcChoicePanel, BorderLayout.CENTER);
265  0 calcChoicePanel.add(scoreModelPanel);
266  0 if (includeParams)
267    {
268  0 scoreModelPanel.add(paramsPanel);
269    }
270  0 this.add(actionPanel, BorderLayout.SOUTH);
271   
272  0 int width = 350;
273  0 int height = includeParams ? 420 : 240;
274   
275  0 setMinimumSize(new Dimension(325, height - 10));
276  0 String title = MessageManager.getString("label.choose_calculation");
277  0 if (af.getViewport().getViewName() != null)
278    {
279  0 title = title + " (" + af.getViewport().getViewName() + ")";
280    }
281   
282  0 Desktop.addInternalFrame(frame, title, width, height, false);
283  0 calcChoicePanel.doLayout();
284  0 revalidate();
285    /*
286    * null the AlignmentPanel's reference to the dialog when it is closed
287    */
288  0 frame.addInternalFrameListener(new InternalFrameAdapter()
289    {
 
290  0 toggle @Override
291    public void internalFrameClosed(InternalFrameEvent evt)
292    {
293  0 af.alignPanel.setCalculationDialog(null);
294    };
295    });
296   
297  0 validateCalcTypes();
298  0 frame.setLayer(JLayeredPane.PALETTE_LAYER);
299    }
300   
301    /**
302    * enable calculations applicable for the current alignment or selection.
303    */
 
304  0 toggle protected void validateCalcTypes()
305    {
306  0 int size = af.getViewport().getAlignment().getHeight();
307  0 if (af.getViewport().getSelectionGroup() != null)
308    {
309  0 size = af.getViewport().getSelectionGroup().getSize();
310    }
311   
312    /*
313    * disable calc options for which there is insufficient input data
314    * return value of true means enabled and selected
315    */
316  0 boolean checkPca = checkEnabled(pca, size, MIN_PCA_SELECTION);
317  0 boolean checkNeighbourJoining = checkEnabled(neighbourJoining, size,
318    MIN_TREE_SELECTION);
319  0 boolean checkAverageDistance = checkEnabled(averageDistance, size,
320    MIN_TREE_SELECTION);
321   
322  0 if (checkPca || checkNeighbourJoining || checkAverageDistance)
323    {
324  0 calculate.setToolTipText(null);
325  0 calculate.setEnabled(true);
326    }
327    else
328    {
329  0 calculate.setEnabled(false);
330    }
331  0 updateScoreModels(modelNames, tips);
332    }
333   
334    /**
335    * Check the input and disable a calculation's radio button if necessary. A
336    * tooltip is shown for disabled calculations.
337    *
338    * @param calc
339    * - radio button for the calculation being validated
340    * @param size
341    * - size of input to calculation
342    * @param minsize
343    * - minimum size for calculation
344    * @return true if size >= minsize and calc.isSelected
345    */
 
346  0 toggle private boolean checkEnabled(JRadioButton calc, int size, int minsize)
347    {
348  0 String ttip = MessageManager
349    .formatMessage("label.you_need_at_least_n_sequences", minsize);
350   
351  0 calc.setEnabled(size >= minsize);
352  0 if (!calc.isEnabled())
353    {
354  0 calc.setToolTipText(ttip);
355    }
356    else
357    {
358  0 calc.setToolTipText(null);
359    }
360  0 if (calc.isSelected())
361    {
362  0 modelNames.setEnabled(calc.isEnabled());
363  0 if (calc.isEnabled())
364    {
365  0 return true;
366    }
367    else
368    {
369  0 calculate.setToolTipText(ttip);
370    }
371    }
372  0 return false;
373    }
374   
375    /**
376    * A rather elaborate helper method (blame Swing, not me) that builds a
377    * drop-down list of score models (by name) with descriptions as tooltips.
378    * There is also a tooltip shown for the currently selected item when hovering
379    * over it (without opening the list).
380    */
 
381  0 toggle protected JComboBox<String> buildModelOptionsList()
382    {
383  0 final JComboBox<String> scoreModelsCombo = new JComboBox<>();
384  0 scoreModelsCombo.setRenderer(renderer);
385   
386    /*
387    * show tooltip on mouse over the combobox
388    * note the listener has to be on the components that make up
389    * the combobox, doesn't work if just on the combobox
390    */
391  0 final MouseAdapter mouseListener = new MouseAdapter()
392    {
 
393  0 toggle @Override
394    public void mouseEntered(MouseEvent e)
395    {
396  0 scoreModelsCombo.setToolTipText(
397    tips.get(scoreModelsCombo.getSelectedIndex()));
398    }
399   
 
400  0 toggle @Override
401    public void mouseExited(MouseEvent e)
402    {
403  0 scoreModelsCombo.setToolTipText(null);
404    }
405    };
406  0 for (Component c : scoreModelsCombo.getComponents())
407    {
408  0 c.addMouseListener(mouseListener);
409    }
410   
411  0 updateScoreModels(scoreModelsCombo, tips);
412   
413    /*
414    * set the list of tooltips on the combobox's renderer
415    */
416  0 renderer.setTooltips(tips);
417   
418  0 return scoreModelsCombo;
419    }
420   
 
421  0 toggle private void updateScoreModels(JComboBox<String> comboBox,
422    List<String> toolTips)
423    {
424  0 Object curSel = comboBox.getSelectedItem();
425  0 toolTips.clear();
426  0 DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
427   
428    /*
429    * select the score models applicable to the alignment type
430    */
431  0 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
432  0 List<ScoreModelI> models = getApplicableScoreModels(nucleotide,
433    pca.isSelected());
434   
435    /*
436    * now we can actually add entries to the combobox,
437    * remembering their descriptions for tooltips
438    */
439  0 boolean selectedIsPresent = false;
440  0 for (ScoreModelI sm : models)
441    {
442  0 if (curSel != null && sm.getName().equals(curSel))
443    {
444  0 selectedIsPresent = true;
445  0 curSel = sm.getName();
446    }
447  0 model.addElement(sm.getName());
448   
449    /*
450    * tooltip is description if provided, else text lookup with
451    * fallback on the model name
452    */
453  0 String tooltip = sm.getDescription();
454  0 if (tooltip == null)
455    {
456  0 tooltip = MessageManager.getStringOrReturn("label.score_model_",
457    sm.getName());
458    }
459  0 toolTips.add(tooltip);
460    }
461   
462  0 if (selectedIsPresent)
463    {
464  0 model.setSelectedItem(curSel);
465    }
466    // finally, update the model
467  0 comboBox.setModel(model);
468    }
469   
470    /**
471    * Builds a list of score models which are applicable for the alignment and
472    * calculation type (peptide or generic models for protein, nucleotide or
473    * generic models for nucleotide).
474    * <p>
475    * As a special case, includes BLOSUM62 as an extra option for nucleotide PCA.
476    * This is for backwards compatibility with Jalview prior to 2.8 when BLOSUM62
477    * was the only score matrix supported. This is included if property
478    * BLOSUM62_PCA_FOR_NUCLEOTIDE is set to true in the Jalview properties file.
479    *
480    * @param nucleotide
481    * @param forPca
482    * @return
483    */
 
484  6 toggle protected static List<ScoreModelI> getApplicableScoreModels(
485    boolean nucleotide, boolean forPca)
486    {
487  6 List<ScoreModelI> filtered = new ArrayList<>();
488   
489  6 ScoreModels scoreModels = ScoreModels.getInstance();
490  6 for (ScoreModelI sm : scoreModels.getModels())
491    {
492  30 if (!nucleotide && sm.isProtein() || nucleotide && sm.isDNA())
493    {
494  20 filtered.add(sm);
495    }
496    }
497   
498    /*
499    * special case: add BLOSUM62 as last option for nucleotide PCA,
500    * for backwards compatibility with Jalview < 2.8 (JAL-2962)
501    */
502  6 if (nucleotide && forPca
503    && Cache.getDefault("BLOSUM62_PCA_FOR_NUCLEOTIDE", false))
504    {
505  1 filtered.add(scoreModels.getBlosum62());
506    }
507   
508  6 return filtered;
509    }
510   
511    /**
512    * Open and calculate the selected tree or PCA on 'OK'
513    */
 
514  0 toggle protected void calculate_actionPerformed()
515    {
516  0 boolean doPCA = pca.isSelected();
517  0 String modelName = modelNames.getSelectedItem().toString();
518  0 SimilarityParamsI params = getSimilarityParameters(doPCA);
519   
520  0 if (doPCA)
521    {
522  0 openPcaPanel(modelName, params);
523    }
524    else
525    {
526  0 openTreePanel(modelName, params);
527    }
528   
529    // closeFrame();
530    }
531   
532    /**
533    * Open a new Tree panel on the desktop
534    *
535    * @param modelName
536    * @param params
537    */
 
538  0 toggle protected void openTreePanel(String modelName, SimilarityParamsI params)
539    {
540    /*
541    * gui validation shouldn't allow insufficient sequences here, but leave
542    * this check in in case this method gets exposed programmatically in future
543    */
544  0 AlignViewport viewport = af.getViewport();
545  0 SequenceGroup sg = viewport.getSelectionGroup();
546  0 if (sg != null && sg.getSize() < MIN_TREE_SELECTION)
547    {
548  0 JvOptionPane.showMessageDialog(Desktop.desktop,
549    MessageManager.formatMessage(
550    "label.you_need_at_least_n_sequences",
551    MIN_TREE_SELECTION),
552    MessageManager.getString("label.not_enough_sequences"),
553    JvOptionPane.WARNING_MESSAGE);
554  0 return;
555    }
556   
557  0 String treeType = neighbourJoining.isSelected()
558    ? TreeBuilder.NEIGHBOUR_JOINING
559    : TreeBuilder.AVERAGE_DISTANCE;
560  0 af.newTreePanel(treeType, modelName, params);
561    }
562   
563    /**
564    * Open a new PCA panel on the desktop
565    *
566    * @param modelName
567    * @param params
568    */
 
569  0 toggle protected void openPcaPanel(String modelName, SimilarityParamsI params)
570    {
571  0 AlignViewport viewport = af.getViewport();
572   
573    /*
574    * gui validation shouldn't allow insufficient sequences here, but leave
575    * this check in in case this method gets exposed programmatically in future
576    */
577  0 if (((viewport.getSelectionGroup() != null)
578    && (viewport.getSelectionGroup().getSize() < MIN_PCA_SELECTION)
579    && (viewport.getSelectionGroup().getSize() > 0))
580    || (viewport.getAlignment().getHeight() < MIN_PCA_SELECTION))
581    {
582  0 JvOptionPane.showInternalMessageDialog(this,
583    MessageManager.formatMessage(
584    "label.you_need_at_least_n_sequences",
585    MIN_PCA_SELECTION),
586    MessageManager
587    .getString("label.sequence_selection_insufficient"),
588    JvOptionPane.WARNING_MESSAGE);
589  0 return;
590    }
591   
592    /*
593    * construct the panel and kick off its calculation thread
594    */
595  0 pcaPanel = new PCAPanel(af.alignPanel, modelName, params);
596  0 new Thread(pcaPanel).start();
597   
598    }
599   
600    /**
601    *
602    */
 
603  0 toggle protected void closeFrame()
604    {
605  0 try
606    {
607  0 frame.setClosed(true);
608    } catch (PropertyVetoException ex)
609    {
610    }
611    }
612   
613    /**
614    * Returns a data bean holding parameters for similarity (or distance) model
615    * calculation
616    *
617    * @param doPCA
618    * @return
619    */
 
620  0 toggle protected SimilarityParamsI getSimilarityParameters(boolean doPCA)
621    {
622    // commented out: parameter choices read from gui widgets
623    // SimilarityParamsI params = new SimilarityParams(
624    // includeGappedColumns.isSelected(), matchGaps.isSelected(),
625    // includeGaps.isSelected(), shorterSequence.isSelected());
626   
627  0 boolean includeGapGap = true;
628  0 boolean includeGapResidue = true;
629  0 boolean matchOnShortestLength = false;
630   
631    /*
632    * 'matchGaps' flag is only used in the PID calculation
633    * - set to false for PCA so that PCA using PID reproduces SeqSpace PCA
634    * - set to true for Tree to reproduce Jalview 2.10.1 calculation
635    * - set to false for Tree for a more correct calculation (JAL-374)
636    */
637  0 boolean matchGap = doPCA ? false : treeMatchGaps;
638   
639  0 return new SimilarityParams(includeGapGap, matchGap, includeGapResidue,
640    matchOnShortestLength);
641    }
642   
643    /**
644    * Closes dialog on Close button press
645    */
 
646  0 toggle protected void close_actionPerformed()
647    {
648  0 try
649    {
650  0 frame.setClosed(true);
651    } catch (Exception ex)
652    {
653    }
654    }
655   
 
656  0 toggle public PCAPanel getPcaPanel()
657    {
658  0 return pcaPanel;
659    }
660    }