JAL-1645 Version-Rel Version 2.9 Year-Rel 2015 Licensing glob
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
index c0e86cb..a15e9a6 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
+ * Copyright (C) 2015 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  */
 package jalview.viewmodel;
 
-import java.awt.Color;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.Conservation;
 import jalview.api.AlignCalcManagerI;
@@ -46,6 +35,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,12 +48,25 @@ 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;
 import jalview.workers.ConsensusThread;
 import jalview.workers.StrucConsensusThread;
 
+import java.awt.Color;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Deque;
+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
  * an active alignment view displayed in the GUI
@@ -71,7 +75,7 @@ import jalview.workers.StrucConsensusThread;
  * 
  */
 public abstract class AlignmentViewport implements AlignViewportI,
-        ViewStyleI, CommandListener, VamsasSource
+        CommandListener, VamsasSource
 {
   protected ViewStyleI viewStyle = new ViewStyle();
 
@@ -502,15 +506,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   /**
-   * @param selected
-   * @see jalview.api.ViewStyleI#setShowSeqFeaturesHeight(boolean)
-   */
-  public void setShowSeqFeaturesHeight(boolean selected)
-  {
-    viewStyle.setShowSeqFeaturesHeight(selected);
-  }
-
-  /**
    * alignment displayed in the viewport. Please use get/setter
    */
   protected AlignmentI alignment;
@@ -545,7 +540,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return isDataset;
   }
 
-
   private Map<SequenceI, SequenceCollectionI> hiddenRepSequences;
 
   protected ColumnSelection colSel = new ColumnSelection();
@@ -558,12 +552,11 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   protected ColourSchemeI globalColourScheme = null;
 
-
   @Override
   public void setGlobalColourScheme(ColourSchemeI cs)
   {
     // TODO: logic refactored from AlignFrame changeColour -
-    // autorecalc stuff should be changed to rely on the worker system
+    // TODO: autorecalc stuff should be changed to rely on the worker system
     // check to see if we should implement a changeColour(cs) method rather than
     // put th logic in here
     // - means that caller decides if they want to just modify state and defer
@@ -635,7 +628,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
         }
       }
     }
-
   }
 
   @Override
@@ -984,7 +976,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   public boolean sortByTree = false;
 
-
   /**
    * 
    * 
@@ -1060,12 +1051,10 @@ public abstract class AlignmentViewport implements AlignViewportI,
     // hasHiddenColumns = colSel.hasHiddenColumns();
   }
 
-  protected boolean hasHiddenRows = false;
-
   @Override
   public boolean hasHiddenRows()
   {
-    return hasHiddenRows;
+    return alignment.getHiddenSequences().getSize() > 0;
   }
 
   protected SequenceGroup selectionGroup;
@@ -1196,6 +1185,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
@@ -1290,7 +1293,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
         setSequenceAnnotationsVisible(seq, true);
       }
 
-      hasHiddenRows = false;
       hiddenRepSequences = null;
 
       firePropertyChange("alignment", null, alignment.getSequences());
@@ -1303,8 +1305,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void showSequence(int index)
   {
     List<SequenceI> tmp = alignment.getHiddenSequences().showSequence(
-            index,
-            hiddenRepSequences);
+            index, hiddenRepSequences);
     if (tmp.size() > 0)
     {
       if (selectionGroup == null)
@@ -1318,12 +1319,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
         selectionGroup.addSequence(seq, false);
         setSequenceAnnotationsVisible(seq, true);
       }
-      // JBPNote: refactor: only update flag if we modified visiblity (used to
-      // do this regardless)
-      if (alignment.getHiddenSequences().getSize() < 1)
-      {
-        hasHiddenRows = false;
-      }
       firePropertyChange("alignment", null, alignment.getSequences());
       sendSelection();
     }
@@ -1352,7 +1347,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
         alignment.getHiddenSequences().hideSequence(seq[i]);
         setSequenceAnnotationsVisible(seq[i], false);
       }
-      hasHiddenRows = true;
       firePropertyChange("alignment", null, alignment.getSequences());
     }
   }
@@ -1412,8 +1406,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   public boolean isHiddenRepSequence(SequenceI seq)
   {
-    return alignment.getSeqrep()==seq || (hiddenRepSequences != null
-            && hiddenRepSequences.containsKey(seq));
+    return alignment.getSeqrep() == seq
+            || (hiddenRepSequences != null && hiddenRepSequences
+                    .containsKey(seq));
   }
 
   public SequenceGroup getRepresentedSequences(SequenceI seq)
@@ -1435,7 +1430,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
     colSel.invertColumnSelection(0, alignment.getWidth());
   }
 
-
   @Override
   public SequenceI[] getSelectionAsNewSequence()
   {
@@ -1463,7 +1457,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return sequences;
   }
 
-
   @Override
   public SequenceI[] getSequenceSelection()
   {
@@ -1479,16 +1472,13 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return sequences;
   }
 
-
   @Override
-  public CigarArray getViewAsCigars(
-          boolean selectedRegionOnly)
+  public CigarArray getViewAsCigars(boolean selectedRegionOnly)
   {
     return new CigarArray(alignment, colSel,
             (selectedRegionOnly ? selectionGroup : null));
   }
 
-
   @Override
   public jalview.datamodel.AlignmentView getAlignmentView(
           boolean selectedOnly)
@@ -1496,7 +1486,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return getAlignmentView(selectedOnly, false);
   }
 
-
   @Override
   public jalview.datamodel.AlignmentView getAlignmentView(
           boolean selectedOnly, boolean markGroups)
@@ -1506,7 +1495,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
             markGroups);
   }
 
-
   @Override
   public String[] getViewAsString(boolean selectedRegionOnly)
   {
@@ -1544,7 +1532,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return selection;
   }
 
-
   @Override
   public List<int[]> getVisibleRegionBoundaries(int min, int max)
   {
@@ -1572,8 +1559,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
         }
       }
 
-      regions.add(new int[]
-      { start, end });
+      regions.add(new int[] { start, end });
 
       if (colSel != null && colSel.hasHiddenColumns())
       {
@@ -1588,19 +1574,23 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
-  public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(boolean selectedOnly)
+  public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(
+          boolean selectedOnly)
   {
     ArrayList<AlignmentAnnotation> ala = new ArrayList<AlignmentAnnotation>();
     AlignmentAnnotation[] aa;
-    if ((aa=alignment.getAlignmentAnnotation())!=null)
+    if ((aa = alignment.getAlignmentAnnotation()) != null)
     {
-      for (AlignmentAnnotation annot:aa)
+      for (AlignmentAnnotation annot : aa)
       {
         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
-        if (selectedOnly && selectionGroup!=null)
+        if (selectedOnly && selectionGroup != null)
+        {
+          colSel.makeVisibleAnnotation(selectionGroup.getStartRes(),
+                  selectionGroup.getEndRes(), clone);
+        }
+        else
         {
-          colSel.makeVisibleAnnotation(selectionGroup.getStartRes(), selectionGroup.getEndRes(),clone);
-        } else {
           colSel.makeVisibleAnnotation(clone);
         }
         ala.add(clone);
@@ -1609,14 +1599,12 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return ala;
   }
 
-
   @Override
   public boolean isPadGaps()
   {
     return padGaps;
   }
 
-
   @Override
   public void setPadGaps(boolean padGaps)
   {
@@ -1944,6 +1932,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     }
     oldrfs.clear();
   }
+
   @Override
   public boolean isDisplayReferenceSeq()
   {
@@ -2052,7 +2041,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public boolean areFeaturesDisplayed()
   {
-    return featuresDisplayed != null && featuresDisplayed.getRegisterdFeaturesCount()>0;
+    return featuresDisplayed != null
+            && featuresDisplayed.getRegisterdFeaturesCount() > 0;
   }
 
   /**
@@ -2066,6 +2056,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   {
     viewStyle.setShowSequenceFeatures(b);
   }
+
   @Override
   public boolean isShowSequenceFeatures()
   {
@@ -2075,7 +2066,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public void setShowSequenceFeaturesHeight(boolean selected)
   {
-    viewStyle.setShowSeqFeaturesHeight(selected);
+    viewStyle.setShowSequenceFeaturesHeight(selected);
   }
 
   @Override
@@ -2084,8 +2075,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return viewStyle.isShowSequenceFeaturesHeight();
   }
 
-
-
   @Override
   public void setShowAnnotation(boolean b)
   {
@@ -2175,14 +2164,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
   {
     return viewStyle.isShowColourText();
   }
-  /**
-   * @return
-   * @see jalview.api.ViewStyleI#isShowSeqFeaturesHeight()
-   */
-  public boolean isShowSeqFeaturesHeight()
-  {
-    return viewStyle.isShowSeqFeaturesHeight();
-  }
 
   /**
    * @param conservationColourSelected
@@ -2338,7 +2319,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   protected void broadcastCommand(CommandI command, boolean undo)
   {
-    getStructureSelectionManager().commandPerformed(command, undo, getVamsasSource());
+    getStructureSelectionManager().commandPerformed(command, undo,
+            getVamsasSource());
   }
 
   /**
@@ -2423,4 +2405,156 @@ 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 complement = getCodingComplement();
+    if (complement == null || !complement.isFollowHighlight())
+    {
+      return 0;
+    }
+    boolean iAmProtein = !getAlignment().isNucleotide();
+    AlignmentI proteinAlignment = iAmProtein ? getAlignment() : complement
+            .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;
+
+    /*
+     * locate 'middle' column (true middle if an odd number visible, left of
+     * middle if an even number visible)
+     */
+    int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2;
+    final HiddenSequences hiddenSequences = getAlignment()
+            .getHiddenSequences();
+
+    /*
+     * searching to the bottom of the alignment gives smoother scrolling across
+     * all gapped visible regions
+     */
+    int lastSeq = alignment.getHeight() - 1;
+    for (int seqNo = getStartSeq(); seqNo < lastSeq; 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;
+  }
 }