Merge branch 'features/JAL-2360colourSchemeApplicability' into features/JAL-2371colle...
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
index eab4aa5..0803773 100644 (file)
@@ -36,15 +36,17 @@ import jalview.datamodel.Annotation;
 import jalview.datamodel.CigarArray;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.HiddenSequences;
-import jalview.datamodel.SearchResults;
+import jalview.datamodel.ProfilesI;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.Blosum62ColourScheme;
+import jalview.schemes.CollectionColourScheme;
+import jalview.schemes.CollectionColourSchemeI;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.PIDColourScheme;
-import jalview.schemes.ResidueProperties;
 import jalview.structure.CommandListener;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
@@ -58,6 +60,7 @@ import jalview.workers.ConsensusThread;
 import jalview.workers.StrucConsensusThread;
 
 import java.awt.Color;
+import java.beans.PropertyChangeSupport;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.BitSet;
@@ -596,7 +599,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   protected boolean ignoreGapsInConsensusCalculation = false;
 
-  protected ColourSchemeI globalColourScheme = null;
+  protected CollectionColourScheme globalColourScheme;
 
   @Override
   public void setGlobalColourScheme(ColourSchemeI cs)
@@ -608,28 +611,39 @@ public abstract class AlignmentViewport implements AlignViewportI,
     // - means that caller decides if they want to just modify state and defer
     // calculation till later or to do all calculations in thread.
     // via changecolour
-    globalColourScheme = cs;
+
+    /*
+     * only instantiate colour scheme once, thereafter update it
+     */
+    if (globalColourScheme == null)
+    {
+      globalColourScheme = new CollectionColourScheme();
+    }
+    globalColourScheme.setColourScheme(cs);
+
     boolean recalc = false;
     if (cs != null)
     {
-      cs.setConservationApplied(recalc = getConservationSelected());
+      recalc = getConservationSelected();
       if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
               || cs instanceof Blosum62ColourScheme)
       {
         recalc = true;
-        cs.setThreshold(viewStyle.getThreshold(),
+        globalColourScheme.setThreshold(viewStyle.getThreshold(),
                 ignoreGapsInConsensusCalculation);
       }
       else
       {
-        cs.setThreshold(0, ignoreGapsInConsensusCalculation);
+        globalColourScheme
+                .setThreshold(0, ignoreGapsInConsensusCalculation);
       }
       if (recalc)
       {
-        cs.setConsensus(hconsensus);
-        cs.setConservation(hconservation);
+        globalColourScheme.setConsensus(hconsensus);
+        globalColourScheme.setConservation(hconservation);
       }
-      cs.alignmentChanged(alignment, hiddenRepSequences);
+      globalColourScheme.setConservationApplied(getConservationSelected());
+      globalColourScheme.alignmentChanged(alignment, hiddenRepSequences);
     }
     if (getColourAppliesToAllGroups())
     {
@@ -637,31 +651,33 @@ public abstract class AlignmentViewport implements AlignViewportI,
       {
         if (cs == null)
         {
-          sg.cs = null;
+          sg.setColourScheme(null);
           continue;
         }
-        sg.cs = cs.applyTo(sg, getHiddenRepSequences());
+        CollectionColourScheme groupColour = new CollectionColourScheme(cs.getInstance(sg,
+                getHiddenRepSequences()));
+        sg.setGroupColourScheme(groupColour);
         sg.setConsPercGaps(ConsPercGaps);
         if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
                 || cs instanceof Blosum62ColourScheme)
         {
-          sg.cs.setThreshold(viewStyle.getThreshold(),
+          groupColour.setThreshold(viewStyle.getThreshold(),
                   isIgnoreGapsConsensus());
           recalc = true;
         }
         else
         {
-          sg.cs.setThreshold(0, isIgnoreGapsConsensus());
+          groupColour.setThreshold(0, isIgnoreGapsConsensus());
         }
 
         if (getConservationSelected())
         {
-          sg.cs.setConservationApplied(true);
+          groupColour.setConservationApplied(true);
           recalc = true;
         }
         else
         {
-          sg.cs.setConservation(null);
+          groupColour.setConservation(null);
           // sg.cs.setThreshold(0, getIgnoreGapsConsensus());
         }
         if (recalc)
@@ -670,7 +686,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
         }
         else
         {
-          sg.cs.alignmentChanged(sg, hiddenRepSequences);
+          groupColour.alignmentChanged(sg, hiddenRepSequences);
         }
       }
     }
@@ -679,6 +695,13 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public ColourSchemeI getGlobalColourScheme()
   {
+    return globalColourScheme == null ? null : globalColourScheme
+            .getColourScheme();
+  }
+
+  @Override
+  public CollectionColourSchemeI getViewportColourScheme()
+  {
     return globalColourScheme;
   }
 
@@ -699,7 +722,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   /**
    * results of alignment consensus analysis for visible portion of view
    */
-  protected Hashtable[] hconsensus = null;
+  protected ProfilesI hconsensus = null;
 
   /**
    * results of cDNA complement consensus visible portion of view
@@ -733,7 +756,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
-  public void setSequenceConsensusHash(Hashtable[] hconsensus)
+  public void setSequenceConsensusHash(ProfilesI hconsensus)
   {
     this.hconsensus = hconsensus;
   }
@@ -745,7 +768,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
-  public Hashtable[] getSequenceConsensusHash()
+  public ProfilesI getSequenceConsensusHash()
   {
     return hconsensus;
   }
@@ -807,7 +830,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void updateConservation(final AlignmentViewPanel ap)
   {
     // see note in mantis : issue number 8585
-    if (alignment.isNucleotide() || conservation == null
+    if (alignment.isNucleotide()
+            || (conservation == null && quality == null)
             || !autoCalculateConsensus)
     {
       return;
@@ -844,14 +868,22 @@ public abstract class AlignmentViewport implements AlignViewportI,
             && !al.getCodonFrames().isEmpty())
     {
       /*
-       * fudge - check first mapping is protein-to-nucleotide
+       * fudge - check first for protein-to-nucleotide mappings
        * (we don't want to do this for protein-to-protein)
        */
-      AlignedCodonFrame mapping = al.getCodonFrames().iterator().next();
-      // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
-      MapList[] mapLists = mapping.getdnaToProt();
-      // mapLists can be empty if project load has not finished resolving seqs
-      if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
+      boolean doConsensus = false;
+      for (AlignedCodonFrame mapping : al.getCodonFrames())
+      {
+        // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
+        MapList[] mapLists = mapping.getdnaToProt();
+        // mapLists can be empty if project load has not finished resolving seqs
+        if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
+        {
+          doConsensus = true;
+          break;
+        }
+      }
+      if (doConsensus)
       {
         if (calculator
                 .getRegisteredWorkersOfClass(ComplementConsensusThread.class) == null)
@@ -905,6 +937,37 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return false;
   }
 
+  public void setAlignment(AlignmentI align)
+  {
+    this.alignment = align;
+  }
+
+  /**
+   * Clean up references when this viewport is closed
+   */
+  @Override
+  public void dispose()
+  {
+    /*
+     * defensively null out references to large objects in case
+     * this object is not garbage collected (as if!)
+     */
+    consensus = null;
+    complementConsensus = null;
+    strucConsensus = null;
+    conservation = null;
+    quality = null;
+    groupConsensus = null;
+    groupConservation = null;
+    hconsensus = null;
+    hcomplementConsensus = null;
+    // colour scheme may hold reference to consensus
+    globalColourScheme = null;
+    // TODO remove listeners from changeSupport?
+    changeSupport = null;
+    setAlignment(null);
+  }
+
   @Override
   public boolean isClosed()
   {
@@ -1099,6 +1162,13 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
+  public boolean hasSelectedColumns()
+  {
+    ColumnSelection columnSelection = getColumnSelection();
+    return columnSelection != null && columnSelection.hasSelectedColumns();
+  }
+
+  @Override
   public boolean hasHiddenColumns()
   {
     return colSel != null && colSel.hasHiddenColumns();
@@ -1225,10 +1295,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return ignoreGapsInConsensusCalculation;
   }
 
-  // / property change stuff
-
+  // property change stuff
   // JBPNote Prolly only need this in the applet version.
-  private final java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
+  private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
           this);
 
   protected boolean showConservation = true;
@@ -1529,7 +1598,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public boolean isHiddenRepSequence(SequenceI seq)
   {
     return (hiddenRepSequences != null && hiddenRepSequences
-                    .containsKey(seq));
+            .containsKey(seq));
   }
 
   /**
@@ -1811,7 +1880,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   void resetAllColourSchemes()
   {
-    ColourSchemeI cs = globalColourScheme;
+    CollectionColourScheme cs = globalColourScheme;
     if (cs != null)
     {
       cs.alignmentChanged(alignment, hiddenRepSequences);
@@ -1820,8 +1889,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
       if (cs.conservationApplied())
       {
         cs.setConservation(Conservation.calculateConservation("All",
-                ResidueProperties.propHash, 3, alignment.getSequences(), 0,
-                alignment.getWidth(), false, getConsPercGaps(), false));
+                alignment.getSequences(), 0, alignment.getWidth(), false,
+                getConsPercGaps(), false));
       }
     }
 
@@ -1872,12 +1941,20 @@ public abstract class AlignmentViewport implements AlignViewportI,
               .getCodonFrames();
       if (codonMappings != null && !codonMappings.isEmpty())
       {
-        // fudge: check mappings are not protein-to-protein
-        // TODO: nicer
-        AlignedCodonFrame mapping = codonMappings.iterator().next();
-        MapList[] mapLists = mapping.getdnaToProt();
-        // mapLists can be empty if project load has not finished resolving seqs
-        if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
+        boolean doConsensus = false;
+        for (AlignedCodonFrame mapping : codonMappings)
+        {
+          // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
+          MapList[] mapLists = mapping.getdnaToProt();
+          // mapLists can be empty if project load has not finished resolving
+          // seqs
+          if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
+          {
+            doConsensus = true;
+            break;
+          }
+        }
+        if (doConsensus)
         {
           complementConsensus = new AlignmentAnnotation("cDNA Consensus",
                   "PID for cDNA", new Annotation[1], 0f, 100f,
@@ -2662,7 +2739,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    *          the SearchResults to add to
    * @return the offset (below top of visible region) of the matched sequence
    */
-  protected int findComplementScrollTarget(SearchResults sr)
+  protected int findComplementScrollTarget(SearchResultsI sr)
   {
     final AlignViewportI complement = getCodingComplement();
     if (complement == null || !complement.isFollowHighlight())
@@ -2700,6 +2777,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
      * all gapped visible regions
      */
     int lastSeq = alignment.getHeight() - 1;
+    List<AlignedCodonFrame> seqMappings = null;
     for (int seqNo = getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++)
     {
       sequence = getAlignment().getSequenceAt(seqNo);
@@ -2711,15 +2789,16 @@ public abstract class AlignmentViewport implements AlignViewportI,
       {
         continue;
       }
-      List<AlignedCodonFrame> seqMappings = MappingUtils
-              .findMappingsForSequence(sequence, mappings);
+      seqMappings = MappingUtils
+              .findMappingsForSequenceAndOthers(sequence, mappings,
+                      getCodingComplement().getAlignment().getSequences());
       if (!seqMappings.isEmpty())
       {
         break;
       }
     }
 
-    if (sequence == null)
+    if (sequence == null || seqMappings == null || seqMappings.isEmpty())
     {
       /*
        * No ungapped mapped sequence in middle column - do nothing
@@ -2727,7 +2806,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
       return 0;
     }
     MappingUtils.addSearchResults(sr, sequence,
-            sequence.findPosition(middleColumn), mappings);
+            sequence.findPosition(middleColumn), seqMappings);
     return seqOffset;
   }
 
@@ -2742,11 +2821,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
   {
     int sgs, sge;
-    if (sg != null
-            && (sgs = sg.getStartRes()) >= 0
+    if (sg != null && (sgs = sg.getStartRes()) >= 0
             && sg.getStartRes() <= (sge = sg.getEndRes())
-            && (colSel == null || colSel.getSelected() == null || colSel
-                    .getSelected().size() == 0))
+            && !this.hasSelectedColumns())
     {
       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
       {
@@ -2764,5 +2841,56 @@ public abstract class AlignmentViewport implements AlignViewportI,
     }
   }
 
+  /**
+   * hold status of current selection group - defined on alignment or not.
+   */
+  private boolean selectionIsDefinedGroup = false;
+
+
+  @Override
+  public boolean isSelectionDefinedGroup()
+  {
+    if (selectionGroup == null)
+    {
+      return false;
+    }
+    if (isSelectionGroupChanged(true))
+    {
+      selectionIsDefinedGroup = false;
+      List<SequenceGroup> gps = alignment.getGroups();
+      if (gps == null || gps.size() == 0)
+      {
+        selectionIsDefinedGroup = false;
+      }
+      else
+      {
+        selectionIsDefinedGroup = gps.contains(selectionGroup);
+      }
+    }
+    return selectionGroup.getContext() == alignment
+            || selectionIsDefinedGroup;
+  }
+
+  /**
+   * null, or currently highlighted results on this view
+   */
+  private SearchResultsI searchResults = null;
+
+  @Override
+  public boolean hasSearchResults()
+  {
+    return searchResults != null;
+  }
+
+  @Override
+  public void setSearchResults(SearchResultsI results)
+  {
+    searchResults = results;
+  }
 
+  @Override
+  public SearchResultsI getSearchResults()
+  {
+    return searchResults;
+  }
 }