X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FCalculationChooser.java;h=af41e588c2623fc673d1bfb51455c81abe088061;hb=HEAD;hp=694b096b7b952867d8be2ecc9b85a253a20b0977;hpb=e65df2c2289b3b05c94ace285ae64a7cbc4999a8;p=jalview.git diff --git a/src/jalview/gui/CalculationChooser.java b/src/jalview/gui/CalculationChooser.java index 694b096..af41e58 100644 --- a/src/jalview/gui/CalculationChooser.java +++ b/src/jalview/gui/CalculationChooser.java @@ -20,13 +20,6 @@ */ package jalview.gui; -import jalview.analysis.TreeBuilder; -import jalview.analysis.scoremodels.ScoreModels; -import jalview.analysis.scoremodels.SimilarityParams; -import jalview.api.analysis.ScoreModelI; -import jalview.api.analysis.SimilarityParamsI; -import jalview.util.MessageManager; - import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -47,6 +40,7 @@ import java.util.List; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; +import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; @@ -58,6 +52,15 @@ import javax.swing.JRadioButton; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; +import jalview.analysis.TreeBuilder; +import jalview.analysis.scoremodels.ScoreModels; +import jalview.analysis.scoremodels.SimilarityParams; +import jalview.api.analysis.ScoreModelI; +import jalview.api.analysis.SimilarityParamsI; +import jalview.bin.Cache; +import jalview.datamodel.SequenceGroup; +import jalview.util.MessageManager; + /** * A dialog where a user can choose and action Tree or PCA calculation options */ @@ -73,6 +76,10 @@ public class CalculationChooser extends JPanel private static final Font VERDANA_11PT = new Font("Verdana", 0, 11); + private static final int MIN_TREE_SELECTION = 3; + + private static final int MIN_PCA_SELECTION = 4; + AlignFrame af; JRadioButton pca; @@ -83,7 +90,7 @@ public class CalculationChooser extends JPanel JComboBox modelNames; - JButton ok; + JButton calculate; private JInternalFrame frame; @@ -95,6 +102,15 @@ public class CalculationChooser extends JPanel private JCheckBox shorterSequence; + final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer(); + + List tips = new ArrayList<>(); + + /* + * the most recently opened PCA results panel + */ + private PCAPanel pcaPanel; + /** * Constructor * @@ -114,9 +130,23 @@ public class CalculationChooser extends JPanel { setLayout(new BorderLayout()); frame = new JInternalFrame(); + frame.setFrameIcon(null); frame.setContentPane(this); this.setBackground(Color.white); + frame.addFocusListener(new FocusListener() + { + + @Override + public void focusLost(FocusEvent e) + { + } + @Override + public void focusGained(FocusEvent e) + { + validateCalcTypes(); + } + }); /* * Layout consists of 3 or 4 panels: * - first with choice of PCA or tree method NJ or AV @@ -127,11 +157,15 @@ public class CalculationChooser extends JPanel pca = new JRadioButton( MessageManager.getString("label.principal_component_analysis")); pca.setOpaque(false); + neighbourJoining = new JRadioButton( MessageManager.getString("label.tree_calc_nj")); + neighbourJoining.setSelected(true); + neighbourJoining.setOpaque(false); + averageDistance = new JRadioButton( MessageManager.getString("label.tree_calc_av")); - neighbourJoining.setOpaque(false); + averageDistance.setOpaque(false); JPanel calcChoicePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); calcChoicePanel.setOpaque(false); @@ -140,20 +174,19 @@ public class CalculationChooser extends JPanel JPanel treePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); treePanel.setOpaque(false); - treePanel.setBorder(BorderFactory.createTitledBorder(MessageManager - .getString("label.tree"))); + JvSwingUtils.createTitledBorder(treePanel, + MessageManager.getString("label.tree"), true); // then copy the inset dimensions for the border-less PCA panel JPanel pcaBorderless = new JPanel(new FlowLayout(FlowLayout.LEFT)); Insets b = treePanel.getBorder().getBorderInsets(treePanel); - pcaBorderless.setBorder(BorderFactory.createEmptyBorder(2, b.left, 2, - b.right)); + pcaBorderless.setBorder( + BorderFactory.createEmptyBorder(2, b.left, 2, b.right)); pcaBorderless.setOpaque(false); pcaBorderless.add(pca, FlowLayout.LEFT); calcChoicePanel.add(pcaBorderless, FlowLayout.LEFT); - treePanel.add(neighbourJoining); treePanel.add(averageDistance); @@ -163,7 +196,7 @@ public class CalculationChooser extends JPanel calcTypes.add(pca); calcTypes.add(neighbourJoining); calcTypes.add(averageDistance); - + ActionListener calcChanged = new ActionListener() { @Override @@ -175,6 +208,7 @@ public class CalculationChooser extends JPanel pca.addActionListener(calcChanged); neighbourJoining.addActionListener(calcChanged); averageDistance.addActionListener(calcChanged); + /* * score models drop-down - with added tooltips! */ @@ -202,30 +236,30 @@ public class CalculationChooser extends JPanel /* * OK / Cancel buttons */ - ok = new JButton(MessageManager.getString("action.calculate")); - ok.setFont(VERDANA_11PT); - ok.addActionListener(new java.awt.event.ActionListener() + calculate = new JButton(MessageManager.getString("action.calculate")); + calculate.setFont(VERDANA_11PT); + calculate.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(ActionEvent e) { - ok_actionPerformed(); + calculate_actionPerformed(); } }); - JButton cancel = new JButton(MessageManager.getString("action.close")); - cancel.setFont(VERDANA_11PT); - cancel.addActionListener(new java.awt.event.ActionListener() + JButton close = new JButton(MessageManager.getString("action.close")); + close.setFont(VERDANA_11PT); + close.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(ActionEvent e) { - cancel_actionPerformed(e); + close_actionPerformed(); } }); JPanel actionPanel = new JPanel(); actionPanel.setOpaque(false); - actionPanel.add(ok); - actionPanel.add(cancel); + actionPanel.add(calculate); + actionPanel.add(close); boolean includeParams = false; this.add(calcChoicePanel, BorderLayout.CENTER); @@ -241,14 +275,12 @@ public class CalculationChooser extends JPanel setMinimumSize(new Dimension(325, height - 10)); String title = MessageManager.getString("label.choose_calculation"); - if (af.getViewport().viewName != null) + if (af.getViewport().getViewName() != null) { - title = title + " (" + af.getViewport().viewName + ")"; + title = title + " (" + af.getViewport().getViewName() + ")"; } - Desktop.addInternalFrame(frame, - title, width, - height, false); + Desktop.addInternalFrame(frame, title, width, height, false); calcChoicePanel.doLayout(); revalidate(); /* @@ -263,6 +295,7 @@ public class CalculationChooser extends JPanel }; }); + validateCalcTypes(); frame.setLayer(JLayeredPane.PALETTE_LAYER); } @@ -276,17 +309,27 @@ public class CalculationChooser extends JPanel { size = af.getViewport().getSelectionGroup().getSize(); } - if (!(checkEnabled(pca, size, 4) - | checkEnabled(neighbourJoining, size, 3) | checkEnabled( - averageDistance, size, 3))) + + /* + * disable calc options for which there is insufficient input data + * return value of true means enabled and selected + */ + boolean checkPca = checkEnabled(pca, size, MIN_PCA_SELECTION); + boolean checkNeighbourJoining = checkEnabled(neighbourJoining, size, + MIN_TREE_SELECTION); + boolean checkAverageDistance = checkEnabled(averageDistance, size, + MIN_TREE_SELECTION); + + if (checkPca || checkNeighbourJoining || checkAverageDistance) { - ok.setToolTipText(null); - ok.setEnabled(true); + calculate.setToolTipText(null); + calculate.setEnabled(true); } else { - ok.setEnabled(false); + calculate.setEnabled(false); } + updateScoreModels(modelNames, tips); } /** @@ -299,12 +342,12 @@ public class CalculationChooser extends JPanel * - size of input to calculation * @param minsize * - minimum size for calculation - * @return true if size < minsize *and* calc.isSelected + * @return true if size >= minsize and calc.isSelected */ private boolean checkEnabled(JRadioButton calc, int size, int minsize) { - String ttip = MessageManager.formatMessage( - "label.you_need_more_than_n_sequences", minsize); + String ttip = MessageManager + .formatMessage("label.you_need_at_least_n_sequences", minsize); calc.setEnabled(size >= minsize); if (!calc.isEnabled()) @@ -318,15 +361,18 @@ public class CalculationChooser extends JPanel if (calc.isSelected()) { modelNames.setEnabled(calc.isEnabled()); - if (!calc.isEnabled()) + if (calc.isEnabled()) { - ok.setEnabled(false); - ok.setToolTipText(ttip); return true; } + else + { + calculate.setToolTipText(ttip); + } } return false; } + /** * A rather elaborate helper method (blame Swing, not me) that builds a * drop-down list of score models (by name) with descriptions as tooltips. @@ -335,73 +381,138 @@ public class CalculationChooser extends JPanel */ protected JComboBox buildModelOptionsList() { - final JComboBox comboBox = new JComboBox(); - ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer(); - comboBox.setRenderer(renderer); - final List tips = new ArrayList(); + final JComboBox scoreModelsCombo = new JComboBox<>(); + scoreModelsCombo.setRenderer(renderer); /* * show tooltip on mouse over the combobox * note the listener has to be on the components that make up * the combobox, doesn't work if just on the combobox */ - MouseAdapter mouseListener = new MouseAdapter() + final MouseAdapter mouseListener = new MouseAdapter() { @Override public void mouseEntered(MouseEvent e) { - comboBox.setToolTipText(tips.get(comboBox.getSelectedIndex())); + scoreModelsCombo.setToolTipText( + tips.get(scoreModelsCombo.getSelectedIndex())); } @Override public void mouseExited(MouseEvent e) { - comboBox.setToolTipText(null); + scoreModelsCombo.setToolTipText(null); } }; - for (Component c : comboBox.getComponents()) + for (Component c : scoreModelsCombo.getComponents()) { c.addMouseListener(mouseListener); } + updateScoreModels(scoreModelsCombo, tips); + + /* + * set the list of tooltips on the combobox's renderer + */ + renderer.setTooltips(tips); + + return scoreModelsCombo; + } + + private void updateScoreModels(JComboBox comboBox, + List toolTips) + { + Object curSel = comboBox.getSelectedItem(); + toolTips.clear(); + DefaultComboBoxModel model = new DefaultComboBoxModel<>(); + + /* + * select the score models applicable to the alignment type + */ + boolean nucleotide = af.getViewport().getAlignment().isNucleotide(); + List models = getApplicableScoreModels(nucleotide, + pca.isSelected()); + /* * now we can actually add entries to the combobox, * remembering their descriptions for tooltips */ - ScoreModels scoreModels = ScoreModels.getInstance(); - for (ScoreModelI sm : scoreModels.getModels()) + boolean selectedIsPresent = false; + for (ScoreModelI sm : models) { - boolean nucleotide = af.getViewport().getAlignment().isNucleotide(); - if (sm.isDNA() && nucleotide || sm.isProtein() && !nucleotide) + if (curSel != null && sm.getName().equals(curSel)) { - comboBox.addItem(sm.getName()); - - /* - * tooltip is description if provided, else text lookup with - * fallback on the model name - */ - String tooltip = sm.getDescription(); - if (tooltip == null) - { - tooltip = MessageManager.getStringOrReturn("label.score_model_", - sm.getName()); - } - tips.add(tooltip); + selectedIsPresent = true; + curSel = sm.getName(); } + model.addElement(sm.getName()); /* - * set the list of tooltips on the combobox's renderer + * tooltip is description if provided, else text lookup with + * fallback on the model name */ - renderer.setTooltips(tips); + String tooltip = sm.getDescription(); + if (tooltip == null) + { + tooltip = MessageManager.getStringOrReturn("label.score_model_", + sm.getName()); + } + toolTips.add(tooltip); + } + + if (selectedIsPresent) + { + model.setSelectedItem(curSel); + } + // finally, update the model + comboBox.setModel(model); + } + + /** + * Builds a list of score models which are applicable for the alignment and + * calculation type (peptide or generic models for protein, nucleotide or + * generic models for nucleotide). + *

+ * As a special case, includes BLOSUM62 as an extra option for nucleotide PCA. + * This is for backwards compatibility with Jalview prior to 2.8 when BLOSUM62 + * was the only score matrix supported. This is included if property + * BLOSUM62_PCA_FOR_NUCLEOTIDE is set to true in the Jalview properties file. + * + * @param nucleotide + * @param forPca + * @return + */ + protected static List getApplicableScoreModels( + boolean nucleotide, boolean forPca) + { + List filtered = new ArrayList<>(); + + ScoreModels scoreModels = ScoreModels.getInstance(); + for (ScoreModelI sm : scoreModels.getModels()) + { + if (!nucleotide && sm.isProtein() || nucleotide && sm.isDNA()) + { + filtered.add(sm); + } + } + + /* + * special case: add BLOSUM62 as last option for nucleotide PCA, + * for backwards compatibility with Jalview < 2.8 (JAL-2962) + */ + if (nucleotide && forPca + && Cache.getDefault("BLOSUM62_PCA_FOR_NUCLEOTIDE", false)) + { + filtered.add(scoreModels.getBlosum62()); } - return comboBox; + return filtered; } /** * Open and calculate the selected tree or PCA on 'OK' */ - protected void ok_actionPerformed() + protected void calculate_actionPerformed() { boolean doPCA = pca.isSelected(); String modelName = modelNames.getSelectedItem().toString(); @@ -427,7 +538,25 @@ public class CalculationChooser extends JPanel */ protected void openTreePanel(String modelName, SimilarityParamsI params) { - String treeType = neighbourJoining.isSelected() ? TreeBuilder.NEIGHBOUR_JOINING + /* + * gui validation shouldn't allow insufficient sequences here, but leave + * this check in in case this method gets exposed programmatically in future + */ + AlignViewport viewport = af.getViewport(); + SequenceGroup sg = viewport.getSelectionGroup(); + if (sg != null && sg.getSize() < MIN_TREE_SELECTION) + { + JvOptionPane.showMessageDialog(Desktop.desktop, + MessageManager.formatMessage( + "label.you_need_at_least_n_sequences", + MIN_TREE_SELECTION), + MessageManager.getString("label.not_enough_sequences"), + JvOptionPane.WARNING_MESSAGE); + return; + } + + String treeType = neighbourJoining.isSelected() + ? TreeBuilder.NEIGHBOUR_JOINING : TreeBuilder.AVERAGE_DISTANCE; af.newTreePanel(treeType, modelName, params); } @@ -441,22 +570,32 @@ public class CalculationChooser extends JPanel protected void openPcaPanel(String modelName, SimilarityParamsI params) { AlignViewport viewport = af.getViewport(); + + /* + * gui validation shouldn't allow insufficient sequences here, but leave + * this check in in case this method gets exposed programmatically in future + */ if (((viewport.getSelectionGroup() != null) - && (viewport.getSelectionGroup().getSize() < 4) && (viewport - .getSelectionGroup().getSize() > 0)) - || (viewport.getAlignment().getHeight() < 4)) + && (viewport.getSelectionGroup().getSize() < MIN_PCA_SELECTION) + && (viewport.getSelectionGroup().getSize() > 0)) + || (viewport.getAlignment().getHeight() < MIN_PCA_SELECTION)) { - JvOptionPane - .showInternalMessageDialog( - this, - MessageManager - .getString("label.principal_component_analysis_must_take_least_four_input_sequences"), - MessageManager - .getString("label.sequence_selection_insufficient"), - JvOptionPane.WARNING_MESSAGE); + JvOptionPane.showInternalMessageDialog(this, + MessageManager.formatMessage( + "label.you_need_at_least_n_sequences", + MIN_PCA_SELECTION), + MessageManager + .getString("label.sequence_selection_insufficient"), + JvOptionPane.WARNING_MESSAGE); return; } - new PCAPanel(af.alignPanel, modelName, params); + + /* + * construct the panel and kick off its calculation thread + */ + pcaPanel = new PCAPanel(af.alignPanel, modelName, params); + new Thread(pcaPanel).start(); + } /** @@ -498,15 +637,14 @@ public class CalculationChooser extends JPanel */ boolean matchGap = doPCA ? false : treeMatchGaps; - return new SimilarityParams(includeGapGap, matchGap, includeGapResidue, matchOnShortestLength); + return new SimilarityParams(includeGapGap, matchGap, includeGapResidue, + matchOnShortestLength); } /** - * Closes dialog on cancel - * - * @param e + * Closes dialog on Close button press */ - protected void cancel_actionPerformed(ActionEvent e) + protected void close_actionPerformed() { try { @@ -515,4 +653,9 @@ public class CalculationChooser extends JPanel { } } + + public PCAPanel getPcaPanel() + { + return pcaPanel; + } }