spike branch updated from latest features/JAL-2446
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
index 2f733ed..5a7a27f 100644 (file)
@@ -35,21 +35,24 @@ import jalview.datamodel.AlignmentView;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.CigarArray;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.HiddenSequences;
+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.renderer.ResidueShader;
+import jalview.renderer.ResidueShaderI;
 import jalview.schemes.ColourSchemeI;
-import jalview.schemes.PIDColourScheme;
 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.util.MessageManager;
 import jalview.viewmodel.styles.ViewStyle;
 import jalview.workers.AlignCalcManager;
 import jalview.workers.ComplementConsensusThread;
@@ -77,6 +80,8 @@ import java.util.Map;
 public abstract class AlignmentViewport implements AlignViewportI,
         CommandListener, VamsasSource
 {
+  final protected ViewportRanges ranges;
+
   protected ViewStyleI viewStyle = new ViewStyle();
 
   /**
@@ -87,9 +92,20 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   FeaturesDisplayedI featuresDisplayed = null;
 
-  protected Deque<CommandI> historyList = new ArrayDeque<CommandI>();
+  protected Deque<CommandI> historyList = new ArrayDeque<>();
+
+  protected Deque<CommandI> redoList = new ArrayDeque<>();
+
+  /**
+   * alignment displayed in the viewport. Please use get/setter
+   */
+  protected AlignmentI alignment;
 
-  protected Deque<CommandI> redoList = new ArrayDeque<CommandI>();
+  public AlignmentViewport(AlignmentI al)
+  {
+    setAlignment(al);
+    ranges = new ViewportRanges(al);
+  }
 
   /**
    * @param name
@@ -389,6 +405,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void setWrapAlignment(boolean state)
   {
     viewStyle.setWrapAlignment(state);
+    ranges.setWrappedMode(state);
   }
 
   /**
@@ -551,10 +568,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     viewStyle.setSeqNameItalics(default1);
   }
 
-  /**
-   * alignment displayed in the viewport. Please use get/setter
-   */
-  protected AlignmentI alignment;
+
 
   @Override
   public AlignmentI getAlignment()
@@ -596,7 +610,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   protected boolean ignoreGapsInConsensusCalculation = false;
 
-  protected ColourSchemeI globalColourScheme = null;
+  protected ResidueShaderI residueShading = new ResidueShader();
 
   @Override
   public void setGlobalColourScheme(ColourSchemeI cs)
@@ -604,74 +618,51 @@ public abstract class AlignmentViewport implements AlignViewportI,
     // TODO: logic refactored from AlignFrame changeColour -
     // 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
+    // put the logic in here
     // - 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;
-    boolean recalc = false;
+
+    /*
+     * only instantiate alignment colouring once, thereafter update it;
+     * this means that any conservation or PID threshold settings
+     * persist when the alignment colour scheme is changed
+     */
+    if (residueShading == null)
+    {
+      residueShading = new ResidueShader(viewStyle);
+    }
+    residueShading.setColourScheme(cs);
+
+    // TODO: do threshold and increment belong in ViewStyle or ResidueShader?
+    // ...problem: groups need these, but do not currently have a ViewStyle
+
     if (cs != null)
     {
-      recalc = getConservationSelected();
-      if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
-              || cs instanceof Blosum62ColourScheme)
-      {
-        recalc = true;
-        cs.setThreshold(viewStyle.getThreshold(),
-                ignoreGapsInConsensusCalculation);
-      }
-      else
+      if (getConservationSelected())
       {
-        cs.setThreshold(0, ignoreGapsInConsensusCalculation);
+        residueShading.setConservation(hconservation);
       }
-      if (recalc)
-      {
-        cs.setConsensus(hconsensus);
-        cs.setConservation(hconservation);
-      }
-      cs.setConservationApplied(getConservationSelected());
-      cs.alignmentChanged(alignment, hiddenRepSequences);
+      residueShading.alignmentChanged(alignment, hiddenRepSequences);
     }
+
+    /*
+     * if 'apply colour to all groups' is selected... do so
+     * (but don't transfer any colour threshold settings to groups)
+     */
     if (getColourAppliesToAllGroups())
     {
       for (SequenceGroup sg : getAlignment().getGroups())
       {
-        if (cs == null)
-        {
-          sg.cs = null;
-          continue;
-        }
-        sg.cs = cs.applyTo(sg, getHiddenRepSequences());
-        sg.setConsPercGaps(ConsPercGaps);
-        if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
-                || cs instanceof Blosum62ColourScheme)
-        {
-          sg.cs.setThreshold(viewStyle.getThreshold(),
-                  isIgnoreGapsConsensus());
-          recalc = true;
-        }
-        else
-        {
-          sg.cs.setThreshold(0, isIgnoreGapsConsensus());
-        }
-
-        if (getConservationSelected())
+        /*
+         * retain any colour thresholds per group while
+         * changing choice of colour scheme (JAL-2386)
+         */
+        sg.setColourScheme(cs);
+        if (cs != null)
         {
-          sg.cs.setConservationApplied(true);
-          recalc = true;
-        }
-        else
-        {
-          sg.cs.setConservation(null);
-          // sg.cs.setThreshold(0, getIgnoreGapsConsensus());
-        }
-        if (recalc)
-        {
-          sg.recalcConservation();
-        }
-        else
-        {
-          sg.cs.alignmentChanged(sg, hiddenRepSequences);
+          sg.getGroupColourScheme()
+                  .alignmentChanged(sg, hiddenRepSequences);
         }
       }
     }
@@ -680,13 +671,22 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public ColourSchemeI getGlobalColourScheme()
   {
-    return globalColourScheme;
+    return residueShading == null ? null : residueShading
+            .getColourScheme();
+  }
+
+  @Override
+  public ResidueShaderI getResidueShading()
+  {
+    return residueShading;
   }
 
   protected AlignmentAnnotation consensus;
 
   protected AlignmentAnnotation complementConsensus;
 
+  protected AlignmentAnnotation gapcounts;
+
   protected AlignmentAnnotation strucConsensus;
 
   protected AlignmentAnnotation conservation;
@@ -700,7 +700,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
@@ -734,7 +734,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
-  public void setSequenceConsensusHash(Hashtable[] hconsensus)
+  public void setSequenceConsensusHash(ProfilesI hconsensus)
   {
     this.hconsensus = hconsensus;
   }
@@ -746,7 +746,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
-  public Hashtable[] getSequenceConsensusHash()
+  public ProfilesI getSequenceConsensusHash()
   {
     return hconsensus;
   }
@@ -789,6 +789,12 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
+  public AlignmentAnnotation getAlignmentGapAnnotation()
+  {
+    return gapcounts;
+  }
+
+  @Override
   public AlignmentAnnotation getComplementConsensusAnnotation()
   {
     return complementConsensus;
@@ -939,7 +945,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
     groupConservation = null;
     hconsensus = null;
     hcomplementConsensus = null;
-    // TODO removed listeners from changeSupport?
+    // colour scheme may hold reference to consensus
+    residueShading = null;
+    // TODO remove listeners from changeSupport?
     changeSupport = null;
     setAlignment(null);
   }
@@ -1086,7 +1094,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   /**
-   * Set the selection group for this window.
+   * Set the selection group for this window. Also sets the current alignment as
+   * the context for the group, if it does not already have one.
    * 
    * @param sg
    *          - group holding references to sequences in this alignment view
@@ -1096,11 +1105,15 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void setSelectionGroup(SequenceGroup sg)
   {
     selectionGroup = sg;
+    if (sg != null && sg.getContext() == null)
+    {
+      sg.setContext(alignment);
+    }
   }
 
-  public void setHiddenColumns(ColumnSelection colsel)
+  public void setHiddenColumns(HiddenColumns hidden)
   {
-    this.colSel = colsel;
+    this.alignment.setHiddenColumns(hidden);
   }
 
   @Override
@@ -1147,7 +1160,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public boolean hasHiddenColumns()
   {
-    return colSel != null && colSel.hasHiddenColumns();
+    return alignment.getHiddenColumns() != null
+            && alignment.getHiddenColumns().hasHiddenColumns();
   }
 
   public void updateHiddenColumns()
@@ -1208,9 +1222,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
     if (ap != null)
     {
       updateConsensus(ap);
-      if (globalColourScheme != null)
+      if (residueShading != null)
       {
-        globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
+        residueShading.setThreshold(residueShading.getThreshold(),
                 ignoreGapsInConsensusCalculation);
       }
     }
@@ -1282,7 +1296,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   protected boolean showConsensus = true;
 
-  private Map<SequenceI, Color> sequenceColours = new HashMap<SequenceI, Color>();
+  protected boolean showOccupancy = true;
+
+  private Map<SequenceI, Color> sequenceColours = new HashMap<>();
 
   protected SequenceAnnotationOrder sortAnnotationsBy = null;
 
@@ -1293,15 +1309,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   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
    * 
@@ -1351,7 +1358,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
       return;
     }
 
-    colSel.hideSelectedColumns();
+    colSel.hideSelectedColumns(alignment);
     setSelectionGroup(null);
     isColSelChanged(true);
   }
@@ -1360,30 +1367,33 @@ public abstract class AlignmentViewport implements AlignViewportI,
   {
     if (start == end)
     {
-      colSel.hideColumns(start);
+      colSel.hideSelectedColumns(start, alignment.getHiddenColumns());
     }
     else
     {
-      colSel.hideColumns(start, end);
+      alignment.getHiddenColumns().hideColumns(start, end);
     }
     isColSelChanged(true);
   }
 
   public void showColumn(int col)
   {
-    colSel.revealHiddenColumns(col);
+    alignment.getHiddenColumns().revealHiddenColumns(col, colSel);
     isColSelChanged(true);
   }
 
   public void showAllHiddenColumns()
   {
-    colSel.revealAllHiddenColumns();
+    alignment.getHiddenColumns().revealAllHiddenColumns(colSel);
     isColSelChanged(true);
   }
 
   // common hide/show seq stuff
   public void showAllHiddenSeqs()
   {
+    int startSeq = ranges.getStartSeq();
+    int endSeq = ranges.getEndSeq();
+
     if (alignment.getHiddenSequences().getSize() > 0)
     {
       if (selectionGroup == null)
@@ -1401,6 +1411,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
       hiddenRepSequences = null;
 
+      ranges.setStartEndSeq(startSeq, endSeq + tmp.size());
+
       firePropertyChange("alignment", null, alignment.getSequences());
       // used to set hasHiddenRows/hiddenRepSequences here, after the property
       // changed event
@@ -1410,6 +1422,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   public void showSequence(int index)
   {
+    int startSeq = ranges.getStartSeq();
+    int endSeq = ranges.getEndSeq();
+
     List<SequenceI> tmp = alignment.getHiddenSequences().showSequence(
             index, hiddenRepSequences);
     if (tmp.size() > 0)
@@ -1425,6 +1440,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
         selectionGroup.addSequence(seq, false);
         setSequenceAnnotationsVisible(seq, true);
       }
+
+      ranges.setStartEndSeq(startSeq, endSeq + tmp.size());
+
       firePropertyChange("alignment", null, alignment.getSequences());
       sendSelection();
     }
@@ -1446,6 +1464,11 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   public void hideSequence(SequenceI[] seq)
   {
+    /*
+     * cache offset to first visible sequence
+     */
+    int startSeq = ranges.getStartSeq();
+
     if (seq != null)
     {
       for (int i = 0; i < seq.length; i++)
@@ -1453,6 +1476,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
         alignment.getHiddenSequences().hideSequence(seq[i]);
         setSequenceAnnotationsVisible(seq[i], false);
       }
+      ranges.setStartSeq(startSeq);
       firePropertyChange("alignment", null, alignment.getSequences());
     }
   }
@@ -1521,7 +1545,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
     if (hiddenRepSequences == null)
     {
-      hiddenRepSequences = new Hashtable<SequenceI, SequenceCollectionI>();
+      hiddenRepSequences = new Hashtable<>();
     }
 
     hiddenRepSequences.put(repSequence, sg);
@@ -1599,7 +1623,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public void invertColumnSelection()
   {
-    colSel.invertColumnSelection(0, alignment.getWidth());
+    colSel.invertColumnSelection(0, alignment.getWidth(), alignment);
   }
 
   @Override
@@ -1647,7 +1671,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public CigarArray getViewAsCigars(boolean selectedRegionOnly)
   {
-    return new CigarArray(alignment, colSel,
+    return new CigarArray(alignment, alignment.getHiddenColumns(),
             (selectedRegionOnly ? selectionGroup : null));
   }
 
@@ -1662,8 +1686,10 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public jalview.datamodel.AlignmentView getAlignmentView(
           boolean selectedOnly, boolean markGroups)
   {
-    return new AlignmentView(alignment, colSel, selectionGroup,
-            colSel != null && colSel.hasHiddenColumns(), selectedOnly,
+    return new AlignmentView(alignment, alignment.getHiddenColumns(),
+            selectionGroup, alignment.getHiddenColumns() != null
+                    && alignment.getHiddenColumns().hasHiddenColumns(),
+            selectedOnly,
             markGroups);
   }
 
@@ -1707,9 +1733,11 @@ public abstract class AlignmentViewport implements AlignViewportI,
     }
 
     selection = new String[iSize];
-    if (colSel != null && colSel.hasHiddenColumns())
+    if (alignment.getHiddenColumns() != null
+            && alignment.getHiddenColumns().hasHiddenColumns())
     {
-      selection = colSel.getVisibleSequenceStrings(start, end, seqs);
+      selection = alignment.getHiddenColumns().getVisibleSequenceStrings(
+              start, end, seqs);
     }
     else
     {
@@ -1725,20 +1753,21 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public List<int[]> getVisibleRegionBoundaries(int min, int max)
   {
-    ArrayList<int[]> regions = new ArrayList<int[]>();
+    ArrayList<int[]> regions = new ArrayList<>();
     int start = min;
     int end = max;
 
     do
     {
-      if (colSel != null && colSel.hasHiddenColumns())
+      HiddenColumns hidden = alignment.getHiddenColumns();
+      if (hidden != null && hidden.hasHiddenColumns())
       {
         if (start == 0)
         {
-          start = colSel.adjustForHiddenColumns(start);
+          start = hidden.adjustForHiddenColumns(start);
         }
 
-        end = colSel.getHiddenBoundaryRight(start);
+        end = hidden.getHiddenBoundaryRight(start);
         if (start == end)
         {
           end = max;
@@ -1751,10 +1780,10 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
       regions.add(new int[] { start, end });
 
-      if (colSel != null && colSel.hasHiddenColumns())
+      if (hidden != null && hidden.hasHiddenColumns())
       {
-        start = colSel.adjustForHiddenColumns(end);
-        start = colSel.getHiddenBoundaryLeft(start) + 1;
+        start = hidden.adjustForHiddenColumns(end);
+        start = hidden.getHiddenBoundaryLeft(start) + 1;
       }
     } while (end < max);
 
@@ -1767,7 +1796,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(
           boolean selectedOnly)
   {
-    ArrayList<AlignmentAnnotation> ala = new ArrayList<AlignmentAnnotation>();
+    ArrayList<AlignmentAnnotation> ala = new ArrayList<>();
     AlignmentAnnotation[] aa;
     if ((aa = alignment.getAlignmentAnnotation()) != null)
     {
@@ -1776,12 +1805,13 @@ public abstract class AlignmentViewport implements AlignViewportI,
         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
         if (selectedOnly && selectionGroup != null)
         {
-          colSel.makeVisibleAnnotation(selectionGroup.getStartRes(),
+          alignment.getHiddenColumns().makeVisibleAnnotation(
+                  selectionGroup.getStartRes(),
                   selectionGroup.getEndRes(), clone);
         }
         else
         {
-          colSel.makeVisibleAnnotation(clone);
+          alignment.getHiddenColumns().makeVisibleAnnotation(clone);
         }
         ala.add(clone);
       }
@@ -1846,7 +1876,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
       selectionGroup.setEndRes(alWidth - 1);
     }
 
-    resetAllColourSchemes();
+    updateAllColourSchemes();
     calculator.restartWorkers();
     // alignment.adjustSequenceAnnotations();
   }
@@ -1854,17 +1884,17 @@ public abstract class AlignmentViewport implements AlignViewportI,
   /**
    * reset scope and do calculations for all applied colourschemes on alignment
    */
-  void resetAllColourSchemes()
+  void updateAllColourSchemes()
   {
-    ColourSchemeI cs = globalColourScheme;
-    if (cs != null)
+    ResidueShaderI rs = residueShading;
+    if (rs != null)
     {
-      cs.alignmentChanged(alignment, hiddenRepSequences);
+      rs.alignmentChanged(alignment, hiddenRepSequences);
 
-      cs.setConsensus(hconsensus);
-      if (cs.conservationApplied())
+      rs.setConsensus(hconsensus);
+      if (rs.conservationApplied())
       {
-        cs.setConservation(Conservation.calculateConservation("All", 3,
+        rs.setConservation(Conservation.calculateConservation("All",
                 alignment.getSequences(), 0, alignment.getWidth(), false,
                 getConsPercGaps(), false));
       }
@@ -1897,19 +1927,21 @@ public abstract class AlignmentViewport implements AlignViewportI,
       {
         initRNAStructure();
       }
-      consensus = new AlignmentAnnotation("Consensus", "PID",
+      consensus = new AlignmentAnnotation("Consensus",
+              MessageManager.getString("label.consensus_descr"),
               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
       initConsensus(consensus);
+      initGapCounts();
 
       initComplementConsensus();
     }
   }
 
   /**
-   * If this is a protein alignment and there are mappings to cDNA, add the cDNA
-   * consensus annotation.
+   * If this is a protein alignment and there are mappings to cDNA, adds the
+   * cDNA consensus annotation and returns true, else returns false.
    */
-  public void initComplementConsensus()
+  public boolean initComplementConsensus()
   {
     if (!alignment.isNucleotide())
     {
@@ -1933,12 +1965,16 @@ public abstract class AlignmentViewport implements AlignViewportI,
         if (doConsensus)
         {
           complementConsensus = new AlignmentAnnotation("cDNA Consensus",
-                  "PID for cDNA", new Annotation[1], 0f, 100f,
+                  MessageManager
+                          .getString("label.complement_consensus_descr"),
+                  new Annotation[1], 0f, 100f,
                   AlignmentAnnotation.BAR_GRAPH);
           initConsensus(complementConsensus);
+          return true;
         }
       }
     }
+    return false;
   }
 
   private void initConsensus(AlignmentAnnotation aa)
@@ -1952,6 +1988,25 @@ public abstract class AlignmentViewport implements AlignViewportI,
     }
   }
 
+  // these should be extracted from the view model - style and settings for
+  // derived annotation
+  private void initGapCounts()
+  {
+    if (showOccupancy)
+    {
+      gapcounts = new AlignmentAnnotation("Occupancy",
+              MessageManager.getString("label.occupancy_descr"),
+              new Annotation[1], 0f,
+              alignment.getHeight(), AlignmentAnnotation.BAR_GRAPH);
+      gapcounts.hasText = true;
+      gapcounts.autoCalculated = true;
+      gapcounts.scaleColLabel = true;
+      gapcounts.graph = AlignmentAnnotation.BAR_GRAPH;
+
+      alignment.addAnnotation(gapcounts);
+    }
+  }
+
   private void initConservation()
   {
     if (showConservation)
@@ -1959,8 +2014,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
       if (conservation == null)
       {
         conservation = new AlignmentAnnotation("Conservation",
-                "Conservation of total alignment less than "
-                        + getConsPercGaps() + "% gaps", new Annotation[1],
+                MessageManager.formatMessage("label.conservation_descr",
+                        getConsPercGaps()), new Annotation[1],
                 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
         conservation.hasText = true;
         conservation.autoCalculated = true;
@@ -1976,7 +2031,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
       if (quality == null)
       {
         quality = new AlignmentAnnotation("Quality",
-                "Alignment Quality based on Blosum62 scores",
+                MessageManager.getString("label.quality_descr"),
                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
         quality.hasText = true;
         quality.autoCalculated = true;
@@ -1989,7 +2044,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
   {
     if (alignment.hasRNAStructure() && strucConsensus == null)
     {
-      strucConsensus = new AlignmentAnnotation("StrucConsensus", "PID",
+      strucConsensus = new AlignmentAnnotation("StrucConsensus",
+              MessageManager.getString("label.strucconsensus_descr"),
               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
       strucConsensus.hasText = true;
       strucConsensus.autoCalculated = true;
@@ -2094,7 +2150,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     // intersect alignment annotation with alignment groups
 
     AlignmentAnnotation[] aan = alignment.getAlignmentAnnotation();
-    List<SequenceGroup> oldrfs = new ArrayList<SequenceGroup>();
+    List<SequenceGroup> oldrfs = new ArrayList<>();
     if (aan != null)
     {
       for (int an = 0; an < aan.length; an++)
@@ -2438,6 +2494,11 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void setViewStyle(ViewStyleI settingsForView)
   {
     viewStyle = new ViewStyle(settingsForView);
+    if (residueShading != null)
+    {
+      residueShading.setConservationApplied(settingsForView
+              .isConservationColourSelected());
+    }
   }
 
   @Override
@@ -2631,6 +2692,18 @@ public abstract class AlignmentViewport implements AlignViewportI,
     viewStyle.setScaleProteinAsCdna(b);
   }
 
+  @Override
+  public boolean isProteinFontAsCdna()
+  {
+    return viewStyle.isProteinFontAsCdna();
+  }
+
+  @Override
+  public void setProteinFontAsCdna(boolean b)
+  {
+    viewStyle.setProteinFontAsCdna(b);
+  }
+
   /**
    * @return true if view should scroll to show the highlighted region of a
    *         sequence
@@ -2648,63 +2721,10 @@ public abstract class AlignmentViewport implements AlignViewportI,
     this.followHighlight = b;
   }
 
-  public int getStartRes()
-  {
-    return startRes;
-  }
-
   @Override
-  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()
+  public ViewportRanges getRanges()
   {
-    return endSeq;
+    return ranges;
   }
 
   /**
@@ -2744,7 +2764,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
      * locate 'middle' column (true middle if an odd number visible, left of
      * middle if an even number visible)
      */
-    int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2;
+    int middleColumn = ranges.getStartRes()
+            + (ranges.getEndRes() - ranges.getStartRes()) / 2;
     final HiddenSequences hiddenSequences = getAlignment()
             .getHiddenSequences();
 
@@ -2754,7 +2775,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
      */
     int lastSeq = alignment.getHeight() - 1;
     List<AlignedCodonFrame> seqMappings = null;
-    for (int seqNo = getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++)
+    for (int seqNo = ranges.getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++)
     {
       sequence = getAlignment().getSequenceAt(seqNo);
       if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
@@ -2842,7 +2863,70 @@ public abstract class AlignmentViewport implements AlignViewportI,
         selectionIsDefinedGroup = gps.contains(selectionGroup);
       }
     }
-    return selectionGroup.getContext() == alignment
-            || selectionIsDefinedGroup;
+    return selectionGroup.isDefined() || 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;
+  }
+
+  /**
+   * get the consensus sequence as displayed under the PID consensus annotation
+   * row.
+   * 
+   * @return consensus sequence as a new sequence object
+   */
+  public SequenceI getConsensusSeq()
+  {
+    if (consensus == null)
+    {
+      updateConsensus(null);
+    }
+    if (consensus == null)
+    {
+      return null;
+    }
+    StringBuffer seqs = new StringBuffer();
+    for (int i = 0; i < consensus.annotations.length; i++)
+    {
+      Annotation annotation = consensus.annotations[i];
+      if (annotation != null)
+      {
+        String description = annotation.description;
+        if (description != null && description.startsWith("["))
+        {
+          // consensus is a tie - just pick the first one
+          seqs.append(description.charAt(1));
+        }
+        else
+        {
+          seqs.append(annotation.displayCharacter);
+        }
+      }
+    }
+  
+    SequenceI sq = new Sequence("Consensus", seqs.toString());
+    sq.setDescription("Percentage Identity Consensus "
+            + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
+    return sq;
   }
 }