Merge branch 'feature/JAL-4159_pasimap' into spike/JAL-4159_pasimap_2113_series
[jalview.git] / src / jalview / gui / CalculationChooser.java
index af41e58..80102cb 100644 (file)
  */
 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.bin.Cache;
+import jalview.datamodel.SequenceGroup;
+import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
@@ -52,12 +61,14 @@ 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.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;
 
@@ -79,16 +90,37 @@ public class CalculationChooser extends JPanel
   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();
+      }
+    }
+    
+  }
+
+  private static final int MIN_PASIMAP_SELECTION = 8; 
 
   AlignFrame af;
 
   JRadioButton pca;
 
+  JRadioButton pasimap;        
+
   JRadioButton neighbourJoining;
 
   JRadioButton averageDistance;
 
   JComboBox<String> modelNames;
+  
+  JComboBox<String> ssSourceDropdown;
 
   JButton calculate;
 
@@ -111,6 +143,8 @@ public class CalculationChooser extends JPanel
    */
   private PCAPanel pcaPanel;
 
+  private PaSiMapPanel pasimapPanel;
+
   /**
    * Constructor
    * 
@@ -121,6 +155,7 @@ public class CalculationChooser extends JPanel
     this.af = alignFrame;
     init();
     af.alignPanel.setCalculationDialog(this);
+    
   }
 
   /**
@@ -128,6 +163,7 @@ public class CalculationChooser extends JPanel
    */
   void init()
   {
+    getSecondaryStructureModelName();
     setLayout(new BorderLayout());
     frame = new JInternalFrame();
     frame.setFrameIcon(null);
@@ -158,6 +194,10 @@ public class CalculationChooser extends JPanel
             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);
@@ -187,6 +227,14 @@ public class CalculationChooser extends JPanel
     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
+    calcChoicePanel.add(pasimapBorderless, FlowLayout.LEFT);   // add button with border and everything to the overall ChoicePanel
+
     treePanel.add(neighbourJoining);
     treePanel.add(averageDistance);
 
@@ -194,6 +242,7 @@ public class CalculationChooser extends JPanel
 
     ButtonGroup calcTypes = new ButtonGroup();
     calcTypes.add(pca);
+    calcTypes.add(pasimap);
     calcTypes.add(neighbourJoining);
     calcTypes.add(averageDistance);
 
@@ -206,18 +255,40 @@ 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
 
     /*
      * 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
      */
@@ -232,7 +303,7 @@ public class CalculationChooser extends JPanel
     paramsPanel.add(matchGaps);
     paramsPanel.add(includeGappedColumns);
     paramsPanel.add(shorterSequence);
-
+    
     /*
      * OK / Cancel buttons
      */
@@ -270,7 +341,7 @@ 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));
@@ -315,12 +386,13 @@ 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);
 
-    if (checkPca || checkNeighbourJoining || checkAverageDistance)
+    if (checkPca || checkPasimap || checkNeighbourJoining || checkAverageDistance)
     {
       calculate.setToolTipText(null);
       calculate.setEnabled(true);
@@ -418,7 +490,38 @@ public class CalculationChooser extends JPanel
 
     return scoreModelsCombo;
   }
+  
+
+  private JComboBox<String> buildSSSourcesOptionsList()
+  {
+    final JComboBox<String> comboBox = new JComboBox<>();
+    Object curSel = comboBox.getSelectedItem();
+    DefaultComboBoxModel<String> sourcesModel = new DefaultComboBoxModel<>();
+
+    List<String> 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<String> comboBox,
           List<String> toolTips)
   {
@@ -430,8 +533,12 @@ public class CalculationChooser extends JPanel
      * select the score models applicable to the alignment type
      */
     boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
-    List<ScoreModelI> models = getApplicableScoreModels(nucleotide,
-            pca.isSelected());
+    AlignmentAnnotation[] alignmentAnnotations = af.getViewport().getAlignment().getAlignmentAnnotation();
+
+    boolean ssPresent = AlignmentUtils.isSecondaryStructurePresent(alignmentAnnotations);
+
+    List<ScoreModelI> models = getApplicableScoreModels(nucleotide, pca.isSelected(),
+            ssPresent);
 
     /*
      * now we can actually add entries to the combobox,
@@ -466,6 +573,7 @@ public class CalculationChooser extends JPanel
     }
     // finally, update the model
     comboBox.setModel(model);
+    
   }
 
   /**
@@ -483,14 +591,15 @@ public class CalculationChooser extends JPanel
    * @return
    */
   protected static List<ScoreModelI> getApplicableScoreModels(
-          boolean nucleotide, boolean forPca)
+          boolean nucleotide, boolean forPca, boolean ssPresent)
   {
     List<ScoreModelI> filtered = new ArrayList<>();
 
     ScoreModels scoreModels = ScoreModels.getInstance();
     for (ScoreModelI sm : scoreModels.getModels())
     {
-      if (!nucleotide && sm.isProtein() || nucleotide && sm.isDNA())
+      if (!nucleotide && sm.isProtein() || nucleotide && sm.isDNA() 
+              || ssPresent && sm.isSecondaryStructure())
       {
         filtered.add(sm);
       }
@@ -505,29 +614,55 @@ public class CalculationChooser extends JPanel
     {
       filtered.add(scoreModels.getBlosum62());
     }
-
+    
     return filtered;
   }
 
+  
+  protected List<String> getApplicableSecondaryStructureSources()
+  {
+    AlignmentAnnotation[] annotations = af.getViewport().getAlignment().getAlignmentAnnotation();
+    
+    List<String> ssSources = AlignmentUtils.getSecondaryStructureSources(annotations);
+    //List<String> ssSources = AlignmentUtils.extractSSSourceInAlignmentAnnotation(annotations);
+    
+                 
+    return ssSources;
+  }
+  
   /**
    * Open and calculate the selected tree or PCA on 'OK'
    */
   protected void calculate_actionPerformed()
   {
     boolean doPCA = pca.isSelected();
+    boolean doPaSiMap = pasimap.isSelected();
     String modelName = modelNames.getSelectedItem().toString();
-    SimilarityParamsI params = getSimilarityParameters(doPCA);
+    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)
+    if (doPCA && !doPaSiMap)
     {
       openPcaPanel(modelName, params);
     }
+    else if (doPaSiMap && !doPCA)
+    {
+      openPasimapPanel(modelName, params);
+    }
     else
     {
       openTreePanel(modelName, params);
     }
 
-    // closeFrame();
+    closeFrame();
   }
 
   /**
@@ -599,6 +734,43 @@ public class CalculationChooser extends JPanel
   }
 
   /**
+   * 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, params);
+    new Thread(pasimapPanel).start();
+
+  }
+
+  /**
    * 
    */
   protected void closeFrame()
@@ -618,7 +790,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(