JAL-2089 patch broken merge to master for Release 2.10.0b1
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
index 7bbe677..c1c88c1 100644 (file)
@@ -44,11 +44,11 @@ import jalview.datamodel.SequenceI;
 import jalview.schemes.Blosum62ColourScheme;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.PIDColourScheme;
-import jalview.schemes.ResidueProperties;
 import jalview.structure.CommandListener;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
 import jalview.util.Comparison;
+import jalview.util.MapList;
 import jalview.util.MappingUtils;
 import jalview.viewmodel.styles.ViewStyle;
 import jalview.workers.AlignCalcManager;
@@ -57,6 +57,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;
@@ -65,7 +66,6 @@ import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * base class holding visualization and analysis attributes and common logic for
@@ -612,7 +612,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     boolean recalc = false;
     if (cs != null)
     {
-      cs.setConservationApplied(recalc = getConservationSelected());
+      recalc = getConservationSelected();
       if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
               || cs instanceof Blosum62ColourScheme)
       {
@@ -629,6 +629,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
         cs.setConsensus(hconsensus);
         cs.setConservation(hconservation);
       }
+      cs.setConservationApplied(getConservationSelected());
       cs.alignmentChanged(alignment, hiddenRepSequences);
     }
     if (getColourAppliesToAllGroups())
@@ -807,7 +808,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;
@@ -837,15 +839,36 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
     /*
      * A separate thread to compute cDNA consensus for a protein alignment
+     * which has mapping to cDNA
      */
     final AlignmentI al = this.getAlignment();
     if (!al.isNucleotide() && al.getCodonFrames() != null
             && !al.getCodonFrames().isEmpty())
     {
-      if (calculator
-              .getRegisteredWorkersOfClass(ComplementConsensusThread.class) == null)
+      /*
+       * fudge - check first for protein-to-nucleotide mappings
+       * (we don't want to do this for protein-to-protein)
+       */
+      boolean doConsensus = false;
+      for (AlignedCodonFrame mapping : al.getCodonFrames())
       {
-        calculator.registerWorker(new ComplementConsensusThread(this, ap));
+        // 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)
+        {
+          calculator
+                  .registerWorker(new ComplementConsensusThread(this, ap));
+        }
       }
     }
   }
@@ -892,6 +915,35 @@ 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;
+    // TODO removed listeners from changeSupport?
+    changeSupport = null;
+    setAlignment(null);
+  }
+
   @Override
   public boolean isClosed()
   {
@@ -1065,6 +1117,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     {
       updateHiddenColumns();
     }
+    isColSelChanged(true);
   }
 
   /**
@@ -1085,6 +1138,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();
@@ -1193,8 +1253,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   public boolean isColSelChanged(boolean b)
   {
-    int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel
-            .hashCode();
+    int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
     if (hc != -1 && hc != colselhash)
     {
       if (b)
@@ -1212,10 +1271,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;
@@ -1295,7 +1353,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
     colSel.hideSelectedColumns();
     setSelectionGroup(null);
-
+    isColSelChanged(true);
   }
 
   public void hideColumns(int start, int end)
@@ -1308,17 +1366,19 @@ public abstract class AlignmentViewport implements AlignViewportI,
     {
       colSel.hideColumns(start, end);
     }
+    isColSelChanged(true);
   }
 
   public void showColumn(int col)
   {
     colSel.revealHiddenColumns(col);
-
+    isColSelChanged(true);
   }
 
   public void showAllHiddenColumns()
   {
     colSel.revealAllHiddenColumns();
+    isColSelChanged(true);
   }
 
   // common hide/show seq stuff
@@ -1398,6 +1458,39 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   /**
+   * Hides the specified sequence, or the sequences it represents
+   * 
+   * @param sequence
+   *          the sequence to hide, or keep as representative
+   * @param representGroup
+   *          if true, hide the current selection group except for the
+   *          representative sequence
+   */
+  public void hideSequences(SequenceI sequence, boolean representGroup)
+  {
+    if (selectionGroup == null || selectionGroup.getSize() < 1)
+    {
+      hideSequence(new SequenceI[] { sequence });
+      return;
+    }
+
+    if (representGroup)
+    {
+      hideRepSequences(sequence, selectionGroup);
+      setSelectionGroup(null);
+      return;
+    }
+
+    int gsize = selectionGroup.getSize();
+    SequenceI[] hseqs = selectionGroup.getSequences().toArray(
+            new SequenceI[gsize]);
+
+    hideSequence(hseqs);
+    setSelectionGroup(null);
+    sendSelection();
+  }
+
+  /**
    * Set visibility for any annotations for the given sequence.
    * 
    * @param sequenceI
@@ -1405,11 +1498,15 @@ public abstract class AlignmentViewport implements AlignViewportI,
   protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
           boolean visible)
   {
-    for (AlignmentAnnotation ann : alignment.getAlignmentAnnotation())
+    AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
+    if (anns != null)
     {
-      if (ann.sequenceRef == sequenceI)
+      for (AlignmentAnnotation ann : anns)
       {
-        ann.visible = visible;
+        if (ann.sequenceRef == sequenceI)
+        {
+          ann.visible = visible;
+        }
       }
     }
   }
@@ -1424,7 +1521,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
     if (hiddenRepSequences == null)
     {
-      hiddenRepSequences = new Hashtable();
+      hiddenRepSequences = new Hashtable<SequenceI, SequenceCollectionI>();
     }
 
     hiddenRepSequences.put(repSequence, sg);
@@ -1450,13 +1547,42 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   }
 
+  /**
+   * 
+   * @return null or the current reference sequence
+   */
+  public SequenceI getReferenceSeq()
+  {
+    return alignment.getSeqrep();
+  }
+
+  /**
+   * @param seq
+   * @return true iff seq is the reference for the alignment
+   */
+  public boolean isReferenceSeq(SequenceI seq)
+  {
+    return alignment.getSeqrep() == seq;
+  }
+
+  /**
+   * 
+   * @param seq
+   * @return true if there are sequences represented by this sequence that are
+   *         currently hidden
+   */
   public boolean isHiddenRepSequence(SequenceI seq)
   {
-    return alignment.getSeqrep() == seq
-            || (hiddenRepSequences != null && hiddenRepSequences
-                    .containsKey(seq));
+    return (hiddenRepSequences != null && hiddenRepSequences
+            .containsKey(seq));
   }
 
+  /**
+   * 
+   * @param seq
+   * @return null or a sequence group containing the sequences that seq
+   *         represents
+   */
   public SequenceGroup getRepresentedSequences(SequenceI seq)
   {
     return (SequenceGroup) (hiddenRepSequences == null ? null
@@ -1544,6 +1670,13 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public String[] getViewAsString(boolean selectedRegionOnly)
   {
+    return getViewAsString(selectedRegionOnly, true);
+  }
+
+  @Override
+  public String[] getViewAsString(boolean selectedRegionOnly,
+          boolean exportHiddenSeqs)
+  {
     String[] selection = null;
     SequenceI[] seqs = null;
     int i, iSize;
@@ -1557,13 +1690,13 @@ public abstract class AlignmentViewport implements AlignViewportI,
     }
     else
     {
-      if (alignment.getHiddenSequences() != null)
+      if (hasHiddenRows() && exportHiddenSeqs)
       {
-        iSize = alignment.getHiddenSequences().getFullAlignment()
-                .getHeight();
-        seqs = alignment.getHiddenSequences().getFullAlignment()
-                .getSequencesArray();
-        end = alignment.getHiddenSequences().getFullAlignment().getWidth();
+        AlignmentI fullAlignment = alignment.getHiddenSequences()
+                .getFullAlignment();
+        iSize = fullAlignment.getHeight();
+        seqs = fullAlignment.getSequencesArray();
+        end = fullAlignment.getWidth();
       }
       else
       {
@@ -1731,9 +1864,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
       cs.setConsensus(hconsensus);
       if (cs.conservationApplied())
       {
-        cs.setConservation(Conservation.calculateConservation("All",
-                ResidueProperties.propHash, 3, alignment.getSequences(), 0,
-                alignment.getWidth(), false, getConsPercGaps(), false));
+        cs.setConservation(Conservation.calculateConservation("All", 3,
+                alignment.getSequences(), 0, alignment.getWidth(), false,
+                getConsPercGaps(), false));
       }
     }
 
@@ -1780,14 +1913,30 @@ public abstract class AlignmentViewport implements AlignViewportI,
   {
     if (!alignment.isNucleotide())
     {
-      final Set<AlignedCodonFrame> codonMappings = alignment
+      final List<AlignedCodonFrame> codonMappings = alignment
               .getCodonFrames();
       if (codonMappings != null && !codonMappings.isEmpty())
       {
-        complementConsensus = new AlignmentAnnotation("cDNA Consensus",
-                "PID for cDNA", new Annotation[1], 0f, 100f,
-                AlignmentAnnotation.BAR_GRAPH);
-        initConsensus(complementConsensus);
+        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,
+                  AlignmentAnnotation.BAR_GRAPH);
+          initConsensus(complementConsensus);
+        }
       }
     }
   }
@@ -2099,7 +2248,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public boolean areFeaturesDisplayed()
   {
     return featuresDisplayed != null
-            && featuresDisplayed.getRegisterdFeaturesCount() > 0;
+            && featuresDisplayed.getRegisteredFeaturesCount() > 0;
   }
 
   /**
@@ -2580,7 +2729,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     {
       return 0;
     }
-    final Set<AlignedCodonFrame> mappings = proteinAlignment
+    final List<AlignedCodonFrame> mappings = proteinAlignment
             .getCodonFrames();
 
     /*
@@ -2604,6 +2753,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);
@@ -2615,15 +2765,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
@@ -2631,7 +2782,67 @@ public abstract class AlignmentViewport implements AlignViewportI,
       return 0;
     }
     MappingUtils.addSearchResults(sr, sequence,
-            sequence.findPosition(middleColumn), mappings);
+            sequence.findPosition(middleColumn), seqMappings);
     return seqOffset;
   }
+
+  /**
+   * synthesize a column selection if none exists so it covers the given
+   * selection group. if wholewidth is false, no column selection is made if the
+   * selection group covers the whole alignment width.
+   * 
+   * @param sg
+   * @param wholewidth
+   */
+  public void expandColSelection(SequenceGroup sg, boolean wholewidth)
+  {
+    int sgs, sge;
+    if (sg != null && (sgs = sg.getStartRes()) >= 0
+            && sg.getStartRes() <= (sge = sg.getEndRes())
+            && !this.hasSelectedColumns())
+    {
+      if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
+      {
+        // do nothing
+        return;
+      }
+      if (colSel == null)
+      {
+        colSel = new ColumnSelection();
+      }
+      for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
+      {
+        colSel.addElement(cspos);
+      }
+    }
+  }
+
+  /**
+   * 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;
+  }
 }