JAL-2089 patch broken merge to master for Release 2.10.0b1
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
index 84c2c08..c1c88c1 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
- * Copyright (C) 2014 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  */
 package jalview.viewmodel;
 
+import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.Conservation;
 import jalview.api.AlignCalcManagerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeaturesDisplayedI;
+import jalview.api.ViewStyleI;
+import jalview.commands.CommandI;
+import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 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;
@@ -37,18 +44,28 @@ 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;
+import jalview.workers.ComplementConsensusThread;
 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;
+import java.util.Deque;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
-import java.util.Vector;
 
 /**
  * base class holding visualization and analysis attributes and common logic for
@@ -57,681 +74,1135 @@ import java.util.Vector;
  * @author jimp
  * 
  */
-public abstract class AlignmentViewport implements AlignViewportI
+public abstract class AlignmentViewport implements AlignViewportI,
+        CommandListener, VamsasSource
 {
-  /**
-   * alignment displayed in the viewport. Please use get/setter
-   */
-  protected AlignmentI alignment;
-
-  protected String sequenceSetID;
+  protected ViewStyleI viewStyle = new ViewStyle();
 
   /**
-   * probably unused indicator that view is of a dataset rather than an
-   * alignment
+   * A viewport that hosts the cDna view of this (protein), or vice versa (if
+   * set).
    */
-  protected boolean isDataset = false;
-
-  private Map<SequenceI, SequenceCollectionI> hiddenRepSequences;
-
-  protected ColumnSelection colSel = new ColumnSelection();
-
-  public boolean autoCalculateConsensus = true;
+  AlignViewportI codingComplement = null;
 
-  protected boolean autoCalculateStrucConsensus = true;
+  FeaturesDisplayedI featuresDisplayed = null;
 
-  protected boolean ignoreGapsInConsensusCalculation = false;
+  protected Deque<CommandI> historyList = new ArrayDeque<CommandI>();
 
-  protected ColourSchemeI globalColourScheme = null;
+  protected Deque<CommandI> redoList = new ArrayDeque<CommandI>();
 
   /**
-   * gui state - changes to colour scheme propagated to all groups
+   * @param name
+   * @see jalview.api.ViewStyleI#setFontName(java.lang.String)
    */
-  private boolean colourAppliesToAllGroups;
+  @Override
+  public void setFontName(String name)
+  {
+    viewStyle.setFontName(name);
+  }
 
   /**
-   * @param value
-   *          indicating if subsequent colourscheme changes will be propagated
-   *          to all groups
+   * @param style
+   * @see jalview.api.ViewStyleI#setFontStyle(int)
    */
-  public void setColourAppliesToAllGroups(boolean b)
+  @Override
+  public void setFontStyle(int style)
   {
-    colourAppliesToAllGroups = b;
+    viewStyle.setFontStyle(style);
   }
 
   /**
-   * 
-   * 
-   * @return flag indicating if colourchanges propagated to all groups
+   * @param size
+   * @see jalview.api.ViewStyleI#setFontSize(int)
    */
-  public boolean getColourAppliesToAllGroups()
+  @Override
+  public void setFontSize(int size)
   {
-    return colourAppliesToAllGroups;
+    viewStyle.setFontSize(size);
   }
 
-  boolean abovePIDThreshold = false;
-
   /**
-   * GUI state
-   * 
-   * @return true if percent identity threshold is applied to shading
+   * @return
+   * @see jalview.api.ViewStyleI#getFontStyle()
    */
-  public boolean getAbovePIDThreshold()
+  @Override
+  public int getFontStyle()
   {
-    return abovePIDThreshold;
+    return viewStyle.getFontStyle();
   }
 
   /**
-   * GUI state
-   * 
-   * 
-   * @param b
-   *          indicate if percent identity threshold is applied to shading
+   * @return
+   * @see jalview.api.ViewStyleI#getFontName()
    */
-  public void setAbovePIDThreshold(boolean b)
+  @Override
+  public String getFontName()
   {
-    abovePIDThreshold = b;
+    return viewStyle.getFontName();
   }
 
-  int threshold;
-
   /**
-   * DOCUMENT ME!
-   * 
-   * @param thresh
-   *          DOCUMENT ME!
+   * @return
+   * @see jalview.api.ViewStyleI#getFontSize()
    */
-  public void setThreshold(int thresh)
+  @Override
+  public int getFontSize()
   {
-    threshold = thresh;
+    return viewStyle.getFontSize();
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
+   * @param upperCasebold
+   * @see jalview.api.ViewStyleI#setUpperCasebold(boolean)
    */
-  public int getThreshold()
+  @Override
+  public void setUpperCasebold(boolean upperCasebold)
   {
-    return threshold;
+    viewStyle.setUpperCasebold(upperCasebold);
   }
 
-  int increment;
-
   /**
-   * 
-   * @param inc
-   *          set the scalar for bleaching colourschemes according to degree of
-   *          conservation
+   * @return
+   * @see jalview.api.ViewStyleI#isUpperCasebold()
    */
-  public void setIncrement(int inc)
+  @Override
+  public boolean isUpperCasebold()
   {
-    increment = inc;
+    return viewStyle.isUpperCasebold();
   }
 
   /**
-   * GUI State
-   * 
-   * @return get scalar for bleaching colourschemes by conservation
+   * @return
+   * @see jalview.api.ViewStyleI#isSeqNameItalics()
    */
-  public int getIncrement()
+  @Override
+  public boolean isSeqNameItalics()
   {
-    return increment;
+    return viewStyle.isSeqNameItalics();
   }
 
-  boolean conservationColourSelected = false;
-
   /**
-   * GUI state
-   * 
-   * @return true if conservation based shading is enabled
+   * @param colourByReferenceSeq
+   * @see jalview.api.ViewStyleI#setColourByReferenceSeq(boolean)
    */
-  public boolean getConservationSelected()
+  @Override
+  public void setColourByReferenceSeq(boolean colourByReferenceSeq)
   {
-    return conservationColourSelected;
+    viewStyle.setColourByReferenceSeq(colourByReferenceSeq);
   }
 
   /**
-   * GUI state
-   * 
    * @param b
-   *          enable conservation based shading
+   * @see jalview.api.ViewStyleI#setColourAppliesToAllGroups(boolean)
    */
-  public void setConservationSelected(boolean b)
+  @Override
+  public void setColourAppliesToAllGroups(boolean b)
   {
-    conservationColourSelected = b;
+    viewStyle.setColourAppliesToAllGroups(b);
   }
 
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getColourAppliesToAllGroups()
+   */
   @Override
-  public void setGlobalColourScheme(ColourSchemeI cs)
+  public boolean getColourAppliesToAllGroups()
   {
-    // TODO: logic refactored from AlignFrame changeColour -
-    // 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
-    // calculation till later or to do all calculations in thread.
-    // via changecolour
-    globalColourScheme = cs;
-    boolean recalc = false;
-    if (cs != null)
-    {
-      cs.setConservationApplied(recalc = getConservationSelected());
-      if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
-              || cs instanceof Blosum62ColourScheme)
-      {
-        recalc = true;
-        cs.setThreshold(threshold, ignoreGapsInConsensusCalculation);
-      }
-      else
-      {
-        cs.setThreshold(0, ignoreGapsInConsensusCalculation);
-      }
-      if (recalc)
-      {
-        cs.setConsensus(hconsensus);
-        cs.setConservation(hconservation);
-      }
-      cs.alignmentChanged(alignment, hiddenRepSequences);
-    }
-    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(threshold, getIgnoreGapsConsensus());
-          recalc = true;
-        }
-        else
-        {
-          sg.cs.setThreshold(0, getIgnoreGapsConsensus());
-        }
-
-        if (getConservationSelected())
-        {
-          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);
-        }
-      }
-    }
-
+    return viewStyle.getColourAppliesToAllGroups();
   }
 
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getAbovePIDThreshold()
+   */
   @Override
-  public ColourSchemeI getGlobalColourScheme()
+  public boolean getAbovePIDThreshold()
   {
-    return globalColourScheme;
+    return viewStyle.getAbovePIDThreshold();
   }
 
-  protected AlignmentAnnotation consensus;
-
-  protected AlignmentAnnotation strucConsensus;
-
-  protected AlignmentAnnotation conservation;
-
-  protected AlignmentAnnotation quality;
-
-  protected AlignmentAnnotation[] groupConsensus;
-
-  protected AlignmentAnnotation[] groupConservation;
-
   /**
-   * results of alignment consensus analysis for visible portion of view
+   * @param inc
+   * @see jalview.api.ViewStyleI#setIncrement(int)
    */
-  protected Hashtable[] hconsensus = null;
+  @Override
+  public void setIncrement(int inc)
+  {
+    viewStyle.setIncrement(inc);
+  }
 
   /**
-   * results of secondary structure base pair consensus for visible portion of
-   * view
+   * @return
+   * @see jalview.api.ViewStyleI#getIncrement()
    */
-  protected Hashtable[] hStrucConsensus = null;
-
-  protected Conservation hconservation = null;
-
   @Override
-  public void setConservation(Conservation cons)
+  public int getIncrement()
   {
-    hconservation = cons;
+    return viewStyle.getIncrement();
   }
 
   /**
-   * percentage gaps allowed in a column before all amino acid properties should
-   * be considered unconserved
+   * @param b
+   * @see jalview.api.ViewStyleI#setConservationSelected(boolean)
    */
-  int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
-
   @Override
-  public int getConsPercGaps()
+  public void setConservationSelected(boolean b)
   {
-    return ConsPercGaps;
+    viewStyle.setConservationSelected(b);
   }
 
+  /**
+   * @param show
+   * @see jalview.api.ViewStyleI#setShowHiddenMarkers(boolean)
+   */
   @Override
-  public void setSequenceConsensusHash(Hashtable[] hconsensus)
+  public void setShowHiddenMarkers(boolean show)
   {
-    this.hconsensus = hconsensus;
-
+    viewStyle.setShowHiddenMarkers(show);
   }
 
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getShowHiddenMarkers()
+   */
   @Override
-  public Hashtable[] getSequenceConsensusHash()
+  public boolean getShowHiddenMarkers()
   {
-    return hconsensus;
+    return viewStyle.getShowHiddenMarkers();
   }
 
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setScaleRightWrapped(boolean)
+   */
   @Override
-  public Hashtable[] getRnaStructureConsensusHash()
+  public void setScaleRightWrapped(boolean b)
   {
-    return hStrucConsensus;
+    viewStyle.setScaleRightWrapped(b);
   }
 
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setScaleLeftWrapped(boolean)
+   */
   @Override
-  public void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus)
+  public void setScaleLeftWrapped(boolean b)
   {
-    this.hStrucConsensus = hStrucConsensus;
-
+    viewStyle.setScaleLeftWrapped(b);
   }
 
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setScaleAboveWrapped(boolean)
+   */
   @Override
-  public AlignmentAnnotation getAlignmentQualityAnnot()
+  public void setScaleAboveWrapped(boolean b)
   {
-    return quality;
+    viewStyle.setScaleAboveWrapped(b);
   }
 
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getScaleLeftWrapped()
+   */
   @Override
-  public AlignmentAnnotation getAlignmentConservationAnnotation()
+  public boolean getScaleLeftWrapped()
   {
-    return conservation;
+    return viewStyle.getScaleLeftWrapped();
   }
 
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getScaleAboveWrapped()
+   */
   @Override
-  public AlignmentAnnotation getAlignmentConsensusAnnotation()
+  public boolean getScaleAboveWrapped()
   {
-    return consensus;
+    return viewStyle.getScaleAboveWrapped();
   }
 
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getScaleRightWrapped()
+   */
   @Override
-  public AlignmentAnnotation getAlignmentStrucConsensusAnnotation()
+  public boolean getScaleRightWrapped()
   {
-    return strucConsensus;
+    return viewStyle.getScaleRightWrapped();
   }
 
-  protected AlignCalcManagerI calculator = new AlignCalcManager();
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setAbovePIDThreshold(boolean)
+   */
+  @Override
+  public void setAbovePIDThreshold(boolean b)
+  {
+    viewStyle.setAbovePIDThreshold(b);
+  }
 
   /**
-   * trigger update of conservation annotation
+   * @param thresh
+   * @see jalview.api.ViewStyleI#setThreshold(int)
    */
-  public void updateConservation(final AlignmentViewPanel ap)
+  @Override
+  public void setThreshold(int thresh)
   {
-    // see note in mantis : issue number 8585
-    if (alignment.isNucleotide() || conservation == null
-            || !autoCalculateConsensus)
-    {
-      return;
-    }
-    if (calculator
-            .getRegisteredWorkersOfClass(jalview.workers.ConservationThread.class) == null)
-    {
-      calculator.registerWorker(new jalview.workers.ConservationThread(
-              this, ap));
-    }
+    viewStyle.setThreshold(thresh);
   }
 
   /**
-   * trigger update of consensus annotation
+   * @return
+   * @see jalview.api.ViewStyleI#getThreshold()
    */
-  public void updateConsensus(final AlignmentViewPanel ap)
+  @Override
+  public int getThreshold()
   {
-    // see note in mantis : issue number 8585
-    if (consensus == null || !autoCalculateConsensus)
-    {
-      return;
-    }
-    if (calculator.getRegisteredWorkersOfClass(ConsensusThread.class) == null)
-    {
-      calculator.registerWorker(new ConsensusThread(this, ap));
-    }
-  }
-
-  // --------START Structure Conservation
-  public void updateStrucConsensus(final AlignmentViewPanel ap)
-  {
-    if (autoCalculateStrucConsensus && strucConsensus == null
-            && alignment.isNucleotide() && alignment.hasRNAStructure())
-    {
-      // secondary structure has been added - so init the consensus line
-      initRNAStructure();
-    }
-
-    // see note in mantis : issue number 8585
-    if (strucConsensus == null || !autoCalculateStrucConsensus)
-    {
-      return;
-    }
-    if (calculator.getRegisteredWorkersOfClass(StrucConsensusThread.class) == null)
-    {
-      calculator.registerWorker(new StrucConsensusThread(this, ap));
-    }
-  }
-
-  public boolean isCalcInProgress()
-  {
-    return calculator.isWorking();
+    return viewStyle.getThreshold();
   }
 
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getShowJVSuffix()
+   */
   @Override
-  public boolean isCalculationInProgress(
-          AlignmentAnnotation alignmentAnnotation)
+  public boolean getShowJVSuffix()
   {
-    if (!alignmentAnnotation.autoCalculated)
-    {
-      return false;
-    }
-    if (calculator.workingInvolvedWith(alignmentAnnotation))
-    {
-      // System.err.println("grey out ("+alignmentAnnotation.label+")");
-      return true;
-    }
-    return false;
+    return viewStyle.getShowJVSuffix();
   }
 
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setShowJVSuffix(boolean)
+   */
   @Override
-  public boolean isClosed()
+  public void setShowJVSuffix(boolean b)
   {
-    // TODO: check that this isClosed is only true after panel is closed, not
-    // before it is fully constructed.
-    return alignment == null;
+    viewStyle.setShowJVSuffix(b);
   }
 
+  /**
+   * @param state
+   * @see jalview.api.ViewStyleI#setWrapAlignment(boolean)
+   */
   @Override
-  public AlignCalcManagerI getCalcManager()
+  public void setWrapAlignment(boolean state)
   {
-    return calculator;
+    viewStyle.setWrapAlignment(state);
   }
 
   /**
-   * should conservation rows be shown for groups
-   */
-  protected boolean showGroupConservation = false;
-
-  /**
-   * should consensus rows be shown for groups
-   */
-  protected boolean showGroupConsensus = false;
-
-  /**
-   * should consensus profile be rendered by default
-   */
-  protected boolean showSequenceLogo = false;
-
-  /**
-   * should consensus profile be rendered normalised to row height
-   */
-  protected boolean normaliseSequenceLogo = false;
-
-  /**
-   * should consensus histograms be rendered by default
-   */
-  protected boolean showConsensusHistogram = true;
-
-  /**
-   * @return the showConsensusProfile
+   * @param state
+   * @see jalview.api.ViewStyleI#setShowText(boolean)
    */
   @Override
-  public boolean isShowSequenceLogo()
+  public void setShowText(boolean state)
   {
-    return showSequenceLogo;
+    viewStyle.setShowText(state);
   }
 
   /**
-   * @param showSequenceLogo
-   *          the new value
+   * @param state
+   * @see jalview.api.ViewStyleI#setRenderGaps(boolean)
    */
-  public void setShowSequenceLogo(boolean showSequenceLogo)
+  @Override
+  public void setRenderGaps(boolean state)
   {
-    if (showSequenceLogo != this.showSequenceLogo)
-    {
-      // TODO: decouple settings setting from calculation when refactoring
-      // annotation update method from alignframe to viewport
-      this.showSequenceLogo = showSequenceLogo;
-      calculator.updateAnnotationFor(ConsensusThread.class);
-      calculator.updateAnnotationFor(StrucConsensusThread.class);
-    }
-    this.showSequenceLogo = showSequenceLogo;
+    viewStyle.setRenderGaps(state);
   }
 
   /**
-   * @param showConsensusHistogram
-   *          the showConsensusHistogram to set
+   * @return
+   * @see jalview.api.ViewStyleI#getColourText()
    */
-  public void setShowConsensusHistogram(boolean showConsensusHistogram)
+  @Override
+  public boolean getColourText()
   {
-    this.showConsensusHistogram = showConsensusHistogram;
+    return viewStyle.getColourText();
   }
 
   /**
-   * @return the showGroupConservation
+   * @param state
+   * @see jalview.api.ViewStyleI#setColourText(boolean)
    */
-  public boolean isShowGroupConservation()
+  @Override
+  public void setColourText(boolean state)
   {
-    return showGroupConservation;
+    viewStyle.setColourText(state);
   }
 
   /**
-   * @param showGroupConservation
-   *          the showGroupConservation to set
+   * @return
+   * @see jalview.api.ViewStyleI#getWrapAlignment()
    */
-  public void setShowGroupConservation(boolean showGroupConservation)
+  @Override
+  public boolean getWrapAlignment()
   {
-    this.showGroupConservation = showGroupConservation;
+    return viewStyle.getWrapAlignment();
   }
 
   /**
-   * @return the showGroupConsensus
+   * @return
+   * @see jalview.api.ViewStyleI#getShowText()
    */
-  public boolean isShowGroupConsensus()
+  @Override
+  public boolean getShowText()
   {
-    return showGroupConsensus;
+    return viewStyle.getShowText();
   }
 
   /**
-   * @param showGroupConsensus
-   *          the showGroupConsensus to set
+   * @return
+   * @see jalview.api.ViewStyleI#getWrappedWidth()
    */
-  public void setShowGroupConsensus(boolean showGroupConsensus)
+  @Override
+  public int getWrappedWidth()
   {
-    this.showGroupConsensus = showGroupConsensus;
+    return viewStyle.getWrappedWidth();
   }
 
   /**
-   * 
-   * @return flag to indicate if the consensus histogram should be rendered by
-   *         default
+   * @param w
+   * @see jalview.api.ViewStyleI#setWrappedWidth(int)
    */
   @Override
-  public boolean isShowConsensusHistogram()
+  public void setWrappedWidth(int w)
   {
-    return this.showConsensusHistogram;
+    viewStyle.setWrappedWidth(w);
   }
 
   /**
-   * show non-conserved residues only
-   */
-  protected boolean showUnconserved = false;
-
-  /**
-   * when set, updateAlignment will always ensure sequences are of equal length
-   */
-  private boolean padGaps = false;
-
-  /**
-   * when set, alignment should be reordered according to a newly opened tree
+   * @return
+   * @see jalview.api.ViewStyleI#getCharHeight()
    */
-  public boolean sortByTree = false;
-
-  public boolean getShowUnconserved()
-  {
-    return showUnconserved;
-  }
-
-  public void setShowUnconserved(boolean showunconserved)
+  @Override
+  public int getCharHeight()
   {
-    showUnconserved = showunconserved;
+    return viewStyle.getCharHeight();
   }
 
   /**
-   * @param showNonconserved
-   *          the showUnconserved to set
+   * @param h
+   * @see jalview.api.ViewStyleI#setCharHeight(int)
    */
-  public void setShowunconserved(boolean displayNonconserved)
+  @Override
+  public void setCharHeight(int h)
   {
-    this.showUnconserved = displayNonconserved;
+    viewStyle.setCharHeight(h);
   }
 
   /**
-   * 
-   * 
-   * @return null or the currently selected sequence region
+   * @return
+   * @see jalview.api.ViewStyleI#getCharWidth()
    */
   @Override
-  public SequenceGroup getSelectionGroup()
+  public int getCharWidth()
   {
-    return selectionGroup;
+    return viewStyle.getCharWidth();
   }
 
   /**
-   * Set the selection group for this window.
-   * 
-   * @param sg
-   *          - group holding references to sequences in this alignment view
-   * 
+   * @param w
+   * @see jalview.api.ViewStyleI#setCharWidth(int)
    */
   @Override
-  public void setSelectionGroup(SequenceGroup sg)
+  public void setCharWidth(int w)
   {
-    selectionGroup = sg;
+    viewStyle.setCharWidth(w);
   }
 
-  public void setHiddenColumns(ColumnSelection colsel)
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getShowBoxes()
+   */
+  @Override
+  public boolean getShowBoxes()
   {
-    this.colSel = colsel;
+    return viewStyle.getShowBoxes();
   }
 
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getShowUnconserved()
+   */
   @Override
-  public ColumnSelection getColumnSelection()
+  public boolean getShowUnconserved()
   {
-    return colSel;
+    return viewStyle.getShowUnconserved();
   }
 
+  /**
+   * @param showunconserved
+   * @see jalview.api.ViewStyleI#setShowUnconserved(boolean)
+   */
   @Override
-  public void setColumnSelection(ColumnSelection colSel)
+  public void setShowUnconserved(boolean showunconserved)
   {
-    this.colSel = colSel;
-    if (colSel != null)
-    {
-      updateHiddenColumns();
-    }
+    viewStyle.setShowUnconserved(showunconserved);
   }
 
   /**
-   * 
-   * @return
+   * @param default1
+   * @see jalview.api.ViewStyleI#setSeqNameItalics(boolean)
    */
   @Override
-  public Map<SequenceI, SequenceCollectionI> getHiddenRepSequences()
+  public void setSeqNameItalics(boolean default1)
   {
-    return hiddenRepSequences;
+    viewStyle.setSeqNameItalics(default1);
   }
 
+  /**
+   * alignment displayed in the viewport. Please use get/setter
+   */
+  protected AlignmentI alignment;
+
   @Override
-  public void setHiddenRepSequences(
-          Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
+  public AlignmentI getAlignment()
   {
-    this.hiddenRepSequences = hiddenRepSequences;
+    return alignment;
   }
 
   @Override
-  public boolean hasHiddenColumns()
+  public char getGapCharacter()
   {
-    return colSel != null && colSel.hasHiddenColumns();
+    return alignment.getGapCharacter();
   }
 
-  public void updateHiddenColumns()
-  {
-    // this method doesn't really do anything now. But - it could, since a
-    // column Selection could be in the process of modification
-    // hasHiddenColumns = colSel.hasHiddenColumns();
-  }
+  protected String sequenceSetID;
 
-  protected boolean hasHiddenRows = false;
+  /**
+   * probably unused indicator that view is of a dataset rather than an
+   * alignment
+   */
+  protected boolean isDataset = false;
 
-  @Override
-  public boolean hasHiddenRows()
+  public void setDataset(boolean b)
   {
-    return hasHiddenRows;
+    isDataset = b;
   }
 
-  protected SequenceGroup selectionGroup;
-
-  public void setSequenceSetId(String newid)
+  public boolean isDataset()
   {
-    if (sequenceSetID != null)
-    {
-      System.err
-              .println("Warning - overwriting a sequenceSetId for a viewport!");
-    }
-    sequenceSetID = new String(newid);
+    return isDataset;
   }
 
-  @Override
-  public String getSequenceSetId()
-  {
-    if (sequenceSetID == null)
-    {
-      sequenceSetID = alignment.hashCode() + "";
-    }
+  private Map<SequenceI, SequenceCollectionI> hiddenRepSequences;
 
-    return sequenceSetID;
-  }
+  protected ColumnSelection colSel = new ColumnSelection();
 
-  /**
-   * unique viewId for synchronizing state (e.g. with stored Jalview Project)
-   * 
-   */
-  protected String viewId = null;
+  public boolean autoCalculateConsensus = true;
 
-  public String getViewId()
-  {
-    if (viewId == null)
-    {
-      viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
-    }
-    return viewId;
-  }
+  protected boolean autoCalculateStrucConsensus = true;
 
-  public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
+  protected boolean ignoreGapsInConsensusCalculation = false;
+
+  protected ColourSchemeI globalColourScheme = null;
+
+  @Override
+  public void setGlobalColourScheme(ColourSchemeI cs)
+  {
+    // 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
+    // - 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;
+    if (cs != null)
+    {
+      recalc = getConservationSelected();
+      if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
+              || cs instanceof Blosum62ColourScheme)
+      {
+        recalc = true;
+        cs.setThreshold(viewStyle.getThreshold(),
+                ignoreGapsInConsensusCalculation);
+      }
+      else
+      {
+        cs.setThreshold(0, ignoreGapsInConsensusCalculation);
+      }
+      if (recalc)
+      {
+        cs.setConsensus(hconsensus);
+        cs.setConservation(hconservation);
+      }
+      cs.setConservationApplied(getConservationSelected());
+      cs.alignmentChanged(alignment, hiddenRepSequences);
+    }
+    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())
+        {
+          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);
+        }
+      }
+    }
+  }
+
+  @Override
+  public ColourSchemeI getGlobalColourScheme()
+  {
+    return globalColourScheme;
+  }
+
+  protected AlignmentAnnotation consensus;
+
+  protected AlignmentAnnotation complementConsensus;
+
+  protected AlignmentAnnotation strucConsensus;
+
+  protected AlignmentAnnotation conservation;
+
+  protected AlignmentAnnotation quality;
+
+  protected AlignmentAnnotation[] groupConsensus;
+
+  protected AlignmentAnnotation[] groupConservation;
+
+  /**
+   * results of alignment consensus analysis for visible portion of view
+   */
+  protected Hashtable[] hconsensus = null;
+
+  /**
+   * results of cDNA complement consensus visible portion of view
+   */
+  protected Hashtable[] hcomplementConsensus = null;
+
+  /**
+   * results of secondary structure base pair consensus for visible portion of
+   * view
+   */
+  protected Hashtable[] hStrucConsensus = null;
+
+  protected Conservation hconservation = null;
+
+  @Override
+  public void setConservation(Conservation cons)
+  {
+    hconservation = cons;
+  }
+
+  /**
+   * percentage gaps allowed in a column before all amino acid properties should
+   * be considered unconserved
+   */
+  int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
+
+  @Override
+  public int getConsPercGaps()
+  {
+    return ConsPercGaps;
+  }
+
+  @Override
+  public void setSequenceConsensusHash(Hashtable[] hconsensus)
+  {
+    this.hconsensus = hconsensus;
+  }
+
+  @Override
+  public void setComplementConsensusHash(Hashtable[] hconsensus)
+  {
+    this.hcomplementConsensus = hconsensus;
+  }
+
+  @Override
+  public Hashtable[] getSequenceConsensusHash()
+  {
+    return hconsensus;
+  }
+
+  @Override
+  public Hashtable[] getComplementConsensusHash()
+  {
+    return hcomplementConsensus;
+  }
+
+  @Override
+  public Hashtable[] getRnaStructureConsensusHash()
+  {
+    return hStrucConsensus;
+  }
+
+  @Override
+  public void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus)
+  {
+    this.hStrucConsensus = hStrucConsensus;
+
+  }
+
+  @Override
+  public AlignmentAnnotation getAlignmentQualityAnnot()
+  {
+    return quality;
+  }
+
+  @Override
+  public AlignmentAnnotation getAlignmentConservationAnnotation()
+  {
+    return conservation;
+  }
+
+  @Override
+  public AlignmentAnnotation getAlignmentConsensusAnnotation()
+  {
+    return consensus;
+  }
+
+  @Override
+  public AlignmentAnnotation getComplementConsensusAnnotation()
+  {
+    return complementConsensus;
+  }
+
+  @Override
+  public AlignmentAnnotation getAlignmentStrucConsensusAnnotation()
+  {
+    return strucConsensus;
+  }
+
+  protected AlignCalcManagerI calculator = new AlignCalcManager();
+
+  /**
+   * trigger update of conservation annotation
+   */
+  public void updateConservation(final AlignmentViewPanel ap)
+  {
+    // see note in mantis : issue number 8585
+    if (alignment.isNucleotide()
+            || (conservation == null && quality == null)
+            || !autoCalculateConsensus)
+    {
+      return;
+    }
+    if (calculator
+            .getRegisteredWorkersOfClass(jalview.workers.ConservationThread.class) == null)
+    {
+      calculator.registerWorker(new jalview.workers.ConservationThread(
+              this, ap));
+    }
+  }
+
+  /**
+   * trigger update of consensus annotation
+   */
+  public void updateConsensus(final AlignmentViewPanel ap)
+  {
+    // see note in mantis : issue number 8585
+    if (consensus == null || !autoCalculateConsensus)
+    {
+      return;
+    }
+    if (calculator.getRegisteredWorkersOfClass(ConsensusThread.class) == null)
+    {
+      calculator.registerWorker(new ConsensusThread(this, ap));
+    }
+
+    /*
+     * 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())
+    {
+      /*
+       * 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())
+      {
+        // 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));
+        }
+      }
+    }
+  }
+
+  // --------START Structure Conservation
+  public void updateStrucConsensus(final AlignmentViewPanel ap)
+  {
+    if (autoCalculateStrucConsensus && strucConsensus == null
+            && alignment.isNucleotide() && alignment.hasRNAStructure())
+    {
+      // secondary structure has been added - so init the consensus line
+      initRNAStructure();
+    }
+
+    // see note in mantis : issue number 8585
+    if (strucConsensus == null || !autoCalculateStrucConsensus)
+    {
+      return;
+    }
+    if (calculator.getRegisteredWorkersOfClass(StrucConsensusThread.class) == null)
+    {
+      calculator.registerWorker(new StrucConsensusThread(this, ap));
+    }
+  }
+
+  public boolean isCalcInProgress()
+  {
+    return calculator.isWorking();
+  }
+
+  @Override
+  public boolean isCalculationInProgress(
+          AlignmentAnnotation alignmentAnnotation)
+  {
+    if (!alignmentAnnotation.autoCalculated)
+    {
+      return false;
+    }
+    if (calculator.workingInvolvedWith(alignmentAnnotation))
+    {
+      // System.err.println("grey out ("+alignmentAnnotation.label+")");
+      return true;
+    }
+    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()
+  {
+    // TODO: check that this isClosed is only true after panel is closed, not
+    // before it is fully constructed.
+    return alignment == null;
+  }
+
+  @Override
+  public AlignCalcManagerI getCalcManager()
+  {
+    return calculator;
+  }
+
+  /**
+   * should conservation rows be shown for groups
+   */
+  protected boolean showGroupConservation = false;
+
+  /**
+   * should consensus rows be shown for groups
+   */
+  protected boolean showGroupConsensus = false;
+
+  /**
+   * should consensus profile be rendered by default
+   */
+  protected boolean showSequenceLogo = false;
+
+  /**
+   * should consensus profile be rendered normalised to row height
+   */
+  protected boolean normaliseSequenceLogo = false;
+
+  /**
+   * should consensus histograms be rendered by default
+   */
+  protected boolean showConsensusHistogram = true;
+
+  /**
+   * @return the showConsensusProfile
+   */
+  @Override
+  public boolean isShowSequenceLogo()
+  {
+    return showSequenceLogo;
+  }
+
+  /**
+   * @param showSequenceLogo
+   *          the new value
+   */
+  public void setShowSequenceLogo(boolean showSequenceLogo)
+  {
+    if (showSequenceLogo != this.showSequenceLogo)
+    {
+      // TODO: decouple settings setting from calculation when refactoring
+      // annotation update method from alignframe to viewport
+      this.showSequenceLogo = showSequenceLogo;
+      calculator.updateAnnotationFor(ConsensusThread.class);
+      calculator.updateAnnotationFor(ComplementConsensusThread.class);
+      calculator.updateAnnotationFor(StrucConsensusThread.class);
+    }
+    this.showSequenceLogo = showSequenceLogo;
+  }
+
+  /**
+   * @param showConsensusHistogram
+   *          the showConsensusHistogram to set
+   */
+  public void setShowConsensusHistogram(boolean showConsensusHistogram)
+  {
+    this.showConsensusHistogram = showConsensusHistogram;
+  }
+
+  /**
+   * @return the showGroupConservation
+   */
+  public boolean isShowGroupConservation()
+  {
+    return showGroupConservation;
+  }
+
+  /**
+   * @param showGroupConservation
+   *          the showGroupConservation to set
+   */
+  public void setShowGroupConservation(boolean showGroupConservation)
+  {
+    this.showGroupConservation = showGroupConservation;
+  }
+
+  /**
+   * @return the showGroupConsensus
+   */
+  public boolean isShowGroupConsensus()
+  {
+    return showGroupConsensus;
+  }
+
+  /**
+   * @param showGroupConsensus
+   *          the showGroupConsensus to set
+   */
+  public void setShowGroupConsensus(boolean showGroupConsensus)
+  {
+    this.showGroupConsensus = showGroupConsensus;
+  }
+
+  /**
+   * 
+   * @return flag to indicate if the consensus histogram should be rendered by
+   *         default
+   */
+  @Override
+  public boolean isShowConsensusHistogram()
+  {
+    return this.showConsensusHistogram;
+  }
+
+  /**
+   * when set, updateAlignment will always ensure sequences are of equal length
+   */
+  private boolean padGaps = false;
+
+  /**
+   * when set, alignment should be reordered according to a newly opened tree
+   */
+  public boolean sortByTree = false;
+
+  /**
+   * 
+   * 
+   * @return null or the currently selected sequence region
+   */
+  @Override
+  public SequenceGroup getSelectionGroup()
+  {
+    return selectionGroup;
+  }
+
+  /**
+   * Set the selection group for this window.
+   * 
+   * @param sg
+   *          - group holding references to sequences in this alignment view
+   * 
+   */
+  @Override
+  public void setSelectionGroup(SequenceGroup sg)
+  {
+    selectionGroup = sg;
+  }
+
+  public void setHiddenColumns(ColumnSelection colsel)
+  {
+    this.colSel = colsel;
+  }
+
+  @Override
+  public ColumnSelection getColumnSelection()
+  {
+    return colSel;
+  }
+
+  @Override
+  public void setColumnSelection(ColumnSelection colSel)
+  {
+    this.colSel = colSel;
+    if (colSel != null)
+    {
+      updateHiddenColumns();
+    }
+    isColSelChanged(true);
+  }
+
+  /**
+   * 
+   * @return
+   */
+  @Override
+  public Map<SequenceI, SequenceCollectionI> getHiddenRepSequences()
+  {
+    return hiddenRepSequences;
+  }
+
+  @Override
+  public void setHiddenRepSequences(
+          Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
+  {
+    this.hiddenRepSequences = hiddenRepSequences;
+  }
+
+  @Override
+  public boolean hasSelectedColumns()
+  {
+    ColumnSelection columnSelection = getColumnSelection();
+    return columnSelection != null && columnSelection.hasSelectedColumns();
+  }
+
+  @Override
+  public boolean hasHiddenColumns()
+  {
+    return colSel != null && colSel.hasHiddenColumns();
+  }
+
+  public void updateHiddenColumns()
+  {
+    // this method doesn't really do anything now. But - it could, since a
+    // column Selection could be in the process of modification
+    // hasHiddenColumns = colSel.hasHiddenColumns();
+  }
+
+  @Override
+  public boolean hasHiddenRows()
+  {
+    return alignment.getHiddenSequences().getSize() > 0;
+  }
+
+  protected SequenceGroup selectionGroup;
+
+  public void setSequenceSetId(String newid)
+  {
+    if (sequenceSetID != null)
+    {
+      System.err
+              .println("Warning - overwriting a sequenceSetId for a viewport!");
+    }
+    sequenceSetID = new String(newid);
+  }
+
+  @Override
+  public String getSequenceSetId()
+  {
+    if (sequenceSetID == null)
+    {
+      sequenceSetID = alignment.hashCode() + "";
+    }
+
+    return sequenceSetID;
+  }
+
+  /**
+   * unique viewId for synchronizing state (e.g. with stored Jalview Project)
+   * 
+   */
+  protected String viewId = null;
+
+  @Override
+  public String getViewId()
+  {
+    if (viewId == null)
+    {
+      viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
+    }
+    return viewId;
+  }
+
+  public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
   {
     ignoreGapsInConsensusCalculation = b;
     if (ap != null)
@@ -782,8 +1253,7 @@ public abstract class AlignmentViewport implements AlignViewportI
    */
   public boolean isColSelChanged(boolean b)
   {
-    int hc = (colSel == null || colSel.size() == 0) ? -1 : colSel
-            .hashCode();
+    int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
     if (hc != -1 && hc != colselhash)
     {
       if (b)
@@ -796,15 +1266,14 @@ public abstract class AlignmentViewport implements AlignViewportI
   }
 
   @Override
-  public boolean getIgnoreGapsConsensus()
+  public boolean isIgnoreGapsConsensus()
   {
     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;
@@ -813,7 +1282,25 @@ public abstract class AlignmentViewport implements AlignViewportI
 
   protected boolean showConsensus = true;
 
-  Hashtable sequenceColours;
+  private Map<SequenceI, Color> sequenceColours = new HashMap<SequenceI, Color>();
+
+  protected SequenceAnnotationOrder sortAnnotationsBy = null;
+
+  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
@@ -859,14 +1346,14 @@ public abstract class AlignmentViewport implements AlignViewportI
 
   public void hideSelectedColumns()
   {
-    if (colSel.size() < 1)
+    if (colSel.isEmpty())
     {
       return;
     }
 
     colSel.hideSelectedColumns();
     setSelectionGroup(null);
-
+    isColSelChanged(true);
   }
 
   public void hideColumns(int start, int end)
@@ -879,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
@@ -910,7 +1399,6 @@ public abstract class AlignmentViewport implements AlignViewportI
         setSequenceAnnotationsVisible(seq, true);
       }
 
-      hasHiddenRows = false;
       hiddenRepSequences = null;
 
       firePropertyChange("alignment", null, alignment.getSequences());
@@ -923,8 +1411,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)
@@ -938,12 +1425,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();
     }
@@ -972,12 +1453,44 @@ public abstract class AlignmentViewport implements AlignViewportI
         alignment.getHiddenSequences().hideSequence(seq[i]);
         setSequenceAnnotationsVisible(seq[i], false);
       }
-      hasHiddenRows = true;
       firePropertyChange("alignment", null, alignment.getSequences());
     }
   }
 
   /**
+   * 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
@@ -985,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;
+        }
       }
     }
   }
@@ -1004,7 +1521,7 @@ public abstract class AlignmentViewport implements AlignViewportI
 
     if (hiddenRepSequences == null)
     {
-      hiddenRepSequences = new Hashtable();
+      hiddenRepSequences = new Hashtable<SequenceI, SequenceCollectionI>();
     }
 
     hiddenRepSequences.put(repSequence, sg);
@@ -1030,12 +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
@@ -1050,15 +1597,11 @@ public abstract class AlignmentViewport implements AlignViewportI
   }
 
   @Override
-  public abstract void sendSelection();
-
-  @Override
   public void invertColumnSelection()
   {
     colSel.invertColumnSelection(0, alignment.getWidth());
   }
 
-
   @Override
   public SequenceI[] getSelectionAsNewSequence()
   {
@@ -1074,10 +1617,8 @@ public abstract class AlignmentViewport implements AlignViewportI
       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
       for (int i = 0; i < sequences.length; i++)
       {
-        sequences[i] = new Sequence(sequences[i], annots); // construct new
-        // sequence with
-        // subset of visible
-        // annotation
+        // construct new sequence with subset of visible annotation
+        sequences[i] = new Sequence(sequences[i], annots);
       }
     }
     else
@@ -1088,7 +1629,6 @@ public abstract class AlignmentViewport implements AlignViewportI
     return sequences;
   }
 
-
   @Override
   public SequenceI[] getSequenceSelection()
   {
@@ -1104,16 +1644,13 @@ public abstract class AlignmentViewport implements AlignViewportI
     return sequences;
   }
 
-
   @Override
-  public jalview.datamodel.CigarArray getViewAsCigars(
-          boolean selectedRegionOnly)
+  public CigarArray getViewAsCigars(boolean selectedRegionOnly)
   {
-    return new jalview.datamodel.CigarArray(alignment, colSel,
+    return new CigarArray(alignment, colSel,
             (selectedRegionOnly ? selectionGroup : null));
   }
 
-
   @Override
   public jalview.datamodel.AlignmentView getAlignmentView(
           boolean selectedOnly)
@@ -1121,7 +1658,6 @@ public abstract class AlignmentViewport implements AlignViewportI
     return getAlignmentView(selectedOnly, false);
   }
 
-
   @Override
   public jalview.datamodel.AlignmentView getAlignmentView(
           boolean selectedOnly, boolean markGroups)
@@ -1131,10 +1667,16 @@ public abstract class AlignmentViewport implements AlignViewportI
             markGroups);
   }
 
-
   @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;
@@ -1148,9 +1690,20 @@ public abstract class AlignmentViewport implements AlignViewportI
     }
     else
     {
-      iSize = alignment.getHeight();
-      seqs = alignment.getSequencesArray();
-      end = alignment.getWidth();
+      if (hasHiddenRows() && exportHiddenSeqs)
+      {
+        AlignmentI fullAlignment = alignment.getHiddenSequences()
+                .getFullAlignment();
+        iSize = fullAlignment.getHeight();
+        seqs = fullAlignment.getSequencesArray();
+        end = fullAlignment.getWidth();
+      }
+      else
+      {
+        iSize = alignment.getHeight();
+        seqs = alignment.getSequencesArray();
+        end = alignment.getWidth();
+      }
     }
 
     selection = new String[iSize];
@@ -1169,11 +1722,10 @@ public abstract class AlignmentViewport implements AlignViewportI
     return selection;
   }
 
-
   @Override
-  public int[][] getVisibleRegionBoundaries(int min, int max)
+  public List<int[]> getVisibleRegionBoundaries(int min, int max)
   {
-    Vector regions = new Vector();
+    ArrayList<int[]> regions = new ArrayList<int[]>();
     int start = min;
     int end = max;
 
@@ -1197,8 +1749,7 @@ public abstract class AlignmentViewport implements AlignViewportI
         }
       }
 
-      regions.addElement(new int[]
-      { start, end });
+      regions.add(new int[] { start, end });
 
       if (colSel != null && colSel.hasHiddenColumns())
       {
@@ -1209,26 +1760,27 @@ public abstract class AlignmentViewport implements AlignViewportI
 
     int[][] startEnd = new int[regions.size()][2];
 
-    regions.copyInto(startEnd);
-
-    return startEnd;
-
+    return regions;
   }
 
   @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);
@@ -1237,14 +1789,12 @@ public abstract class AlignmentViewport implements AlignViewportI
     return ala;
   }
 
-
   @Override
   public boolean isPadGaps()
   {
     return padGaps;
   }
 
-
   @Override
   public void setPadGaps(boolean padGaps)
   {
@@ -1314,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));
       }
     }
 
@@ -1347,21 +1897,58 @@ public abstract class AlignmentViewport implements AlignViewportI
       {
         initRNAStructure();
       }
-      initConsensus();
+      consensus = new AlignmentAnnotation("Consensus", "PID",
+              new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
+      initConsensus(consensus);
+
+      initComplementConsensus();
     }
   }
 
-  private void initConsensus()
+  /**
+   * If this is a protein alignment and there are mappings to cDNA, add the cDNA
+   * consensus annotation.
+   */
+  public void initComplementConsensus()
   {
+    if (!alignment.isNucleotide())
+    {
+      final List<AlignedCodonFrame> codonMappings = alignment
+              .getCodonFrames();
+      if (codonMappings != null && !codonMappings.isEmpty())
+      {
+        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);
+        }
+      }
+    }
+  }
 
-    consensus = new AlignmentAnnotation("Consensus", "PID",
-            new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
-    consensus.hasText = true;
-    consensus.autoCalculated = true;
+  private void initConsensus(AlignmentAnnotation aa)
+  {
+    aa.hasText = true;
+    aa.autoCalculated = true;
 
     if (showConsensus)
     {
-      alignment.addAnnotation(consensus);
+      alignment.addAnnotation(aa);
     }
   }
 
@@ -1423,57 +2010,57 @@ public abstract class AlignmentViewport implements AlignViewportI
   public int calcPanelHeight()
   {
     // setHeight of panels
-    AlignmentAnnotation[] aa = getAlignment().getAlignmentAnnotation();
+    AlignmentAnnotation[] anns = getAlignment().getAlignmentAnnotation();
     int height = 0;
     int charHeight = getCharHeight();
-    if (aa != null)
+    if (anns != null)
     {
       BitSet graphgrp = new BitSet();
-      for (int i = 0; i < aa.length; i++)
+      for (AlignmentAnnotation aa : anns)
       {
-        if (aa[i] == null)
+        if (aa == null)
         {
           System.err.println("Null annotation row: ignoring.");
           continue;
         }
-        if (!aa[i].visible)
+        if (!aa.visible)
         {
           continue;
         }
-        if (aa[i].graphGroup > -1)
+        if (aa.graphGroup > -1)
         {
-          if (graphgrp.get(aa[i].graphGroup))
+          if (graphgrp.get(aa.graphGroup))
           {
             continue;
           }
           else
           {
-            graphgrp.set(aa[i].graphGroup);
+            graphgrp.set(aa.graphGroup);
           }
         }
-        aa[i].height = 0;
+        aa.height = 0;
 
-        if (aa[i].hasText)
+        if (aa.hasText)
         {
-          aa[i].height += charHeight;
+          aa.height += charHeight;
         }
 
-        if (aa[i].hasIcons)
+        if (aa.hasIcons)
         {
-          aa[i].height += 16;
+          aa.height += 16;
         }
 
-        if (aa[i].graph > 0)
+        if (aa.graph > 0)
         {
-          aa[i].height += aa[i].graphHeight;
+          aa.height += aa.graphHeight;
         }
 
-        if (aa[i].height == 0)
+        if (aa.height == 0)
         {
-          aa[i].height = 20;
+          aa.height = 20;
         }
 
-        height += aa[i].height;
+        height += aa.height;
       }
     }
     if (height == 0)
@@ -1551,178 +2138,711 @@ public abstract class AlignmentViewport implements AlignViewportI
     }
     oldrfs.clear();
   }
+
+  @Override
+  public boolean isDisplayReferenceSeq()
+  {
+    return alignment.hasSeqrep() && viewStyle.isDisplayReferenceSeq();
+  }
+
+  @Override
+  public void setDisplayReferenceSeq(boolean displayReferenceSeq)
+  {
+    viewStyle.setDisplayReferenceSeq(displayReferenceSeq);
+  }
+
+  @Override
+  public boolean isColourByReferenceSeq()
+  {
+    return alignment.hasSeqrep() && viewStyle.isColourByReferenceSeq();
+  }
+
+  @Override
+  public Color getSequenceColour(SequenceI seq)
+  {
+    Color sqc = sequenceColours.get(seq);
+    return (sqc == null ? Color.white : sqc);
+  }
+
+  @Override
+  public void setSequenceColour(SequenceI seq, Color col)
+  {
+    if (col == null)
+    {
+      sequenceColours.remove(seq);
+    }
+    else
+    {
+      sequenceColours.put(seq, col);
+    }
+  }
+
+  @Override
+  public void updateSequenceIdColours()
+  {
+    for (SequenceGroup sg : alignment.getGroups())
+    {
+      if (sg.idColour != null)
+      {
+        for (SequenceI s : sg.getSequences(getHiddenRepSequences()))
+        {
+          sequenceColours.put(s, sg.idColour);
+        }
+      }
+    }
+  }
+
+  @Override
+  public void clearSequenceColours()
+  {
+    sequenceColours.clear();
+  };
+
+  @Override
+  public AlignViewportI getCodingComplement()
+  {
+    return this.codingComplement;
+  }
+
+  /**
+   * Set this as the (cDna/protein) complement of the given viewport. Also
+   * ensures the reverse relationship is set on the given viewport.
+   */
+  @Override
+  public void setCodingComplement(AlignViewportI av)
+  {
+    if (this == av)
+    {
+      System.err.println("Ignoring recursive setCodingComplement request");
+    }
+    else
+    {
+      this.codingComplement = av;
+      // avoid infinite recursion!
+      if (av.getCodingComplement() != this)
+      {
+        av.setCodingComplement(this);
+      }
+    }
+  }
+
+  @Override
+  public boolean isNucleotide()
+  {
+    return getAlignment() == null ? false : getAlignment().isNucleotide();
+  }
+
+  @Override
+  public FeaturesDisplayedI getFeaturesDisplayed()
+  {
+    return featuresDisplayed;
+  }
+
+  @Override
+  public void setFeaturesDisplayed(FeaturesDisplayedI featuresDisplayedI)
+  {
+    featuresDisplayed = featuresDisplayedI;
+  }
+
+  @Override
+  public boolean areFeaturesDisplayed()
+  {
+    return featuresDisplayed != null
+            && featuresDisplayed.getRegisteredFeaturesCount() > 0;
+  }
+
+  /**
+   * set the flag
+   * 
+   * @param b
+   *          features are displayed if true
+   */
+  @Override
+  public void setShowSequenceFeatures(boolean b)
+  {
+    viewStyle.setShowSequenceFeatures(b);
+  }
+
+  @Override
+  public boolean isShowSequenceFeatures()
+  {
+    return viewStyle.isShowSequenceFeatures();
+  }
+
+  @Override
+  public void setShowSequenceFeaturesHeight(boolean selected)
+  {
+    viewStyle.setShowSequenceFeaturesHeight(selected);
+  }
+
+  @Override
+  public boolean isShowSequenceFeaturesHeight()
+  {
+    return viewStyle.isShowSequenceFeaturesHeight();
+  }
+
+  @Override
+  public void setShowAnnotation(boolean b)
+  {
+    viewStyle.setShowAnnotation(b);
+  }
+
+  @Override
+  public boolean isShowAnnotation()
+  {
+    return viewStyle.isShowAnnotation();
+  }
+
+  @Override
+  public boolean isRightAlignIds()
+  {
+    return viewStyle.isRightAlignIds();
+  }
+
+  @Override
+  public void setRightAlignIds(boolean rightAlignIds)
+  {
+    viewStyle.setRightAlignIds(rightAlignIds);
+  }
+
+  @Override
+  public boolean getConservationSelected()
+  {
+    return viewStyle.getConservationSelected();
+  }
+
+  @Override
+  public void setShowBoxes(boolean state)
+  {
+    viewStyle.setShowBoxes(state);
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getTextColour()
+   */
+  @Override
+  public Color getTextColour()
+  {
+    return viewStyle.getTextColour();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getTextColour2()
+   */
+  @Override
+  public Color getTextColour2()
+  {
+    return viewStyle.getTextColour2();
+  }
+
   /**
-   * show the reference sequence in the alignment view
+   * @return
+   * @see jalview.api.ViewStyleI#getThresholdTextColour()
    */
-  private boolean displayReferenceSeq=false;
+  @Override
+  public int getThresholdTextColour()
+  {
+    return viewStyle.getThresholdTextColour();
+  }
+
   /**
-   * colour according to the reference sequence defined on the alignment
+   * @return
+   * @see jalview.api.ViewStyleI#isConservationColourSelected()
    */
-  private boolean colourByReferenceSeq=false;
+  @Override
+  public boolean isConservationColourSelected()
+  {
+    return viewStyle.isConservationColourSelected();
+  }
 
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isRenderGaps()
+   */
   @Override
-  public boolean isDisplayReferenceSeq()
+  public boolean isRenderGaps()
   {
-    return alignment.hasSeqrep() && displayReferenceSeq;
+    return viewStyle.isRenderGaps();
   }
 
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isShowColourText()
+   */
   @Override
-  public void setDisplayReferenceSeq(boolean displayReferenceSeq)
+  public boolean isShowColourText()
   {
-    this.displayReferenceSeq = displayReferenceSeq;
+    return viewStyle.isShowColourText();
   }
 
-  public boolean isColourByReferenceSeq()
+  /**
+   * @param conservationColourSelected
+   * @see jalview.api.ViewStyleI#setConservationColourSelected(boolean)
+   */
+  @Override
+  public void setConservationColourSelected(
+          boolean conservationColourSelected)
   {
-    return alignment.hasSeqrep() && colourByReferenceSeq;
+    viewStyle.setConservationColourSelected(conservationColourSelected);
   }
 
-  public void setColourByReferenceSeq(boolean colourByReferenceSeq)
+  /**
+   * @param showColourText
+   * @see jalview.api.ViewStyleI#setShowColourText(boolean)
+   */
+  @Override
+  public void setShowColourText(boolean showColourText)
   {
-    this.colourByReferenceSeq = colourByReferenceSeq;
+    viewStyle.setShowColourText(showColourText);
   }
 
+  /**
+   * @param textColour
+   * @see jalview.api.ViewStyleI#setTextColour(java.awt.Color)
+   */
   @Override
-  public Color getSequenceColour(SequenceI seq)
+  public void setTextColour(Color textColour)
   {
-    Color sqc = Color.white;
-    if (sequenceColours != null)
-    {
-      sqc = (Color) sequenceColours.get(seq);
-      if (sqc == null)
-      {
-        sqc = Color.white;
-      }
-    }
-    return sqc;
+    viewStyle.setTextColour(textColour);
   }
 
+  /**
+   * @param thresholdTextColour
+   * @see jalview.api.ViewStyleI#setThresholdTextColour(int)
+   */
   @Override
-  public void setSequenceColour(SequenceI seq, Color col)
+  public void setThresholdTextColour(int thresholdTextColour)
   {
-    if (sequenceColours == null)
-    {
-      sequenceColours = new Hashtable();
-    }
+    viewStyle.setThresholdTextColour(thresholdTextColour);
+  }
 
-    if (col == null)
-    {
-      sequenceColours.remove(seq);
-    }
-    else
-    {
-      sequenceColours.put(seq, col);
-    }
+  /**
+   * @param textColour2
+   * @see jalview.api.ViewStyleI#setTextColour2(java.awt.Color)
+   */
+  @Override
+  public void setTextColour2(Color textColour2)
+  {
+    viewStyle.setTextColour2(textColour2);
   }
 
   @Override
-  public void updateSequenceIdColours()
+  public ViewStyleI getViewStyle()
   {
-    if (sequenceColours == null)
-    {
-      sequenceColours = new Hashtable();
-    }
-    for (SequenceGroup sg : alignment.getGroups())
-    {
-      if (sg.idColour != null)
-      {
-        for (SequenceI s : sg.getSequences(getHiddenRepSequences()))
-        {
-          sequenceColours.put(s, sg.idColour);
-        }
-      }
-    }
+    return new ViewStyle(viewStyle);
   }
 
   @Override
-  public void clearSequenceColours()
+  public void setViewStyle(ViewStyleI settingsForView)
   {
-    sequenceColours = null;
-  };
+    viewStyle = new ViewStyle(settingsForView);
+  }
 
-  FeaturesDisplayedI featuresDisplayed = null;
+  @Override
+  public boolean sameStyle(ViewStyleI them)
+  {
+    return viewStyle.sameStyle(them);
+  }
 
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getIdWidth()
+   */
   @Override
-  public FeaturesDisplayedI getFeaturesDisplayed()
+  public int getIdWidth()
   {
-    return featuresDisplayed;
+    return viewStyle.getIdWidth();
   }
 
+  /**
+   * @param i
+   * @see jalview.api.ViewStyleI#setIdWidth(int)
+   */
   @Override
-  public void setFeaturesDisplayed(FeaturesDisplayedI featuresDisplayedI)
+  public void setIdWidth(int i)
   {
-    featuresDisplayed = featuresDisplayedI;
+    viewStyle.setIdWidth(i);
   }
 
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isCentreColumnLabels()
+   */
   @Override
-  public boolean areFeaturesDisplayed()
+  public boolean isCentreColumnLabels()
   {
-    return featuresDisplayed != null && featuresDisplayed.getRegisterdFeaturesCount()>0;
+    return viewStyle.isCentreColumnLabels();
   }
 
   /**
-   * display setting for showing/hiding sequence features on alignment view
+   * @param centreColumnLabels
+   * @see jalview.api.ViewStyleI#setCentreColumnLabels(boolean)
    */
-  boolean showSequenceFeatures = false;
+  @Override
+  public void setCentreColumnLabels(boolean centreColumnLabels)
+  {
+    viewStyle.setCentreColumnLabels(centreColumnLabels);
+  }
 
   /**
-   * set the flag
-   * 
-   * @param b
-   *          features are displayed if true
+   * @param showdbrefs
+   * @see jalview.api.ViewStyleI#setShowDBRefs(boolean)
    */
   @Override
-  public void setShowSequenceFeatures(boolean b)
+  public void setShowDBRefs(boolean showdbrefs)
   {
-    showSequenceFeatures = b;
+    viewStyle.setShowDBRefs(showdbrefs);
   }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isShowDBRefs()
+   */
   @Override
-  public boolean isShowSequenceFeatures()
+  public boolean isShowDBRefs()
   {
-    return showSequenceFeatures;
+    return viewStyle.isShowDBRefs();
   }
 
-  boolean showSeqFeaturesHeight;
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isShowNPFeats()
+   */
+  @Override
+  public boolean isShowNPFeats()
+  {
+    return viewStyle.isShowNPFeats();
+  }
 
+  /**
+   * @param shownpfeats
+   * @see jalview.api.ViewStyleI#setShowNPFeats(boolean)
+   */
   @Override
-  public void setShowSequenceFeaturesHeight(boolean selected)
+  public void setShowNPFeats(boolean shownpfeats)
+  {
+    viewStyle.setShowNPFeats(shownpfeats);
+  }
+
+  public abstract StructureSelectionManager getStructureSelectionManager();
+
+  /**
+   * Add one command to the command history list.
+   * 
+   * @param command
+   */
+  public void addToHistoryList(CommandI command)
+  {
+    if (this.historyList != null)
+    {
+      this.historyList.push(command);
+      broadcastCommand(command, false);
+    }
+  }
+
+  protected void broadcastCommand(CommandI command, boolean undo)
+  {
+    getStructureSelectionManager().commandPerformed(command, undo,
+            getVamsasSource());
+  }
+
+  /**
+   * Add one command to the command redo list.
+   * 
+   * @param command
+   */
+  public void addToRedoList(CommandI command)
+  {
+    if (this.redoList != null)
+    {
+      this.redoList.push(command);
+    }
+    broadcastCommand(command, true);
+  }
+
+  /**
+   * Clear the command redo list.
+   */
+  public void clearRedoList()
+  {
+    if (this.redoList != null)
+    {
+      this.redoList.clear();
+    }
+  }
+
+  public void setHistoryList(Deque<CommandI> list)
+  {
+    this.historyList = list;
+  }
+
+  public Deque<CommandI> getHistoryList()
+  {
+    return this.historyList;
+  }
+
+  public void setRedoList(Deque<CommandI> list)
+  {
+    this.redoList = list;
+  }
+
+  public Deque<CommandI> getRedoList()
   {
-    showSeqFeaturesHeight = selected;
+    return this.redoList;
   }
 
   @Override
-  public boolean isShowSequenceFeaturesHeight()
+  public VamsasSource getVamsasSource()
+  {
+    return this;
+  }
+
+  public SequenceAnnotationOrder getSortAnnotationsBy()
   {
-    return showSeqFeaturesHeight;
+    return sortAnnotationsBy;
   }
 
-  private boolean showAnnotation = true;
+  public void setSortAnnotationsBy(SequenceAnnotationOrder sortAnnotationsBy)
+  {
+    this.sortAnnotationsBy = sortAnnotationsBy;
+  }
 
-  private boolean rightAlignIds = false;
+  public boolean isShowAutocalculatedAbove()
+  {
+    return showAutocalculatedAbove;
+  }
 
+  public void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
+  {
+    this.showAutocalculatedAbove = showAutocalculatedAbove;
+  }
 
   @Override
-  public void setShowAnnotation(boolean b)
+  public boolean isScaleProteinAsCdna()
   {
-    showAnnotation = b;
+    return viewStyle.isScaleProteinAsCdna();
   }
 
   @Override
-  public boolean isShowAnnotation()
+  public void setScaleProteinAsCdna(boolean b)
   {
-    return showAnnotation;
+    viewStyle.setScaleProteinAsCdna(b);
   }
 
+  /**
+   * @return true if view should scroll to show the highlighted region of a
+   *         sequence
+   * @return
+   */
   @Override
-  public boolean isRightAlignIds()
+  public final boolean isFollowHighlight()
   {
-    return rightAlignIds;
+    return followHighlight;
   }
 
   @Override
-  public void setRightAlignIds(boolean rightAlignIds)
+  public final void setFollowHighlight(boolean b)
+  {
+    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()
+  {
+    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 List<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;
+    List<AlignedCodonFrame> seqMappings = null;
+    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;
+      }
+      seqMappings = MappingUtils
+              .findMappingsForSequenceAndOthers(sequence, mappings,
+                      getCodingComplement().getAlignment().getSequences());
+      if (!seqMappings.isEmpty())
+      {
+        break;
+      }
+    }
+
+    if (sequence == null || seqMappings == null || seqMappings.isEmpty())
+    {
+      /*
+       * No ungapped mapped sequence in middle column - do nothing
+       */
+      return 0;
+    }
+    MappingUtils.addSearchResults(sr, sequence,
+            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)
   {
-    this.rightAlignIds = rightAlignIds;
+    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;
+  }
 }