JAL-1691 applet SplitFrame scrolling; pull up of 5 fields to
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
index c0e86cb..d6aa400 100644 (file)
@@ -46,6 +46,8 @@ import jalview.datamodel.AlignmentView;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.CigarArray;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenSequences;
+import jalview.datamodel.SearchResults;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceGroup;
@@ -57,6 +59,8 @@ import jalview.schemes.ResidueProperties;
 import jalview.structure.CommandListener;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
+import jalview.util.Comparison;
+import jalview.util.MappingUtils;
 import jalview.viewmodel.styles.ViewStyle;
 import jalview.workers.AlignCalcManager;
 import jalview.workers.ComplementConsensusThread;
@@ -1196,6 +1200,20 @@ public abstract class AlignmentViewport implements AlignViewportI,
   protected boolean showAutocalculatedAbove;
 
   /**
+   * when set, view will scroll to show the highlighted position
+   */
+  private boolean followHighlight = true;
+
+  // TODO private with getters and setters?
+  public int startRes;
+
+  public int endRes;
+
+  public int startSeq;
+
+  public int endSeq;
+
+  /**
    * Property change listener for changes in alignment
    * 
    * @param listener
@@ -2423,4 +2441,145 @@ public abstract class AlignmentViewport implements AlignViewportI,
   {
     viewStyle.setScaleProteinAsCdna(b);
   }
+
+  /**
+   * @return true if view should scroll to show the highlighted region of a
+   *         sequence
+   * @return
+   */
+  @Override
+  public final boolean isFollowHighlight()
+  {
+    return followHighlight;
+  }
+
+  @Override
+  public final void setFollowHighlight(boolean b)
+  {
+    this.followHighlight = b;
+  }
+
+  public int getStartRes()
+  {
+    return startRes;
+  }
+
+  public int getEndRes()
+  {
+    return endRes;
+  }
+
+  public int getStartSeq()
+  {
+    return startSeq;
+  }
+
+  public void setStartRes(int res)
+  {
+    this.startRes = res;
+  }
+
+  public void setStartSeq(int seq)
+  {
+    this.startSeq = seq;
+  }
+
+  public void setEndRes(int res)
+  {
+    if (res > alignment.getWidth() - 1)
+    {
+      // log.System.out.println(" Corrected res from " + res + " to maximum " +
+      // (alignment.getWidth()-1));
+      res = alignment.getWidth() - 1;
+    }
+    if (res < 0)
+    {
+      res = 0;
+    }
+    this.endRes = res;
+  }
+
+  public void setEndSeq(int seq)
+  {
+    if (seq > alignment.getHeight())
+    {
+      seq = alignment.getHeight();
+    }
+    if (seq < 0)
+    {
+      seq = 0;
+    }
+    this.endSeq = seq;
+  }
+
+  public int getEndSeq()
+  {
+    return endSeq;
+  }
+
+  /**
+   * Helper method to populate the SearchResults with the location in the
+   * complementary alignment to scroll to, in order to match this one.
+   * 
+   * @param sr
+   *          the SearchResults to add to
+   * @return the offset (below top of visible region) of the matched sequence
+   */
+  protected int findComplementScrollTarget(SearchResults sr)
+  {
+    final AlignViewportI codingComplement = getCodingComplement();
+    if (codingComplement == null || !codingComplement.isFollowHighlight())
+    {
+      return 0;
+    }
+    boolean iAmProtein = !getAlignment().isNucleotide();
+    AlignmentI proteinAlignment = iAmProtein ? getAlignment()
+            : codingComplement.getAlignment();
+    if (proteinAlignment == null)
+    {
+      return 0;
+    }
+    final Set<AlignedCodonFrame> mappings = proteinAlignment
+            .getCodonFrames();
+  
+    /*
+     * Heuristic: find the first mapped sequence (if any) with a non-gapped
+     * residue in the middle column of the visible region. Scroll the
+     * complementary alignment to line up the corresponding residue.
+     */
+    int seqOffset = 0;
+    SequenceI sequence = null;
+    int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2;
+    final HiddenSequences hiddenSequences = getAlignment()
+            .getHiddenSequences();
+    for (int seqNo = getStartSeq(); seqNo < getEndSeq(); seqNo++, seqOffset++)
+    {
+      sequence = getAlignment().getSequenceAt(seqNo);
+      if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
+      {
+        continue;
+      }
+      if (Comparison.isGap(sequence.getCharAt(middleColumn)))
+      {
+        continue;
+      }
+      List<AlignedCodonFrame> seqMappings = MappingUtils
+              .findMappingsForSequence(sequence, mappings);
+      if (!seqMappings.isEmpty())
+      {
+        break;
+      }
+    }
+  
+    if (sequence == null)
+    {
+      /*
+       * No ungapped mapped sequence in middle column - do nothing
+       */
+      return 0;
+    }
+    MappingUtils.addSearchResults(sr, sequence,
+            sequence.findPosition(middleColumn), mappings);
+    return seqOffset;
+  }
 }