X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FCalculationChooser.java;h=af41e588c2623fc673d1bfb51455c81abe088061;hb=HEAD;hp=8a95594f133d5d431acd9c0b7485783a25704a7f;hpb=968e71fa2914ce20dd8caca18dd7d4651b636af5;p=jalview.git diff --git a/src/jalview/gui/CalculationChooser.java b/src/jalview/gui/CalculationChooser.java index 8a95594..8e5cc9e 100644 --- a/src/jalview/gui/CalculationChooser.java +++ b/src/jalview/gui/CalculationChooser.java @@ -25,9 +25,11 @@ 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; - +import jalview.util.Platform; +import jalview.viewmodel.AlignmentViewport; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -60,6 +62,18 @@ import javax.swing.JRadioButton; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; +import jalview.analysis.AlignmentUtils; +import jalview.analysis.TreeBuilder; +import jalview.analysis.scoremodels.ScoreMatrix; +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.AlignmentAnnotation; +import jalview.datamodel.SequenceGroup; +import jalview.util.MessageManager; + /** * A dialog where a user can choose and action Tree or PCA calculation options */ @@ -75,20 +89,50 @@ public class CalculationChooser extends JPanel private static final Font VERDANA_11PT = new Font("Verdana", 0, 11); + private static final int MIN_PAIRWISE_SELECTION = 2; + private static final int MIN_TREE_SELECTION = 3; private static final int MIN_PCA_SELECTION = 4; + private String secondaryStructureModelName; + + private void getSecondaryStructureModelName() + { + + ScoreModels scoreModels = ScoreModels.getInstance(); + for (ScoreModelI sm : scoreModels.getModels()) + { + if (sm.isSecondaryStructure()) + { + secondaryStructureModelName = sm.getName(); + } + } + + } + + /** + * minimum number of sequences needed for PASIMAP is 9 (so each has 8 + * connections) + */ + private static final int MIN_PASIMAP_SELECTION = 9; + AlignFrame af; + JRadioButton pairwise; + JRadioButton pca; + JRadioButton pasimap; + JRadioButton neighbourJoining; JRadioButton averageDistance; JComboBox modelNames; + JComboBox ssSourceDropdown; + JButton calculate; private JInternalFrame frame; @@ -103,7 +147,14 @@ public class CalculationChooser extends JPanel final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer(); - List tips = new ArrayList(); + List tips = new ArrayList<>(); + + /* + * the most recently opened PCA results panel + */ + private PCAPanel pcaPanel; + + private PaSiMapPanel pasimapPanel; /** * Constructor @@ -115,6 +166,7 @@ public class CalculationChooser extends JPanel this.af = alignFrame; init(); af.alignPanel.setCalculationDialog(this); + } /** @@ -122,8 +174,10 @@ public class CalculationChooser extends JPanel */ void init() { + getSecondaryStructureModelName(); setLayout(new BorderLayout()); frame = new JInternalFrame(); + frame.setFrameIcon(null); frame.setContentPane(this); this.setBackground(Color.white); frame.addFocusListener(new FocusListener() @@ -150,12 +204,24 @@ public class CalculationChooser extends JPanel pca = new JRadioButton( MessageManager.getString("label.principal_component_analysis")); pca.setOpaque(false); + + pasimap = new JRadioButton( // create the JRadioButton for pasimap with + // label.pasimap as its text + MessageManager.getString("label.pasimap")); + pasimap.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); + + pairwise = new JRadioButton( + MessageManager.getString("action.pairwise_alignment")); + pairwise.setOpaque(false); JPanel calcChoicePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); calcChoicePanel.setOpaque(false); @@ -164,29 +230,67 @@ 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); + // create pasimap panel + JPanel pasimapBorderless = new JPanel(new FlowLayout(FlowLayout.LEFT)); // create + // new + // JPanel + // (button) + // for + // pasimap + pasimapBorderless.setBorder( + BorderFactory.createEmptyBorder(2, b.left, 2, b.right)); // set + // border + // (margin) + // for + // button + // (same as + // treePanel + // and pca) + pasimapBorderless.setOpaque(false); // false -> stops every pixel inside + // border from being painted + pasimapBorderless.add(pasimap, FlowLayout.LEFT); // add pasimap button to + // the JPanel + if (!Platform.isJS()) + { + // FIXME JAL-4443 + calcChoicePanel.add(pasimapBorderless, FlowLayout.LEFT); // add button + // with + // border and + // everything to + // the overall + // ChoicePanel + } + treePanel.add(neighbourJoining); treePanel.add(averageDistance); calcChoicePanel.add(treePanel); + calcChoicePanel.add(pairwise, FlowLayout.CENTER); ButtonGroup calcTypes = new ButtonGroup(); calcTypes.add(pca); + if (!Platform.isJS()) + { + // FIXME JAL-4443 + calcTypes.add(pasimap); + } calcTypes.add(neighbourJoining); calcTypes.add(averageDistance); - + calcTypes.add(pairwise); + ActionListener calcChanged = new ActionListener() { @Override @@ -196,17 +300,44 @@ public class CalculationChooser extends JPanel } }; pca.addActionListener(calcChanged); + pasimap.addActionListener(calcChanged); // add the calcChanged + // ActionListener to pasimap --> + // <++> idk neighbourJoining.addActionListener(calcChanged); averageDistance.addActionListener(calcChanged); + // to do + ssSourceDropdown = buildSSSourcesOptionsList(); + ssSourceDropdown.setVisible(false); // Initially hide the dropdown + pairwise.addActionListener(calcChanged); /* * score models drop-down - with added tooltips! */ modelNames = buildModelOptionsList(); + // Step 3: Show or Hide Dropdown Based on Selection + modelNames.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + String selectedModel = modelNames.getSelectedItem().toString(); + + if (selectedModel.equals(secondaryStructureModelName)) + { + ssSourceDropdown.setVisible(true); + } + else + { + ssSourceDropdown.setVisible(false); + } + } + }); + JPanel scoreModelPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); scoreModelPanel.setOpaque(false); scoreModelPanel.add(modelNames); + scoreModelPanel.add(ssSourceDropdown); /* * score model parameters @@ -260,14 +391,14 @@ public class CalculationChooser extends JPanel } this.add(actionPanel, BorderLayout.SOUTH); - int width = 350; + int width = 365; int height = includeParams ? 420 : 240; 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); @@ -285,6 +416,7 @@ public class CalculationChooser extends JPanel }; }); + validateCalcTypes(); frame.setLayer(JLayeredPane.PALETTE_LAYER); } @@ -304,12 +436,18 @@ public class CalculationChooser extends JPanel * return value of true means enabled and selected */ boolean checkPca = checkEnabled(pca, size, MIN_PCA_SELECTION); + boolean checkPasimap = checkEnabled(pasimap, size, + MIN_PASIMAP_SELECTION); // check if pasimap is enabled and min_size + // is fulfilled boolean checkNeighbourJoining = checkEnabled(neighbourJoining, size, MIN_TREE_SELECTION); boolean checkAverageDistance = checkEnabled(averageDistance, size, MIN_TREE_SELECTION); + boolean checkPairwise = checkEnabled(pairwise, size, + MIN_PAIRWISE_SELECTION); - if (checkPca || checkNeighbourJoining || checkAverageDistance) + if (checkPca || checkPasimap || checkPca || checkNeighbourJoining + || checkAverageDistance || checkPairwise) { calculate.setToolTipText(null); calculate.setEnabled(true); @@ -335,8 +473,8 @@ public class CalculationChooser extends JPanel */ private boolean checkEnabled(JRadioButton calc, int size, int minsize) { - String ttip = MessageManager.formatMessage( - "label.you_need_at_least_n_sequences", minsize); + String ttip = MessageManager + .formatMessage("label.you_need_at_least_n_sequences", minsize); calc.setEnabled(size >= minsize); if (!calc.isEnabled()) @@ -370,7 +508,7 @@ public class CalculationChooser extends JPanel */ protected JComboBox buildModelOptionsList() { - final JComboBox scoreModelsCombo = new JComboBox(); + final JComboBox scoreModelsCombo = new JComboBox<>(); scoreModelsCombo.setRenderer(renderer); /* @@ -383,7 +521,8 @@ public class CalculationChooser extends JPanel @Override public void mouseEntered(MouseEvent e) { - scoreModelsCombo.setToolTipText(tips.get(scoreModelsCombo.getSelectedIndex())); + scoreModelsCombo.setToolTipText( + tips.get(scoreModelsCombo.getSelectedIndex())); } @Override @@ -407,50 +546,152 @@ public class CalculationChooser extends JPanel return scoreModelsCombo; } + private JComboBox buildSSSourcesOptionsList() + { + final JComboBox comboBox = new JComboBox<>(); + Object curSel = comboBox.getSelectedItem(); + DefaultComboBoxModel sourcesModel = new DefaultComboBoxModel<>(); + + List ssSources = getApplicableSecondaryStructureSources(); + + boolean selectedIsPresent = false; + for (String source : ssSources) + { + if (curSel != null && source.equals(curSel)) + { + selectedIsPresent = true; + curSel = source; + } + sourcesModel.addElement(source); + + } + + if (selectedIsPresent) + { + sourcesModel.setSelectedItem(curSel); + } + comboBox.setModel(sourcesModel); + + return comboBox; + } + private void updateScoreModels(JComboBox comboBox, List toolTips) { Object curSel = comboBox.getSelectedItem(); toolTips.clear(); - DefaultComboBoxModel model = new DefaultComboBoxModel(); + DefaultComboBoxModel model = new DefaultComboBoxModel<>(); + + /* + * select the score models applicable to the alignment type + */ + boolean nucleotide = af.getViewport().getAlignment().isNucleotide(); + AlignmentAnnotation[] alignmentAnnotations = af.getViewport() + .getAlignment().getAlignmentAnnotation(); + + boolean ssPresent = AlignmentUtils + .isSecondaryStructurePresent(alignmentAnnotations); + + List models = getApplicableScoreModels(nucleotide, + pca.isSelected(), ssPresent, (pasimap.isSelected() || pairwise.isSelected())); /* * now we can actually add entries to the combobox, * remembering their descriptions for tooltips */ - ScoreModels scoreModels = ScoreModels.getInstance(); boolean selectedIsPresent = false; - for (ScoreModelI sm : scoreModels.getModels()) + for (ScoreModelI sm : models) { - boolean nucleotide = af.getViewport().getAlignment().isNucleotide(); - if (sm.isDNA() && nucleotide || sm.isProtein() && !nucleotide) + if (curSel != null && sm.getName().equals(curSel)) { - if (curSel != null && sm.getName().equals(curSel)) - { - selectedIsPresent = true; - curSel = sm.getName(); - } - model.addElement(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()); - } - toolTips.add(tooltip); + selectedIsPresent = true; + curSel = sm.getName(); } + model.addElement(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()); + } + toolTips.add(tooltip); } + if (selectedIsPresent) { model.setSelectedItem(curSel); } // finally, update the model comboBox.setModel(model); + comboBox.setEnabled(model.getSize() > 0); + + } + + /** + * 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 + * @param ssPresent + * - include secondary structure similarity model + * @param forPasimap + * - limit to ScoreMatrix based models - allows use of AlignSeq + * @return + */ + protected static List getApplicableScoreModels( + boolean nucleotide, boolean forPca, boolean ssPresent, + boolean forPasimap) + { + List filtered = new ArrayList<>(); + + ScoreModels scoreModels = ScoreModels.getInstance(); + for (ScoreModelI sm : scoreModels.getModels()) + { + if ((!forPasimap || sm instanceof ScoreMatrix) + && (!nucleotide && sm.isProtein() || nucleotide && sm.isDNA() + || sm.isSecondaryStructure() && ssPresent)) + + { + filtered.add(sm); + } + } + + /* + * special case: add BLOSUM62 as last option for nucleotide PCA, + * for backwards compatibility with Jalview < 2.8 (JAL-2962) + */ + if (!forPasimap && nucleotide && forPca + && Cache.getDefault("BLOSUM62_PCA_FOR_NUCLEOTIDE", false)) + { + filtered.add(scoreModels.getBlosum62()); + } + + return filtered; + } + + protected List getApplicableSecondaryStructureSources() + { + AlignmentAnnotation[] annotations = af.getViewport().getAlignment() + .getAlignmentAnnotation(); + + List ssSources = AlignmentUtils + .getSecondaryStructureSources(annotations); + // List ssSources = + // AlignmentUtils.extractSSSourceInAlignmentAnnotation(annotations); + + return ssSources; } /** @@ -459,19 +700,67 @@ public class CalculationChooser extends JPanel protected void calculate_actionPerformed() { boolean doPCA = pca.isSelected(); - String modelName = modelNames.getSelectedItem().toString(); - SimilarityParamsI params = getSimilarityParameters(doPCA); + boolean doPaSiMap = pasimap.isSelected(); + boolean doPairwise = pairwise.isSelected(); + String modelName = modelNames.getSelectedItem() == null ? "" + : modelNames.getSelectedItem().toString(); + String ssSource = ""; + Object selectedItem = ssSourceDropdown.getSelectedItem(); + if (selectedItem != null) + { + ssSource = selectedItem.toString(); + } + SimilarityParams params = getSimilarityParameters(doPCA); + if (ssSource.length() > 0) + { + params.setSecondaryStructureSource(ssSource); + } if (doPCA) { openPcaPanel(modelName, params); } + else if (doPaSiMap) + { + openPasimapPanel(modelName, params); + } + else if (doPairwise) + { + openPairwisePanel(modelName, params); + } else { openTreePanel(modelName, params); } - // closeFrame(); + closeFrame(); + } + + private void openPairwisePanel(String modelName, SimilarityParamsI params) + { + ScoreModelI sm = ScoreModels.getInstance().getScoreModel(modelName, + af.alignPanel); + if (sm == null || !(sm instanceof ScoreMatrix)) + { + return; + } + new Thread(new Runnable() + { + @Override + public void run() + { + String pairwise_alignment_title = af.formCalculationTitle( + MessageManager.getString("action.pairwise_alignment") + + " with " + sm.getName(), + af.getViewport().getSelectionGroup() != null, + af.getTitle()); + JInternalFrame frame = new JInternalFrame(); + frame.setFrameIcon(null); + frame.setContentPane( + new PairwiseAlignPanel(af.getViewport(), (ScoreMatrix) sm)); + Desktop.addInternalFrame(frame, pairwise_alignment_title, 600, 500); + } + }).start(); } /** @@ -490,19 +779,17 @@ public class CalculationChooser extends JPanel 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", + 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); + MessageManager.getString("label.not_enough_sequences"), + JvOptionPane.WARNING_MESSAGE); return; } - String treeType = neighbourJoining.isSelected() ? TreeBuilder.NEIGHBOUR_JOINING + String treeType = neighbourJoining.isSelected() + ? TreeBuilder.NEIGHBOUR_JOINING : TreeBuilder.AVERAGE_DISTANCE; af.newTreePanel(treeType, modelName, params); } @@ -522,18 +809,66 @@ public class CalculationChooser extends JPanel * this check in in case this method gets exposed programmatically in future */ if (((viewport.getSelectionGroup() != null) - && (viewport.getSelectionGroup().getSize() < MIN_PCA_SELECTION) && (viewport - .getSelectionGroup().getSize() > 0)) + && (viewport.getSelectionGroup().getSize() < MIN_PCA_SELECTION) + && (viewport.getSelectionGroup().getSize() > 0)) || (viewport.getAlignment().getHeight() < MIN_PCA_SELECTION)) { - JvOptionPane.showInternalMessageDialog(this, MessageManager - .formatMessage("label.you_need_at_least_n_sequences", - MIN_PCA_SELECTION), MessageManager - .getString("label.sequence_selection_insufficient"), + 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(); + + } + + /** + * Open a new PaSiMap panel on the desktop + * + * @param modelName + * @param params + */ + protected void openPasimapPanel(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() < MIN_PASIMAP_SELECTION) + && (viewport.getSelectionGroup().getSize() > 0)) + || (viewport.getAlignment() + .getHeight() < MIN_PASIMAP_SELECTION)) + { + JvOptionPane.showInternalMessageDialog(this, + MessageManager.formatMessage( + "label.you_need_at_least_n_sequences", + MIN_PASIMAP_SELECTION), + MessageManager + .getString("label.sequence_selection_insufficient"), + JvOptionPane.WARNING_MESSAGE); + return; + } + + /* + * construct the panel and kick off its calculation thread + */ + pasimapPanel = new PaSiMapPanel(af.alignPanel, modelName); + new Thread(pasimapPanel).start(); + } /** @@ -556,7 +891,7 @@ public class CalculationChooser extends JPanel * @param doPCA * @return */ - protected SimilarityParamsI getSimilarityParameters(boolean doPCA) + protected SimilarityParams getSimilarityParameters(boolean doPCA) { // commented out: parameter choices read from gui widgets // SimilarityParamsI params = new SimilarityParams( @@ -575,7 +910,8 @@ public class CalculationChooser extends JPanel */ boolean matchGap = doPCA ? false : treeMatchGaps; - return new SimilarityParams(includeGapGap, matchGap, includeGapResidue, matchOnShortestLength); + return new SimilarityParams(includeGapGap, matchGap, includeGapResidue, + matchOnShortestLength); } /** @@ -590,4 +926,9 @@ public class CalculationChooser extends JPanel { } } + + public PCAPanel getPcaPanel() + { + return pcaPanel; + } }