JAL-1691 linked scrollling in split frame (Desktop)
[jalview.git] / src / jalview / gui / AlignViewport.java
index 44b4167..d84d3a3 100644 (file)
@@ -44,6 +44,7 @@ import java.awt.Font;
 import java.awt.Rectangle;
 import java.util.ArrayList;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.Set;
 import java.util.Vector;
 
@@ -61,7 +62,9 @@ import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenSequences;
 import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SearchResults;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -71,6 +74,8 @@ import jalview.structure.CommandListener;
 import jalview.structure.SelectionSource;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
+import jalview.util.Comparison;
+import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.AutoCalcSetting;
@@ -1130,4 +1135,74 @@ public class AlignViewport extends AlignmentViewport implements
   {
     this.gatherViewsHere = gatherViewsHere;
   }
+
+  /**
+   * If this viewport has a (Protein/cDNA) complement, then scroll the
+   * complementary alignment to match this one.
+   * 
+   * @param horizontal
+   *          true for horizontal scroll event, false for vertical
+   */
+  public void scrollComplementaryAlignment(boolean horizontal)
+  {
+    /*
+     * If no complement, or it is not following scrolling, do nothing.
+     */
+    // TODO pull up followHighlight to AlignmentViewport/AlignViewportI
+    final AlignViewport codingComplement = (AlignViewport) getCodingComplement();
+    if (codingComplement == null || !codingComplement.followHighlight)
+    {
+      return;
+    }
+    boolean iAmProtein = !getAlignment().isNucleotide();
+    AlignmentI proteinAlignment = iAmProtein ? getAlignment()
+            : codingComplement.getAlignment();
+    if (proteinAlignment == null)
+    {
+      return;
+    }
+    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;
+    }
+    SearchResults sr = MappingUtils.buildSearchResults(sequence,
+            sequence.findPosition(middleColumn), mappings);
+    codingComplement.getAlignPanel().scrollAsComplement(sr, seqOffset,
+            horizontal);
+  }
 }