JAL-1645 Version-Rel Version 2.9 Year-Rel 2015 Licensing glob
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
index 3cac289..a15e9a6 100644 (file)
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
- * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
+ * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
+ * Copyright (C) 2015 The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  * Jalview is free software: you can redistribute it and/or
  * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
- * 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
  * Jalview is distributed in the hope that it will be useful, but 
  * WITHOUT ANY WARRANTY; without even the implied warranty 
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
  * PURPOSE.  See the GNU General Public License for more details.
  * 
- * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 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;
 import jalview.datamodel.SequenceI;
-import jalview.schemes.ClustalxColourScheme;
+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.MappingUtils;
+import jalview.viewmodel.styles.ViewStyle;
 import jalview.workers.AlignCalcManager;
+import jalview.workers.ComplementConsensusThread;
 import jalview.workers.ConsensusThread;
-import jalview.workers.ConservationThread;
 import jalview.workers.StrucConsensusThread;
 
+import java.awt.Color;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Deque;
+import java.util.HashMap;
 import java.util.Hashtable;
-import java.util.Vector;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
- * base class holding visualization and analysis attributes and common logic for an active alignment view displayed in the GUI
+ * base class holding visualization and analysis attributes and common logic for
+ * an active alignment view displayed in the GUI
+ * 
  * @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 Hashtable hiddenRepSequences;
-
-  protected ColumnSelection colSel = new ColumnSelection();
-
-
-  public boolean autoCalculateConsensus = true;
+  AlignViewportI codingComplement = null;
 
-  protected boolean autoCalculateStrucConsensus = true;
-
-  protected boolean ignoreGapsInConsensusCalculation = false;
-  
+  FeaturesDisplayedI featuresDisplayed = null;
 
-  protected ColourSchemeI globalColourScheme = null;
+  protected Deque<CommandI> historyList = new ArrayDeque<CommandI>();
 
+  protected Deque<CommandI> redoList = new ArrayDeque<CommandI>();
 
-  public void setGlobalColourScheme(ColourSchemeI cs)
+  /**
+   * @param name
+   * @see jalview.api.ViewStyleI#setFontName(java.lang.String)
+   */
+  public void setFontName(String name)
   {
-    globalColourScheme = cs;
+    viewStyle.setFontName(name);
   }
 
-  public ColourSchemeI getGlobalColourScheme()
+  /**
+   * @param style
+   * @see jalview.api.ViewStyleI#setFontStyle(int)
+   */
+  public void setFontStyle(int style)
   {
-    return globalColourScheme;
+    viewStyle.setFontStyle(style);
   }
 
-
-  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 size
+   * @see jalview.api.ViewStyleI#setFontSize(int)
    */
-  protected Hashtable[] hconsensus=null;
+  public void setFontSize(int size)
+  {
+    viewStyle.setFontSize(size);
+  }
 
   /**
-   * results of secondary structure base pair consensus for visible portion of view
+   * @return
+   * @see jalview.api.ViewStyleI#getFontStyle()
    */
-  protected Hashtable[] hStrucConsensus=null;
+  public int getFontStyle()
+  {
+    return viewStyle.getFontStyle();
+  }
 
   /**
-   * percentage gaps allowed in a column before all amino acid properties should be considered unconserved
+   * @return
+   * @see jalview.api.ViewStyleI#getFontName()
    */
-  int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
-
-
-  public int getConsPercGaps()
+  public String getFontName()
   {
-    return ConsPercGaps;
+    return viewStyle.getFontName();
   }
-  @Override
-  public void setSequenceConsensusHash(Hashtable[] hconsensus)
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getFontSize()
+   */
+  public int getFontSize()
   {
-    this.hconsensus=hconsensus;
-    
+    return viewStyle.getFontSize();
   }
 
-  @Override
-  public Hashtable[] getSequenceConsensusHash()
+  /**
+   * @param upperCasebold
+   * @see jalview.api.ViewStyleI#setUpperCasebold(boolean)
+   */
+  public void setUpperCasebold(boolean upperCasebold)
   {
-    return hconsensus;
+    viewStyle.setUpperCasebold(upperCasebold);
   }
 
-  @Override
-  public Hashtable[] getRnaStructureConsensusHash()
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isUpperCasebold()
+   */
+  public boolean isUpperCasebold()
   {
-    return hStrucConsensus;
+    return viewStyle.isUpperCasebold();
   }
-  @Override
-  public void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus)
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isSeqNameItalics()
+   */
+  public boolean isSeqNameItalics()
   {
-    this.hStrucConsensus=hStrucConsensus;
-    
+    return viewStyle.isSeqNameItalics();
   }
-  @Override
-  public AlignmentAnnotation getAlignmentQualityAnnot()
+
+  /**
+   * @param colourByReferenceSeq
+   * @see jalview.api.ViewStyleI#setColourByReferenceSeq(boolean)
+   */
+  public void setColourByReferenceSeq(boolean colourByReferenceSeq)
   {
-    return quality;
+    viewStyle.setColourByReferenceSeq(colourByReferenceSeq);
   }
 
-  @Override
-  public AlignmentAnnotation getAlignmentConservationAnnotation()
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setColourAppliesToAllGroups(boolean)
+   */
+  public void setColourAppliesToAllGroups(boolean b)
   {
-    return conservation;
+    viewStyle.setColourAppliesToAllGroups(b);
   }
-  @Override
-  public AlignmentAnnotation getAlignmentConsensusAnnotation()
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getColourAppliesToAllGroups()
+   */
+  public boolean getColourAppliesToAllGroups()
   {
-    return consensus;
+    return viewStyle.getColourAppliesToAllGroups();
   }
-  @Override
-  public AlignmentAnnotation getAlignmentStrucConsensusAnnotation()
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getAbovePIDThreshold()
+   */
+  public boolean getAbovePIDThreshold()
   {
-    return strucConsensus;
+    return viewStyle.getAbovePIDThreshold();
   }
-  
-  protected AlignCalcManagerI calculator=new AlignCalcManager();
-
-  jalview.workers.ConsensusThread consensusThread;
-
-  StrucConsensusThread strucConsensusThread;
-
-
-  private ConservationThread conservationThread;
 
   /**
-   * trigger update of conservation annotation
+   * @param inc
+   * @see jalview.api.ViewStyleI#setIncrement(int)
    */
-  public void updateConservation(final AlignmentViewPanel ap)
+  public void setIncrement(int inc)
   {
-    // see note in mantis : issue number 8585
-    if (alignment.isNucleotide() || conservation == null
-            || !autoCalculateConsensus)
-    {
-      return;
-    }
-    
-    calculator.startWorker(conservationThread=new jalview.workers.ConservationThread(this, ap));
+    viewStyle.setIncrement(inc);
   }
 
   /**
-   * trigger update of consensus annotation
+   * @return
+   * @see jalview.api.ViewStyleI#getIncrement()
    */
-  public void updateConsensus(final AlignmentViewPanel ap)
+  public int getIncrement()
   {
-    // see note in mantis : issue number 8585
-    if (consensus == null || !autoCalculateConsensus)
-    {
-      return;
-    }
-    calculator.startWorker(consensusThread = new ConsensusThread(this, ap));
+    return viewStyle.getIncrement();
   }
 
-  // --------START Structure Conservation
-  public void updateStrucConsensus(final AlignmentViewPanel ap)
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setConservationSelected(boolean)
+   */
+  public void setConservationSelected(boolean b)
   {
-    if (autoCalculateStrucConsensus && strucConsensus==null && alignment.isNucleotide() && alignment.hasRNAStructure())
-    {
-      
-    }
-
-    // see note in mantis : issue number 8585
-    if (strucConsensus == null || !autoCalculateStrucConsensus)
-    {
-      return;
-    }
-    calculator.startWorker(strucConsensusThread = new StrucConsensusThread(this,ap));
+    viewStyle.setConservationSelected(b);
   }
 
-  public boolean isCalcInProgress()
+  /**
+   * @param show
+   * @see jalview.api.ViewStyleI#setShowHiddenMarkers(boolean)
+   */
+  public void setShowHiddenMarkers(boolean show)
   {
-    return calculator.isWorking();
+    viewStyle.setShowHiddenMarkers(show);
   }
 
-  public boolean isCalculationInProgress(
-          AlignmentAnnotation alignmentAnnotation)
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getShowHiddenMarkers()
+   */
+  public boolean getShowHiddenMarkers()
   {
-    if (!alignmentAnnotation.autoCalculated)
-      return false;
-    if ((calculator.isWorking(consensusThread) && consensus==alignmentAnnotation)
-            || (calculator.isWorking(conservationThread) &&  (conservation==alignmentAnnotation || quality==alignmentAnnotation))
-            || (calculator.isWorking(strucConsensusThread) && strucConsensus==alignmentAnnotation)
-            )
-    {
-      return true;
-    }
-    return false;
+    return viewStyle.getShowHiddenMarkers();
   }
-  @Override
-  public boolean isClosed()
+
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setScaleRightWrapped(boolean)
+   */
+  public void setScaleRightWrapped(boolean b)
   {
-    // TODO: check that this isClosed is only true after panel is closed, not before it is fully constructed.
-    return alignment==null;
+    viewStyle.setScaleRightWrapped(b);
   }
 
-  @Override
-  public AlignCalcManagerI getCalcManager()
+  /**
+   * @param b
+   * @see jalview.api.ViewStyleI#setScaleLeftWrapped(boolean)
+   */
+  public void setScaleLeftWrapped(boolean b)
   {
-    return calculator;
+    viewStyle.setScaleLeftWrapped(b);
   }
 
   /**
-   * should conservation rows be shown for groups
+   * @param b
+   * @see jalview.api.ViewStyleI#setScaleAboveWrapped(boolean)
    */
-  protected boolean showGroupConservation = false;
+  public void setScaleAboveWrapped(boolean b)
+  {
+    viewStyle.setScaleAboveWrapped(b);
+  }
 
   /**
-   * should consensus rows be shown for groups
+   * @return
+   * @see jalview.api.ViewStyleI#getScaleLeftWrapped()
    */
-  protected boolean showGroupConsensus = false;
+  public boolean getScaleLeftWrapped()
+  {
+    return viewStyle.getScaleLeftWrapped();
+  }
 
   /**
-   * should consensus profile be rendered by default
+   * @return
+   * @see jalview.api.ViewStyleI#getScaleAboveWrapped()
    */
-  protected boolean showSequenceLogo = false;
+  public boolean getScaleAboveWrapped()
+  {
+    return viewStyle.getScaleAboveWrapped();
+  }
+
   /**
-   * should consensus profile be rendered normalised to row height
+   * @return
+   * @see jalview.api.ViewStyleI#getScaleRightWrapped()
    */
-  protected boolean normaliseSequenceLogo = false;
+  public boolean getScaleRightWrapped()
+  {
+    return viewStyle.getScaleRightWrapped();
+  }
+
   /**
-   * should consensus histograms be rendered by default
+   * @param b
+   * @see jalview.api.ViewStyleI#setAbovePIDThreshold(boolean)
    */
-  protected boolean showConsensusHistogram = true;
+  public void setAbovePIDThreshold(boolean b)
+  {
+    viewStyle.setAbovePIDThreshold(b);
+  }
 
   /**
-   * @return the showConsensusProfile
+   * @param thresh
+   * @see jalview.api.ViewStyleI#setThreshold(int)
    */
-  public boolean isShowSequenceLogo()
+  public void setThreshold(int thresh)
   {
-    return showSequenceLogo;
+    viewStyle.setThreshold(thresh);
   }
 
   /**
-   * @param showSequenceLogo
-   *          the new value
+   * @return
+   * @see jalview.api.ViewStyleI#getThreshold()
    */
-  public void setShowSequenceLogo(boolean showSequenceLogo)
+  public int getThreshold()
   {
-    if (showSequenceLogo != this.showSequenceLogo)
-    {
-      // TODO: decouple settings setting from calculation when refactoring
-      // annotation update method from alignframe to viewport
-      this.showSequenceLogo = showSequenceLogo;
-      if (consensusThread != null)
-      {
-        consensusThread.updateAnnotation();
-      }
-      if (strucConsensusThread != null)
-      {
-        strucConsensusThread.updateAnnotation();
-      }
-    }
-    this.showSequenceLogo = showSequenceLogo;
+    return viewStyle.getThreshold();
   }
 
   /**
-   * @param showConsensusHistogram
-   *          the showConsensusHistogram to set
+   * @return
+   * @see jalview.api.ViewStyleI#getShowJVSuffix()
    */
-  public void setShowConsensusHistogram(boolean showConsensusHistogram)
+  public boolean getShowJVSuffix()
   {
-    this.showConsensusHistogram = showConsensusHistogram;
+    return viewStyle.getShowJVSuffix();
   }
 
   /**
-   * @return the showGroupConservation
+   * @param b
+   * @see jalview.api.ViewStyleI#setShowJVSuffix(boolean)
    */
-  public boolean isShowGroupConservation()
+  public void setShowJVSuffix(boolean b)
   {
-    return showGroupConservation;
+    viewStyle.setShowJVSuffix(b);
   }
 
   /**
-   * @param showGroupConservation
-   *          the showGroupConservation to set
+   * @param state
+   * @see jalview.api.ViewStyleI#setWrapAlignment(boolean)
    */
-  public void setShowGroupConservation(boolean showGroupConservation)
+  public void setWrapAlignment(boolean state)
   {
-    this.showGroupConservation = showGroupConservation;
+    viewStyle.setWrapAlignment(state);
   }
 
   /**
-   * @return the showGroupConsensus
+   * @param state
+   * @see jalview.api.ViewStyleI#setShowText(boolean)
    */
-  public boolean isShowGroupConsensus()
+  public void setShowText(boolean state)
   {
-    return showGroupConsensus;
+    viewStyle.setShowText(state);
   }
 
   /**
-   * @param showGroupConsensus
-   *          the showGroupConsensus to set
+   * @param state
+   * @see jalview.api.ViewStyleI#setRenderGaps(boolean)
    */
-  public void setShowGroupConsensus(boolean showGroupConsensus)
+  public void setRenderGaps(boolean state)
   {
-    this.showGroupConsensus = showGroupConsensus;
+    viewStyle.setRenderGaps(state);
   }
 
   /**
-   * 
-   * @return flag to indicate if the consensus histogram should be rendered by
-   *         default
+   * @return
+   * @see jalview.api.ViewStyleI#getColourText()
    */
-  public boolean isShowConsensusHistogram()
+  public boolean getColourText()
   {
-    return this.showConsensusHistogram;
+    return viewStyle.getColourText();
   }
 
   /**
-   * show non-conserved residues only
+   * @param state
+   * @see jalview.api.ViewStyleI#setColourText(boolean)
    */
-  protected boolean showUnconserved = false;
-
+  public void setColourText(boolean state)
+  {
+    viewStyle.setColourText(state);
+  }
 
   /**
-   * when set, updateAlignment will always ensure sequences are of equal length
+   * @return
+   * @see jalview.api.ViewStyleI#getWrapAlignment()
    */
-  private boolean padGaps = false;
+  public boolean getWrapAlignment()
+  {
+    return viewStyle.getWrapAlignment();
+  }
 
   /**
-   * when set, alignment should be reordered according to a newly opened tree
+   * @return
+   * @see jalview.api.ViewStyleI#getShowText()
    */
-  public boolean sortByTree = false;
-
-  public boolean getShowUnconserved()
+  public boolean getShowText()
   {
-    return showUnconserved;
+    return viewStyle.getShowText();
   }
 
-  public void setShowUnconserved(boolean showunconserved)
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getWrappedWidth()
+   */
+  public int getWrappedWidth()
   {
-    showUnconserved = showunconserved;
+    return viewStyle.getWrappedWidth();
   }
 
   /**
-   * @param showNonconserved
-   *          the showUnconserved to set
+   * @param w
+   * @see jalview.api.ViewStyleI#setWrappedWidth(int)
    */
-  public void setShowunconserved(boolean displayNonconserved)
+  public void setWrappedWidth(int w)
   {
-    this.showUnconserved = displayNonconserved;
+    viewStyle.setWrappedWidth(w);
   }
 
   /**
-   * 
-   * 
-   * @return null or the currently selected sequence region
+   * @return
+   * @see jalview.api.ViewStyleI#getCharHeight()
    */
-  public SequenceGroup getSelectionGroup()
+  public int getCharHeight()
   {
-    return selectionGroup;
+    return viewStyle.getCharHeight();
   }
 
   /**
-   * Set the selection group for this window.
-   * 
-   * @param sg
-   *          - group holding references to sequences in this alignment view
-   * 
+   * @param h
+   * @see jalview.api.ViewStyleI#setCharHeight(int)
    */
-  public void setSelectionGroup(SequenceGroup sg)
+  public void setCharHeight(int h)
   {
-    selectionGroup = sg;
+    viewStyle.setCharHeight(h);
   }
 
-  public void setHiddenColumns(ColumnSelection colsel)
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getCharWidth()
+   */
+  public int getCharWidth()
   {
-    this.colSel = colsel;
-    if (colSel.getHiddenColumns() != null)
-    {
-      hasHiddenColumns = true;
-    }
+    return viewStyle.getCharWidth();
   }
 
-  public ColumnSelection getColumnSelection()
+  /**
+   * @param w
+   * @see jalview.api.ViewStyleI#setCharWidth(int)
+   */
+  public void setCharWidth(int w)
   {
-    return colSel;
+    viewStyle.setCharWidth(w);
   }
-  public void setColumnSelection(ColumnSelection colSel)
-  {
-    this.colSel=colSel;
-  }
-  public Hashtable getHiddenRepSequences()
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getShowBoxes()
+   */
+  public boolean getShowBoxes()
   {
-    return hiddenRepSequences;
+    return viewStyle.getShowBoxes();
   }
-  public void setHiddenRepSequences(Hashtable hiddenRepSequences)
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getShowUnconserved()
+   */
+  public boolean getShowUnconserved()
   {
-    this.hiddenRepSequences = hiddenRepSequences;
+    return viewStyle.getShowUnconserved();
   }
-  protected boolean hasHiddenColumns = false;
 
-  public void updateHiddenColumns()
+  /**
+   * @param showunconserved
+   * @see jalview.api.ViewStyleI#setShowUnconserved(boolean)
+   */
+  public void setShowUnconserved(boolean showunconserved)
   {
-    hasHiddenColumns = colSel.getHiddenColumns() != null;  
+    viewStyle.setShowUnconserved(showunconserved);
   }
-  
-  protected boolean hasHiddenRows = false;
-  
-  public boolean hasHiddenRows() {
-    return hasHiddenRows;
+
+  /**
+   * @param default1
+   * @see jalview.api.ViewStyleI#setSeqNameItalics(boolean)
+   */
+  public void setSeqNameItalics(boolean default1)
+  {
+    viewStyle.setSeqNameItalics(default1);
   }
 
-  protected SequenceGroup selectionGroup;
+  /**
+   * alignment displayed in the viewport. Please use get/setter
+   */
+  protected AlignmentI alignment;
 
-  public void setSequenceSetId(String newid)
+  @Override
+  public AlignmentI getAlignment()
   {
-    if (sequenceSetID!=null)
-    {
-      System.err.println("Warning - overwriting a sequenceSetId for a viewport!");
-    }
-    sequenceSetID=new String(newid);
+    return alignment;
   }
-  public String getSequenceSetId()
-  {
-    if (sequenceSetID == null)
-    {
-      sequenceSetID = alignment.hashCode() + "";
-    }
 
-    return sequenceSetID;
+  @Override
+  public char getGapCharacter()
+  {
+    return alignment.getGapCharacter();
   }
+
+  protected String sequenceSetID;
+
   /**
-   * unique viewId for synchronizing state (e.g. with stored Jalview Project)
-   * 
+   * probably unused indicator that view is of a dataset rather than an
+   * alignment
    */
-  protected String viewId = null;
+  protected boolean isDataset = false;
 
-  public String getViewId()
+  public void setDataset(boolean b)
   {
-    if (viewId == null)
-    {
-      viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
-    }
-    return viewId;
+    isDataset = b;
   }
-  public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
+
+  public boolean isDataset()
   {
-    ignoreGapsInConsensusCalculation = b;
-    if (ap!=null) {updateConsensus(ap);
-    if (globalColourScheme != null)
-    {
-      globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
-              ignoreGapsInConsensusCalculation);
-    }}
-    
+    return isDataset;
   }
-  private long sgrouphash = -1, colselhash = -1;
 
-  /**
-   * checks current SelectionGroup against record of last hash value, and
-   * updates record.
-   * 
-   * @param b
-   *          update the record of last hash value
-   * 
-   * @return true if SelectionGroup changed since last call (when b is true)
-   */
-  public boolean isSelectionGroupChanged(boolean b)
+  private Map<SequenceI, SequenceCollectionI> hiddenRepSequences;
+
+  protected ColumnSelection colSel = new ColumnSelection();
+
+  public boolean autoCalculateConsensus = true;
+
+  protected boolean autoCalculateStrucConsensus = true;
+
+  protected boolean ignoreGapsInConsensusCalculation = false;
+
+  protected ColourSchemeI globalColourScheme = null;
+
+  @Override
+  public void setGlobalColourScheme(ColourSchemeI cs)
   {
-    int hc = (selectionGroup == null || selectionGroup.getSize() == 0) ? -1
-            : selectionGroup.hashCode();
-    if (hc != -1 && hc != sgrouphash)
+    // 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)
     {
-      if (b)
+      cs.setConservationApplied(recalc = getConservationSelected());
+      if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
+              || cs instanceof Blosum62ColourScheme)
       {
-        sgrouphash = hc;
+        recalc = true;
+        cs.setThreshold(viewStyle.getThreshold(),
+                ignoreGapsInConsensusCalculation);
       }
-      return true;
+      else
+      {
+        cs.setThreshold(0, ignoreGapsInConsensusCalculation);
+      }
+      if (recalc)
+      {
+        cs.setConsensus(hconsensus);
+        cs.setConservation(hconservation);
+      }
+      cs.alignmentChanged(alignment, hiddenRepSequences);
     }
-    return false;
-  }
-
-  /**
-   * checks current colsel against record of last hash value, and optionally
-   * updates record.
-   * 
-   * @param b
-   *          update the record of last hash value
-   * @return true if colsel changed since last call (when b is true)
-   */
-  public boolean isColSelChanged(boolean b)
-  {
-    int hc = (colSel == null || colSel.size() == 0) ? -1 : colSel
-            .hashCode();
-    if (hc != -1 && hc != colselhash)
+    if (getColourAppliesToAllGroups())
     {
-      if (b)
+      for (SequenceGroup sg : getAlignment().getGroups())
       {
-        colselhash = hc;
+        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);
+        }
       }
-      return true;
     }
-    return false;
   }
 
-  public boolean getIgnoreGapsConsensus()
+  @Override
+  public ColourSchemeI getGlobalColourScheme()
   {
-    return ignoreGapsInConsensusCalculation;
+    return globalColourScheme;
   }
 
-  /// property change stuff
+  protected AlignmentAnnotation consensus;
 
-  // JBPNote Prolly only need this in the applet version.
-  private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
-          this);
+  protected AlignmentAnnotation complementConsensus;
 
-  protected boolean showConservation = true;
+  protected AlignmentAnnotation strucConsensus;
 
-  protected boolean showQuality = true;
+  protected AlignmentAnnotation conservation;
 
-  protected boolean showConsensus = true;
+  protected AlignmentAnnotation quality;
+
+  protected AlignmentAnnotation[] groupConsensus;
 
+  protected AlignmentAnnotation[] groupConservation;
 
   /**
-   * Property change listener for changes in alignment
-   * 
-   * @param listener
-   *          DOCUMENT ME!
+   * results of alignment consensus analysis for visible portion of view
    */
-  public void addPropertyChangeListener(
-          java.beans.PropertyChangeListener listener)
-  {
-    changeSupport.addPropertyChangeListener(listener);
-  }
+  protected Hashtable[] hconsensus = null;
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param listener
-   *          DOCUMENT ME!
+   * results of cDNA complement consensus visible portion of view
    */
-  public void removePropertyChangeListener(
-          java.beans.PropertyChangeListener listener)
+  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)
   {
-    changeSupport.removePropertyChangeListener(listener);
+    hconservation = cons;
   }
 
   /**
-   * Property change listener for changes in alignment
-   * 
-   * @param prop
-   *          DOCUMENT ME!
-   * @param oldvalue
-   *          DOCUMENT ME!
-   * @param newvalue
-   *          DOCUMENT ME!
+   * percentage gaps allowed in a column before all amino acid properties should
+   * be considered unconserved
    */
-  public void firePropertyChange(String prop, Object oldvalue,
-          Object newvalue)
+  int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
+
+  @Override
+  public int getConsPercGaps()
   {
-    changeSupport.firePropertyChange(prop, oldvalue, newvalue);
+    return ConsPercGaps;
   }
 
-  // common hide/show column stuff
-  
-
-  public void hideSelectedColumns()
+  @Override
+  public void setSequenceConsensusHash(Hashtable[] hconsensus)
   {
-    if (colSel.size() < 1)
-    {
-      return;
-    }
+    this.hconsensus = hconsensus;
+  }
 
-    colSel.hideSelectedColumns();
-    setSelectionGroup(null);
+  @Override
+  public void setComplementConsensusHash(Hashtable[] hconsensus)
+  {
+    this.hcomplementConsensus = hconsensus;
+  }
 
-    hasHiddenColumns = true;
+  @Override
+  public Hashtable[] getSequenceConsensusHash()
+  {
+    return hconsensus;
   }
 
-  public void hideColumns(int start, int end)
+  @Override
+  public Hashtable[] getComplementConsensusHash()
   {
-    if (start == end)
-    {
-      colSel.hideColumns(start);
-    }
-    else
-    {
-      colSel.hideColumns(start, end);
-    }
+    return hcomplementConsensus;
+  }
 
-    hasHiddenColumns = true;
+  @Override
+  public Hashtable[] getRnaStructureConsensusHash()
+  {
+    return hStrucConsensus;
   }
 
-  public void showColumn(int col)
+  @Override
+  public void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus)
   {
-    colSel.revealHiddenColumns(col);
-    if (colSel.getHiddenColumns() == null)
-    {
-      hasHiddenColumns = false;
-    }
+    this.hStrucConsensus = hStrucConsensus;
+
   }
 
-  public void showAllHiddenColumns()
+  @Override
+  public AlignmentAnnotation getAlignmentQualityAnnot()
   {
-    colSel.revealAllHiddenColumns();
-    hasHiddenColumns = false;
+    return quality;
   }
 
-  
-  // common hide/show seq stuff
-  public void showAllHiddenSeqs()
+  @Override
+  public AlignmentAnnotation getAlignmentConservationAnnotation()
   {
-    if (alignment.getHiddenSequences().getSize() > 0)
-    {
-      if (selectionGroup == null)
-      {
-        selectionGroup = new SequenceGroup();
-        selectionGroup.setEndRes(alignment.getWidth() - 1);
-      }
-      Vector tmp = alignment.getHiddenSequences().showAll(
-              hiddenRepSequences);
-      for (int t = 0; t < tmp.size(); t++)
-      {
-        selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
-      }
+    return conservation;
+  }
 
-      hasHiddenRows = false;
-      hiddenRepSequences = null;
+  @Override
+  public AlignmentAnnotation getAlignmentConsensusAnnotation()
+  {
+    return consensus;
+  }
 
-      firePropertyChange("alignment", null, alignment.getSequences());
-      // used to set hasHiddenRows/hiddenRepSequences here, after the property changed event
-      sendSelection();
-    }
+  @Override
+  public AlignmentAnnotation getComplementConsensusAnnotation()
+  {
+    return complementConsensus;
   }
 
-  public void showSequence(int index)
+  @Override
+  public AlignmentAnnotation getAlignmentStrucConsensusAnnotation()
   {
-    Vector tmp = alignment.getHiddenSequences().showSequence(index,
-            hiddenRepSequences);
-    if (tmp.size() > 0)
-    {
+    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
+            || !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
+     */
+    final AlignmentI al = this.getAlignment();
+    if (!al.isNucleotide() && al.getCodonFrames() != null
+            && !al.getCodonFrames().isEmpty())
+    {
+      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;
+  }
+
+  @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();
+    }
+  }
+
+  /**
+   * 
+   * @return
+   */
+  @Override
+  public Map<SequenceI, SequenceCollectionI> getHiddenRepSequences()
+  {
+    return hiddenRepSequences;
+  }
+
+  @Override
+  public void setHiddenRepSequences(
+          Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
+  {
+    this.hiddenRepSequences = hiddenRepSequences;
+  }
+
+  @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)
+    {
+      updateConsensus(ap);
+      if (globalColourScheme != null)
+      {
+        globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
+                ignoreGapsInConsensusCalculation);
+      }
+    }
+
+  }
+
+  private long sgrouphash = -1, colselhash = -1;
+
+  /**
+   * checks current SelectionGroup against record of last hash value, and
+   * updates record.
+   * 
+   * @param b
+   *          update the record of last hash value
+   * 
+   * @return true if SelectionGroup changed since last call (when b is true)
+   */
+  public boolean isSelectionGroupChanged(boolean b)
+  {
+    int hc = (selectionGroup == null || selectionGroup.getSize() == 0) ? -1
+            : selectionGroup.hashCode();
+    if (hc != -1 && hc != sgrouphash)
+    {
+      if (b)
+      {
+        sgrouphash = hc;
+      }
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * checks current colsel against record of last hash value, and optionally
+   * updates record.
+   * 
+   * @param b
+   *          update the record of last hash value
+   * @return true if colsel changed since last call (when b is true)
+   */
+  public boolean isColSelChanged(boolean b)
+  {
+    int hc = (colSel == null || colSel.size() == 0) ? -1 : colSel
+            .hashCode();
+    if (hc != -1 && hc != colselhash)
+    {
+      if (b)
+      {
+        colselhash = hc;
+      }
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public boolean isIgnoreGapsConsensus()
+  {
+    return ignoreGapsInConsensusCalculation;
+  }
+
+  // / property change stuff
+
+  // JBPNote Prolly only need this in the applet version.
+  private final java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
+          this);
+
+  protected boolean showConservation = true;
+
+  protected boolean showQuality = true;
+
+  protected boolean showConsensus = true;
+
+  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
+   * 
+   * @param listener
+   *          DOCUMENT ME!
+   */
+  public void addPropertyChangeListener(
+          java.beans.PropertyChangeListener listener)
+  {
+    changeSupport.addPropertyChangeListener(listener);
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @param listener
+   *          DOCUMENT ME!
+   */
+  public void removePropertyChangeListener(
+          java.beans.PropertyChangeListener listener)
+  {
+    changeSupport.removePropertyChangeListener(listener);
+  }
+
+  /**
+   * Property change listener for changes in alignment
+   * 
+   * @param prop
+   *          DOCUMENT ME!
+   * @param oldvalue
+   *          DOCUMENT ME!
+   * @param newvalue
+   *          DOCUMENT ME!
+   */
+  public void firePropertyChange(String prop, Object oldvalue,
+          Object newvalue)
+  {
+    changeSupport.firePropertyChange(prop, oldvalue, newvalue);
+  }
+
+  // common hide/show column stuff
+
+  public void hideSelectedColumns()
+  {
+    if (colSel.size() < 1)
+    {
+      return;
+    }
+
+    colSel.hideSelectedColumns();
+    setSelectionGroup(null);
+
+  }
+
+  public void hideColumns(int start, int end)
+  {
+    if (start == end)
+    {
+      colSel.hideColumns(start);
+    }
+    else
+    {
+      colSel.hideColumns(start, end);
+    }
+  }
+
+  public void showColumn(int col)
+  {
+    colSel.revealHiddenColumns(col);
+
+  }
+
+  public void showAllHiddenColumns()
+  {
+    colSel.revealAllHiddenColumns();
+  }
+
+  // common hide/show seq stuff
+  public void showAllHiddenSeqs()
+  {
+    if (alignment.getHiddenSequences().getSize() > 0)
+    {
+      if (selectionGroup == null)
+      {
+        selectionGroup = new SequenceGroup();
+        selectionGroup.setEndRes(alignment.getWidth() - 1);
+      }
+      List<SequenceI> tmp = alignment.getHiddenSequences().showAll(
+              hiddenRepSequences);
+      for (SequenceI seq : tmp)
+      {
+        selectionGroup.addSequence(seq, false);
+        setSequenceAnnotationsVisible(seq, true);
+      }
+
+      hiddenRepSequences = null;
+
+      firePropertyChange("alignment", null, alignment.getSequences());
+      // used to set hasHiddenRows/hiddenRepSequences here, after the property
+      // changed event
+      sendSelection();
+    }
+  }
+
+  public void showSequence(int index)
+  {
+    List<SequenceI> tmp = alignment.getHiddenSequences().showSequence(
+            index, hiddenRepSequences);
+    if (tmp.size() > 0)
+    {
       if (selectionGroup == null)
       {
-        selectionGroup = new SequenceGroup();
-        selectionGroup.setEndRes(alignment.getWidth() - 1);
+        selectionGroup = new SequenceGroup();
+        selectionGroup.setEndRes(alignment.getWidth() - 1);
+      }
+
+      for (SequenceI seq : tmp)
+      {
+        selectionGroup.addSequence(seq, false);
+        setSequenceAnnotationsVisible(seq, true);
+      }
+      firePropertyChange("alignment", null, alignment.getSequences());
+      sendSelection();
+    }
+  }
+
+  public void hideAllSelectedSeqs()
+  {
+    if (selectionGroup == null || selectionGroup.getSize() < 1)
+    {
+      return;
+    }
+
+    SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
+
+    hideSequence(seqs);
+
+    setSelectionGroup(null);
+  }
+
+  public void hideSequence(SequenceI[] seq)
+  {
+    if (seq != null)
+    {
+      for (int i = 0; i < seq.length; i++)
+      {
+        alignment.getHiddenSequences().hideSequence(seq[i]);
+        setSequenceAnnotationsVisible(seq[i], false);
+      }
+      firePropertyChange("alignment", null, alignment.getSequences());
+    }
+  }
+
+  /**
+   * Set visibility for any annotations for the given sequence.
+   * 
+   * @param sequenceI
+   */
+  protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
+          boolean visible)
+  {
+    for (AlignmentAnnotation ann : alignment.getAlignmentAnnotation())
+    {
+      if (ann.sequenceRef == sequenceI)
+      {
+        ann.visible = visible;
+      }
+    }
+  }
+
+  public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
+  {
+    int sSize = sg.getSize();
+    if (sSize < 2)
+    {
+      return;
+    }
+
+    if (hiddenRepSequences == null)
+    {
+      hiddenRepSequences = new Hashtable();
+    }
+
+    hiddenRepSequences.put(repSequence, sg);
+
+    // Hide all sequences except the repSequence
+    SequenceI[] seqs = new SequenceI[sSize - 1];
+    int index = 0;
+    for (int i = 0; i < sSize; i++)
+    {
+      if (sg.getSequenceAt(i) != repSequence)
+      {
+        if (index == sSize - 1)
+        {
+          return;
+        }
+
+        seqs[index++] = sg.getSequenceAt(i);
+      }
+    }
+    sg.setSeqrep(repSequence); // note: not done in 2.7applet
+    sg.setHidereps(true); // note: not done in 2.7applet
+    hideSequence(seqs);
+
+  }
+
+  public boolean isHiddenRepSequence(SequenceI seq)
+  {
+    return alignment.getSeqrep() == seq
+            || (hiddenRepSequences != null && hiddenRepSequences
+                    .containsKey(seq));
+  }
+
+  public SequenceGroup getRepresentedSequences(SequenceI seq)
+  {
+    return (SequenceGroup) (hiddenRepSequences == null ? null
+            : hiddenRepSequences.get(seq));
+  }
+
+  @Override
+  public int adjustForHiddenSeqs(int alignmentIndex)
+  {
+    return alignment.getHiddenSequences().adjustForHiddenSeqs(
+            alignmentIndex);
+  }
+
+  @Override
+  public void invertColumnSelection()
+  {
+    colSel.invertColumnSelection(0, alignment.getWidth());
+  }
+
+  @Override
+  public SequenceI[] getSelectionAsNewSequence()
+  {
+    SequenceI[] sequences;
+    // JBPNote: Need to test jalviewLite.getSelectedSequencesAsAlignmentFrom -
+    // this was the only caller in the applet for this method
+    // JBPNote: in applet, this method returned references to the alignment
+    // sequences, and it did not honour the presence/absence of annotation
+    // attached to the alignment (probably!)
+    if (selectionGroup == null || selectionGroup.getSize() == 0)
+    {
+      sequences = alignment.getSequencesArray();
+      AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
+      for (int i = 0; i < sequences.length; i++)
+      {
+        // construct new sequence with subset of visible annotation
+        sequences[i] = new Sequence(sequences[i], annots);
+      }
+    }
+    else
+    {
+      sequences = selectionGroup.getSelectionAsNewSequences(alignment);
+    }
+
+    return sequences;
+  }
+
+  @Override
+  public SequenceI[] getSequenceSelection()
+  {
+    SequenceI[] sequences = null;
+    if (selectionGroup != null)
+    {
+      sequences = selectionGroup.getSequencesInOrder(alignment);
+    }
+    if (sequences == null)
+    {
+      sequences = alignment.getSequencesArray();
+    }
+    return sequences;
+  }
+
+  @Override
+  public CigarArray getViewAsCigars(boolean selectedRegionOnly)
+  {
+    return new CigarArray(alignment, colSel,
+            (selectedRegionOnly ? selectionGroup : null));
+  }
+
+  @Override
+  public jalview.datamodel.AlignmentView getAlignmentView(
+          boolean selectedOnly)
+  {
+    return getAlignmentView(selectedOnly, false);
+  }
+
+  @Override
+  public jalview.datamodel.AlignmentView getAlignmentView(
+          boolean selectedOnly, boolean markGroups)
+  {
+    return new AlignmentView(alignment, colSel, selectionGroup,
+            colSel != null && colSel.hasHiddenColumns(), selectedOnly,
+            markGroups);
+  }
+
+  @Override
+  public String[] getViewAsString(boolean selectedRegionOnly)
+  {
+    String[] selection = null;
+    SequenceI[] seqs = null;
+    int i, iSize;
+    int start = 0, end = 0;
+    if (selectedRegionOnly && selectionGroup != null)
+    {
+      iSize = selectionGroup.getSize();
+      seqs = selectionGroup.getSequencesInOrder(alignment);
+      start = selectionGroup.getStartRes();
+      end = selectionGroup.getEndRes() + 1;
+    }
+    else
+    {
+      iSize = alignment.getHeight();
+      seqs = alignment.getSequencesArray();
+      end = alignment.getWidth();
+    }
+
+    selection = new String[iSize];
+    if (colSel != null && colSel.hasHiddenColumns())
+    {
+      selection = colSel.getVisibleSequenceStrings(start, end, seqs);
+    }
+    else
+    {
+      for (i = 0; i < iSize; i++)
+      {
+        selection[i] = seqs[i].getSequenceAsString(start, end);
+      }
+
+    }
+    return selection;
+  }
+
+  @Override
+  public List<int[]> getVisibleRegionBoundaries(int min, int max)
+  {
+    ArrayList<int[]> regions = new ArrayList<int[]>();
+    int start = min;
+    int end = max;
+
+    do
+    {
+      if (colSel != null && colSel.hasHiddenColumns())
+      {
+        if (start == 0)
+        {
+          start = colSel.adjustForHiddenColumns(start);
+        }
+
+        end = colSel.getHiddenBoundaryRight(start);
+        if (start == end)
+        {
+          end = max;
+        }
+        if (end > max)
+        {
+          end = max;
+        }
+      }
+
+      regions.add(new int[] { start, end });
+
+      if (colSel != null && colSel.hasHiddenColumns())
+      {
+        start = colSel.adjustForHiddenColumns(end);
+        start = colSel.getHiddenBoundaryLeft(start) + 1;
+      }
+    } while (end < max);
+
+    int[][] startEnd = new int[regions.size()][2];
+
+    return regions;
+  }
+
+  @Override
+  public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(
+          boolean selectedOnly)
+  {
+    ArrayList<AlignmentAnnotation> ala = new ArrayList<AlignmentAnnotation>();
+    AlignmentAnnotation[] aa;
+    if ((aa = alignment.getAlignmentAnnotation()) != null)
+    {
+      for (AlignmentAnnotation annot : aa)
+      {
+        AlignmentAnnotation clone = new AlignmentAnnotation(annot);
+        if (selectedOnly && selectionGroup != null)
+        {
+          colSel.makeVisibleAnnotation(selectionGroup.getStartRes(),
+                  selectionGroup.getEndRes(), clone);
+        }
+        else
+        {
+          colSel.makeVisibleAnnotation(clone);
+        }
+        ala.add(clone);
+      }
+    }
+    return ala;
+  }
+
+  @Override
+  public boolean isPadGaps()
+  {
+    return padGaps;
+  }
+
+  @Override
+  public void setPadGaps(boolean padGaps)
+  {
+    this.padGaps = padGaps;
+  }
+
+  /**
+   * apply any post-edit constraints and trigger any calculations needed after
+   * an edit has been performed on the alignment
+   * 
+   * @param ap
+   */
+  @Override
+  public void alignmentChanged(AlignmentViewPanel ap)
+  {
+    if (isPadGaps())
+    {
+      alignment.padGaps();
+    }
+    if (autoCalculateConsensus)
+    {
+      updateConsensus(ap);
+    }
+    if (hconsensus != null && autoCalculateConsensus)
+    {
+      updateConservation(ap);
+    }
+    if (autoCalculateStrucConsensus)
+    {
+      updateStrucConsensus(ap);
+    }
+
+    // Reset endRes of groups if beyond alignment width
+    int alWidth = alignment.getWidth();
+    List<SequenceGroup> groups = alignment.getGroups();
+    if (groups != null)
+    {
+      for (SequenceGroup sg : groups)
+      {
+        if (sg.getEndRes() > alWidth)
+        {
+          sg.setEndRes(alWidth - 1);
+        }
+      }
+    }
+
+    if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
+    {
+      selectionGroup.setEndRes(alWidth - 1);
+    }
+
+    resetAllColourSchemes();
+    calculator.restartWorkers();
+    // alignment.adjustSequenceAnnotations();
+  }
+
+  /**
+   * reset scope and do calculations for all applied colourschemes on alignment
+   */
+  void resetAllColourSchemes()
+  {
+    ColourSchemeI cs = globalColourScheme;
+    if (cs != null)
+    {
+      cs.alignmentChanged(alignment, hiddenRepSequences);
+
+      cs.setConsensus(hconsensus);
+      if (cs.conservationApplied())
+      {
+        cs.setConservation(Conservation.calculateConservation("All",
+                ResidueProperties.propHash, 3, alignment.getSequences(), 0,
+                alignment.getWidth(), false, getConsPercGaps(), false));
+      }
+    }
+
+    for (SequenceGroup sg : alignment.getGroups())
+    {
+      if (sg.cs != null)
+      {
+        sg.cs.alignmentChanged(sg, hiddenRepSequences);
+      }
+      sg.recalcConservation();
+    }
+  }
+
+  protected void initAutoAnnotation()
+  {
+    // TODO: add menu option action that nulls or creates consensus object
+    // depending on if the user wants to see the annotation or not in a
+    // specific alignment
+
+    if (hconsensus == null && !isDataset)
+    {
+      if (!alignment.isNucleotide())
+      {
+        initConservation();
+        initQuality();
+      }
+      else
+      {
+        initRNAStructure();
+      }
+      consensus = new AlignmentAnnotation("Consensus", "PID",
+              new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
+      initConsensus(consensus);
+
+      initComplementConsensus();
+    }
+  }
+
+  /**
+   * If this is a protein alignment and there are mappings to cDNA, add the cDNA
+   * consensus annotation.
+   */
+  public void initComplementConsensus()
+  {
+    if (!alignment.isNucleotide())
+    {
+      final Set<AlignedCodonFrame> codonMappings = alignment
+              .getCodonFrames();
+      if (codonMappings != null && !codonMappings.isEmpty())
+      {
+        complementConsensus = new AlignmentAnnotation("cDNA Consensus",
+                "PID for cDNA", new Annotation[1], 0f, 100f,
+                AlignmentAnnotation.BAR_GRAPH);
+        initConsensus(complementConsensus);
+      }
+    }
+  }
+
+  private void initConsensus(AlignmentAnnotation aa)
+  {
+    aa.hasText = true;
+    aa.autoCalculated = true;
+
+    if (showConsensus)
+    {
+      alignment.addAnnotation(aa);
+    }
+  }
+
+  private void initConservation()
+  {
+    if (showConservation)
+    {
+      if (conservation == null)
+      {
+        conservation = new AlignmentAnnotation("Conservation",
+                "Conservation of total alignment less than "
+                        + getConsPercGaps() + "% gaps", new Annotation[1],
+                0f, 11f, AlignmentAnnotation.BAR_GRAPH);
+        conservation.hasText = true;
+        conservation.autoCalculated = true;
+        alignment.addAnnotation(conservation);
       }
+    }
+  }
 
-      for (int t = 0; t < tmp.size(); t++)
+  private void initQuality()
+  {
+    if (showQuality)
+    {
+      if (quality == null)
       {
-        selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
+        quality = new AlignmentAnnotation("Quality",
+                "Alignment Quality based on Blosum62 scores",
+                new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
+        quality.hasText = true;
+        quality.autoCalculated = true;
+        alignment.addAnnotation(quality);
       }
-      // JBPNote: refactor: only update flag if we modified visiblity (used to do this regardless) 
-      if (alignment.getHiddenSequences().getSize() < 1)
+    }
+  }
+
+  private void initRNAStructure()
+  {
+    if (alignment.hasRNAStructure() && strucConsensus == null)
+    {
+      strucConsensus = new AlignmentAnnotation("StrucConsensus", "PID",
+              new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
+      strucConsensus.hasText = true;
+      strucConsensus.autoCalculated = true;
+
+      if (showConsensus)
       {
-        hasHiddenRows = false;
+        alignment.addAnnotation(strucConsensus);
       }
-      firePropertyChange("alignment", null, alignment.getSequences());
-      sendSelection();
     }
   }
 
-
-  
-  public void hideAllSelectedSeqs()
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.api.AlignViewportI#calcPanelHeight()
+   */
+  @Override
+  public int calcPanelHeight()
   {
-    if (selectionGroup == null || selectionGroup.getSize() < 1)
+    // setHeight of panels
+    AlignmentAnnotation[] anns = getAlignment().getAlignmentAnnotation();
+    int height = 0;
+    int charHeight = getCharHeight();
+    if (anns != null)
     {
-      return;
-    }
+      BitSet graphgrp = new BitSet();
+      for (AlignmentAnnotation aa : anns)
+      {
+        if (aa == null)
+        {
+          System.err.println("Null annotation row: ignoring.");
+          continue;
+        }
+        if (!aa.visible)
+        {
+          continue;
+        }
+        if (aa.graphGroup > -1)
+        {
+          if (graphgrp.get(aa.graphGroup))
+          {
+            continue;
+          }
+          else
+          {
+            graphgrp.set(aa.graphGroup);
+          }
+        }
+        aa.height = 0;
 
-    SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
+        if (aa.hasText)
+        {
+          aa.height += charHeight;
+        }
 
-    hideSequence(seqs);
+        if (aa.hasIcons)
+        {
+          aa.height += 16;
+        }
 
-    setSelectionGroup(null);
+        if (aa.graph > 0)
+        {
+          aa.height += aa.graphHeight;
+        }
+
+        if (aa.height == 0)
+        {
+          aa.height = 20;
+        }
+
+        height += aa.height;
+      }
+    }
+    if (height == 0)
+    {
+      // set minimum
+      height = 20;
+    }
+    return height;
   }
-  
 
-  public void hideSequence(SequenceI[] seq)
-  {
-    if (seq != null)
+  @Override
+  public void updateGroupAnnotationSettings(boolean applyGlobalSettings,
+          boolean preserveNewGroupSettings)
+  {
+    boolean updateCalcs = false;
+    boolean conv = isShowGroupConservation();
+    boolean cons = isShowGroupConsensus();
+    boolean showprf = isShowSequenceLogo();
+    boolean showConsHist = isShowConsensusHistogram();
+    boolean normLogo = isNormaliseSequenceLogo();
+
+    /**
+     * TODO reorder the annotation rows according to group/sequence ordering on
+     * alignment
+     */
+    boolean sortg = true;
+
+    // remove old automatic annotation
+    // add any new annotation
+
+    // intersect alignment annotation with alignment groups
+
+    AlignmentAnnotation[] aan = alignment.getAlignmentAnnotation();
+    List<SequenceGroup> oldrfs = new ArrayList<SequenceGroup>();
+    if (aan != null)
     {
-      for (int i = 0; i < seq.length; i++)
+      for (int an = 0; an < aan.length; an++)
       {
-        alignment.getHiddenSequences().hideSequence(seq[i]);
+        if (aan[an].autoCalculated && aan[an].groupRef != null)
+        {
+          oldrfs.add(aan[an].groupRef);
+          alignment.deleteAnnotation(aan[an], false);
+        }
       }
-      hasHiddenRows = true;
-      firePropertyChange("alignment", null, alignment.getSequences());
     }
+    if (alignment.getGroups() != null)
+    {
+      for (SequenceGroup sg : alignment.getGroups())
+      {
+        updateCalcs = false;
+        if (applyGlobalSettings
+                || (!preserveNewGroupSettings && !oldrfs.contains(sg)))
+        {
+          // set defaults for this group's conservation/consensus
+          sg.setshowSequenceLogo(showprf);
+          sg.setShowConsensusHistogram(showConsHist);
+          sg.setNormaliseSequenceLogo(normLogo);
+        }
+        if (conv)
+        {
+          updateCalcs = true;
+          alignment.addAnnotation(sg.getConservationRow(), 0);
+        }
+        if (cons)
+        {
+          updateCalcs = true;
+          alignment.addAnnotation(sg.getConsensus(), 0);
+        }
+        // refresh the annotation rows
+        if (updateCalcs)
+        {
+          sg.recalcConservation();
+        }
+      }
+    }
+    oldrfs.clear();
   }
 
-  public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
+  @Override
+  public boolean isDisplayReferenceSeq()
   {
-    int sSize = sg.getSize();
-    if (sSize < 2)
+    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)
     {
-      return;
+      sequenceColours.remove(seq);
     }
-
-    if (hiddenRepSequences == null)
+    else
     {
-      hiddenRepSequences = new Hashtable();
+      sequenceColours.put(seq, col);
     }
+  }
 
-    hiddenRepSequences.put(repSequence, sg);
-
-    // Hide all sequences except the repSequence
-    SequenceI[] seqs = new SequenceI[sSize - 1];
-    int index = 0;
-    for (int i = 0; i < sSize; i++)
+  @Override
+  public void updateSequenceIdColours()
+  {
+    for (SequenceGroup sg : alignment.getGroups())
     {
-      if (sg.getSequenceAt(i) != repSequence)
+      if (sg.idColour != null)
       {
-        if (index == sSize - 1)
+        for (SequenceI s : sg.getSequences(getHiddenRepSequences()))
         {
-          return;
+          sequenceColours.put(s, sg.idColour);
         }
+      }
+    }
+  }
 
-        seqs[index++] = sg.getSequenceAt(i);
+  @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);
       }
     }
-    sg.setSeqrep(repSequence); // note: not done in 2.7applet 
-    sg.setHidereps(true); // note: not done in 2.7applet
-    hideSequence(seqs);
+  }
+
+  @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.getRegisterdFeaturesCount() > 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()
+   */
+  public Color getTextColour()
+  {
+    return viewStyle.getTextColour();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getTextColour2()
+   */
+  public Color getTextColour2()
+  {
+    return viewStyle.getTextColour2();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getThresholdTextColour()
+   */
+  public int getThresholdTextColour()
+  {
+    return viewStyle.getThresholdTextColour();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isConservationColourSelected()
+   */
+  public boolean isConservationColourSelected()
+  {
+    return viewStyle.isConservationColourSelected();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isRenderGaps()
+   */
+  public boolean isRenderGaps()
+  {
+    return viewStyle.isRenderGaps();
+  }
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#isShowColourText()
+   */
+  public boolean isShowColourText()
+  {
+    return viewStyle.isShowColourText();
+  }
+
+  /**
+   * @param conservationColourSelected
+   * @see jalview.api.ViewStyleI#setConservationColourSelected(boolean)
+   */
+  public void setConservationColourSelected(
+          boolean conservationColourSelected)
+  {
+    viewStyle.setConservationColourSelected(conservationColourSelected);
+  }
+
+  /**
+   * @param showColourText
+   * @see jalview.api.ViewStyleI#setShowColourText(boolean)
+   */
+  public void setShowColourText(boolean showColourText)
+  {
+    viewStyle.setShowColourText(showColourText);
+  }
+
+  /**
+   * @param textColour
+   * @see jalview.api.ViewStyleI#setTextColour(java.awt.Color)
+   */
+  public void setTextColour(Color textColour)
+  {
+    viewStyle.setTextColour(textColour);
+  }
+
+  /**
+   * @param thresholdTextColour
+   * @see jalview.api.ViewStyleI#setThresholdTextColour(int)
+   */
+  public void setThresholdTextColour(int thresholdTextColour)
+  {
+    viewStyle.setThresholdTextColour(thresholdTextColour);
+  }
+
+  /**
+   * @param textColour2
+   * @see jalview.api.ViewStyleI#setTextColour2(java.awt.Color)
+   */
+  public void setTextColour2(Color textColour2)
+  {
+    viewStyle.setTextColour2(textColour2);
+  }
+
+  @Override
+  public ViewStyleI getViewStyle()
+  {
+    return new ViewStyle(viewStyle);
+  }
+
+  @Override
+  public void setViewStyle(ViewStyleI settingsForView)
+  {
+    viewStyle = new ViewStyle(settingsForView);
   }
 
-  public boolean isHiddenRepSequence(SequenceI seq)
+  @Override
+  public boolean sameStyle(ViewStyleI them)
   {
-    return hiddenRepSequences != null
-          && hiddenRepSequences.containsKey(seq);
+    return viewStyle.sameStyle(them);
   }
-  public SequenceGroup getRepresentedSequences(SequenceI seq)
+
+  /**
+   * @return
+   * @see jalview.api.ViewStyleI#getIdWidth()
+   */
+  public int getIdWidth()
   {
-    return (SequenceGroup) (hiddenRepSequences == null ? null : hiddenRepSequences.get(seq));
+    return viewStyle.getIdWidth();
   }
 
-  public int adjustForHiddenSeqs(int alignmentIndex)
+  /**
+   * @param i
+   * @see jalview.api.ViewStyleI#setIdWidth(int)
+   */
+  public void setIdWidth(int i)
   {
-    return alignment.getHiddenSequences().adjustForHiddenSeqs(
-            alignmentIndex);
+    viewStyle.setIdWidth(i);
   }
 
-  // Selection manipulation
   /**
-   * broadcast selection to any interested parties
+   * @return
+   * @see jalview.api.ViewStyleI#isCentreColumnLabels()
    */
-  public abstract void sendSelection();
-  
-
-  public void invertColumnSelection()
+  public boolean isCentreColumnLabels()
   {
-    colSel.invertColumnSelection(0, alignment.getWidth());
+    return viewStyle.isCentreColumnLabels();
   }
 
-
   /**
-   * This method returns an array of new SequenceI objects derived from the
-   * whole alignment or just the current selection with start and end points
-   * adjusted
-   * 
-   * @note if you need references to the actual SequenceI objects in the
-   *       alignment or currently selected then use getSequenceSelection()
-   * @return selection as new sequenceI objects
+   * @param centreColumnLabels
+   * @see jalview.api.ViewStyleI#setCentreColumnLabels(boolean)
    */
-  public SequenceI[] getSelectionAsNewSequence()
+  public void setCentreColumnLabels(boolean centreColumnLabels)
   {
-    SequenceI[] sequences;
-    // JBPNote: Need to test jalviewLite.getSelectedSequencesAsAlignmentFrom - this was the only caller in the applet for this method
-    // JBPNote: in applet, this method returned references to the alignment sequences, and it did not honour the presence/absence of annotation attached to the alignment (probably!)
-    if (selectionGroup == null)
-    {
-      sequences = alignment.getSequencesArray();
-      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
-      }
-    }
-    else
-    {
-      sequences = selectionGroup.getSelectionAsNewSequences(alignment);
-    }
-
-    return sequences;
+    viewStyle.setCentreColumnLabels(centreColumnLabels);
   }
 
-
   /**
-   * get the currently selected sequence objects or all the sequences in the
-   * alignment.
-   * 
-   * @return array of references to sequence objects
+   * @param showdbrefs
+   * @see jalview.api.ViewStyleI#setShowDBRefs(boolean)
    */
-  public SequenceI[] getSequenceSelection()
+  public void setShowDBRefs(boolean showdbrefs)
   {
-    SequenceI[] sequences = null;
-    if (selectionGroup != null)
-    {
-      sequences = selectionGroup.getSequencesInOrder(alignment);
-    }
-    if (sequences == null)
-    {
-      sequences = alignment.getSequencesArray();
-    }
-    return sequences;
+    viewStyle.setShowDBRefs(showdbrefs);
   }
 
-
   /**
-   * This method returns the visible alignment as text, as seen on the GUI, ie
-   * if columns are hidden they will not be returned in the result. Use this for
-   * calculating trees, PCA, redundancy etc on views which contain hidden
-   * columns.
-   * 
-   * @return String[]
+   * @return
+   * @see jalview.api.ViewStyleI#isShowDBRefs()
    */
-  public jalview.datamodel.CigarArray getViewAsCigars(
-          boolean selectedRegionOnly)
+  public boolean isShowDBRefs()
   {
-    return new jalview.datamodel.CigarArray(alignment,
-            (hasHiddenColumns ? colSel : null),
-            (selectedRegionOnly ? selectionGroup : null));
+    return viewStyle.isShowDBRefs();
   }
 
   /**
-   * return a compact representation of the current alignment selection to pass
-   * to an analysis function
-   * 
-   * @param selectedOnly
-   *          boolean true to just return the selected view
-   * @return AlignmentView
+   * @return
+   * @see jalview.api.ViewStyleI#isShowNPFeats()
    */
-  public jalview.datamodel.AlignmentView getAlignmentView(
-          boolean selectedOnly)
+  public boolean isShowNPFeats()
   {
-    return getAlignmentView(selectedOnly, false);
+    return viewStyle.isShowNPFeats();
   }
 
   /**
-   * return a compact representation of the current alignment selection to pass
-   * to an analysis function
-   * 
-   * @param selectedOnly
-   *          boolean true to just return the selected view
-   * @param markGroups
-   *          boolean true to annotate the alignment view with groups on the
-   *          alignment (and intersecting with selected region if selectedOnly
-   *          is true)
-   * @return AlignmentView
+   * @param shownpfeats
+   * @see jalview.api.ViewStyleI#setShowNPFeats(boolean)
    */
-  public jalview.datamodel.AlignmentView getAlignmentView(
-          boolean selectedOnly, boolean markGroups)
+  public void setShowNPFeats(boolean shownpfeats)
   {
-    return new AlignmentView(alignment, colSel, selectionGroup,
-            hasHiddenColumns, selectedOnly, markGroups);
+    viewStyle.setShowNPFeats(shownpfeats);
   }
 
+  public abstract StructureSelectionManager getStructureSelectionManager();
 
   /**
-   * This method returns the visible alignment as text, as seen on the GUI, ie
-   * if columns are hidden they will not be returned in the result. Use this for
-   * calculating trees, PCA, redundancy etc on views which contain hidden
-   * columns.
+   * Add one command to the command history list.
    * 
-   * @return String[]
+   * @param command
    */
-  public String[] getViewAsString(boolean selectedRegionOnly)
+  public void addToHistoryList(CommandI command)
   {
-    String[] selection = null;
-    SequenceI[] seqs = null;
-    int i, iSize;
-    int start = 0, end = 0;
-    if (selectedRegionOnly && selectionGroup != null)
+    if (this.historyList != null)
     {
-      iSize = selectionGroup.getSize();
-      seqs = selectionGroup.getSequencesInOrder(alignment);
-      start = selectionGroup.getStartRes();
-      end = selectionGroup.getEndRes() + 1;
+      this.historyList.push(command);
+      broadcastCommand(command, false);
     }
-    else
+  }
+
+  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)
     {
-      iSize = alignment.getHeight();
-      seqs = alignment.getSequencesArray();
-      end = alignment.getWidth();
+      this.redoList.push(command);
     }
+    broadcastCommand(command, true);
+  }
 
-    selection = new String[iSize];
-    if (hasHiddenColumns)
+  /**
+   * Clear the command redo list.
+   */
+  public void clearRedoList()
+  {
+    if (this.redoList != null)
     {
-      selection = colSel.getVisibleSequenceStrings(start, end, seqs);
+      this.redoList.clear();
     }
-    else
-    {
-      for (i = 0; i < iSize; i++)
-      {
-        selection[i] = seqs[i].getSequenceAsString(start, end);
-      }
+  }
 
-    }
-    return selection;
+  public void setHistoryList(Deque<CommandI> list)
+  {
+    this.historyList = list;
   }
 
+  public Deque<CommandI> getHistoryList()
+  {
+    return this.historyList;
+  }
 
-  /**
-   * return visible region boundaries within given column range
-   * @param min first column (inclusive, from 0)
-   * @param max last column (exclusive)
-   * @return int[][] range of {start,end} visible positions
-   */
-  public int[][] getVisibleRegionBoundaries(int min, int max)
+  public void setRedoList(Deque<CommandI> list)
   {
-    Vector regions = new Vector();
-    int start = min;
-    int end = max;
+    this.redoList = list;
+  }
 
-    do
-    {
-      if (hasHiddenColumns)
-      {
-        if (start == 0)
-        {
-          start = colSel.adjustForHiddenColumns(start);
-        }
+  public Deque<CommandI> getRedoList()
+  {
+    return this.redoList;
+  }
 
-        end = colSel.getHiddenBoundaryRight(start);
-        if (start == end)
-        {
-          end = max;
-        }
-        if (end > max)
-        {
-          end = max;
-        }
-      }
+  @Override
+  public VamsasSource getVamsasSource()
+  {
+    return this;
+  }
 
-      regions.addElement(new int[]
-      { start, end });
+  public SequenceAnnotationOrder getSortAnnotationsBy()
+  {
+    return sortAnnotationsBy;
+  }
 
-      if (hasHiddenColumns)
-      {
-        start = colSel.adjustForHiddenColumns(end);
-        start = colSel.getHiddenBoundaryLeft(start) + 1;
-      }
-    } while (end < max);
+  public void setSortAnnotationsBy(SequenceAnnotationOrder sortAnnotationsBy)
+  {
+    this.sortAnnotationsBy = sortAnnotationsBy;
+  }
 
-    int[][] startEnd = new int[regions.size()][2];
+  public boolean isShowAutocalculatedAbove()
+  {
+    return showAutocalculatedAbove;
+  }
 
-    regions.copyInto(startEnd);
+  public void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
+  {
+    this.showAutocalculatedAbove = showAutocalculatedAbove;
+  }
 
-    return startEnd;
+  @Override
+  public boolean isScaleProteinAsCdna()
+  {
+    return viewStyle.isScaleProteinAsCdna();
+  }
 
+  @Override
+  public void setScaleProteinAsCdna(boolean b)
+  {
+    viewStyle.setScaleProteinAsCdna(b);
   }
+
   /**
-   * @return the padGaps
+   * @return true if view should scroll to show the highlighted region of a
+   *         sequence
+   * @return
    */
-  public boolean isPadGaps()
+  @Override
+  public final boolean isFollowHighlight()
   {
-    return padGaps;
+    return followHighlight;
   }
-  /**
-   * @param padGaps the padGaps to set
-   */
-  public void setPadGaps(boolean padGaps)
+
+  @Override
+  public final void setFollowHighlight(boolean b)
   {
-    this.padGaps = padGaps;
+    this.followHighlight = b;
   }
-  /**
-   * apply any post-edit constraints and trigger any calculations needed after an edit has been performed on the alignment 
-   * @param ap
-   */
-  public void alignmentChanged(AlignmentViewPanel ap)
+
+  public int getStartRes()
   {
-    if (isPadGaps())
-    {
-      alignment.padGaps();
-    }
-    if (autoCalculateConsensus)
-    {
-      updateConsensus(ap);
-    }
-    if (hconsensus != null && autoCalculateConsensus)
+    return startRes;
+  }
+
+  public int getEndRes()
+  {
+    return endRes;
+  }
+
+  public int getStartSeq()
+  {
+    return startSeq;
+  }
+
+  public void setStartRes(int res)
+  {
+    this.startRes = res;
+  }
+
+  public void setStartSeq(int seq)
+  {
+    this.startSeq = seq;
+  }
+
+  public void setEndRes(int res)
+  {
+    if (res > alignment.getWidth() - 1)
     {
-      updateConservation(ap);
+      // log.System.out.println(" Corrected res from " + res + " to maximum " +
+      // (alignment.getWidth()-1));
+      res = alignment.getWidth() - 1;
     }
-    if (autoCalculateStrucConsensus)
+    if (res < 0)
     {
-      updateStrucConsensus(ap);
+      res = 0;
     }
+    this.endRes = res;
+  }
 
-    // Reset endRes of groups if beyond alignment width
-    int alWidth = alignment.getWidth();
-    Vector groups = alignment.getGroups();
-    if (groups != null)
+  public void setEndSeq(int seq)
+  {
+    if (seq > alignment.getHeight())
     {
-      for (int i = 0; i < groups.size(); i++)
-      {
-        SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
-        if (sg.getEndRes() > alWidth)
-        {
-          sg.setEndRes(alWidth - 1);
-        }
-      }
+      seq = alignment.getHeight();
     }
-
-    if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
+    if (seq < 0)
     {
-      selectionGroup.setEndRes(alWidth - 1);
+      seq = 0;
     }
+    this.endSeq = seq;
+  }
 
-    resetAllColourSchemes();
-
-    // alignment.adjustSequenceAnnotations();
+  public int getEndSeq()
+  {
+    return endSeq;
   }
 
-  
   /**
-   * reset scope and do calculations for all applied colourschemes on alignment
+   * 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
    */
-  void resetAllColourSchemes()
+  protected int findComplementScrollTarget(SearchResults sr)
   {
-    ColourSchemeI cs = globalColourScheme;
-    if (cs != null)
+    final AlignViewportI complement = getCodingComplement();
+    if (complement == null || !complement.isFollowHighlight())
+    {
+      return 0;
+    }
+    boolean iAmProtein = !getAlignment().isNucleotide();
+    AlignmentI proteinAlignment = iAmProtein ? getAlignment() : complement
+            .getAlignment();
+    if (proteinAlignment == null)
+    {
+      return 0;
+    }
+    final Set<AlignedCodonFrame> mappings = proteinAlignment
+            .getCodonFrames();
+
+    /*
+     * Heuristic: find the first mapped sequence (if any) with a non-gapped
+     * residue in the middle column of the visible region. Scroll the
+     * complementary alignment to line up the corresponding residue.
+     */
+    int seqOffset = 0;
+    SequenceI sequence = null;
+
+    /*
+     * locate 'middle' column (true middle if an odd number visible, left of
+     * middle if an even number visible)
+     */
+    int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2;
+    final HiddenSequences hiddenSequences = getAlignment()
+            .getHiddenSequences();
+
+    /*
+     * searching to the bottom of the alignment gives smoother scrolling across
+     * all gapped visible regions
+     */
+    int lastSeq = alignment.getHeight() - 1;
+    for (int seqNo = getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++)
     {
-      if (cs instanceof ClustalxColourScheme)
+      sequence = getAlignment().getSequenceAt(seqNo);
+      if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
       {
-        ((ClustalxColourScheme) cs).resetClustalX(alignment.getSequences(),
-                alignment.getWidth());
+        continue;
       }
-
-      cs.setConsensus(hconsensus);
-      if (cs.conservationApplied())
+      if (Comparison.isGap(sequence.getCharAt(middleColumn)))
       {
-        cs.setConservation(Conservation.calculateConservation("All",
-                ResidueProperties.propHash, 3, alignment.getSequences(), 0,
-                alignment.getWidth(), false, getConsPercGaps(), false));
+        continue;
       }
-    }
-
-    int s, sSize = alignment.getGroups().size();
-    for (s = 0; s < sSize; s++)
-    {
-      SequenceGroup sg = (SequenceGroup) alignment.getGroups().elementAt(s);
-      if (sg.cs != null && sg.cs instanceof ClustalxColourScheme)
+      List<AlignedCodonFrame> seqMappings = MappingUtils
+              .findMappingsForSequence(sequence, mappings);
+      if (!seqMappings.isEmpty())
       {
-        ((ClustalxColourScheme) sg.cs).resetClustalX(sg
-                .getSequences(hiddenRepSequences), sg.getWidth());
+        break;
       }
-      sg.recalcConservation();
     }
-  }
 
-  protected void initAutoAnnotation()
-  {
-    // TODO: add menu option action that nulls or creates consensus object
-    // depending on if the user wants to see the annotation or not in a
-    // specific alignment
-
-    if (hconsensus == null && !isDataset)
+    if (sequence == null)
     {
-      if (!alignment.isNucleotide())
-      {
-        if (showConservation)
-        {
-          if (conservation==null)
-        {
-        conservation = new AlignmentAnnotation("Conservation",
-                "Conservation of total alignment less than " + getConsPercGaps()
-                        + "% gaps", new Annotation[1], 0f, 11f,
-                AlignmentAnnotation.BAR_GRAPH);
-        conservation.hasText = true;
-        conservation.autoCalculated = true;
-          alignment.addAnnotation(conservation);
-        }
-        }
-        if (showQuality)
-        {
-          if (quality==null)
-          {
-          quality = new AlignmentAnnotation("Quality",
-                  "Alignment Quality based on Blosum62 scores",
-                  new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
-          quality.hasText = true;
-          quality.autoCalculated = true;
-          alignment.addAnnotation(quality);
-        }
-        }
-      } else {
-        if (alignment.hasRNAStructure())
-        {
-          strucConsensus = new AlignmentAnnotation("StrucConsensus", "PID",
-                  new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
-          strucConsensus.hasText = true;
-          strucConsensus.autoCalculated = true;
-        }
-      }
-        
-      consensus = new AlignmentAnnotation("Consensus", "PID",
-              new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
-      consensus.hasText = true;
-      consensus.autoCalculated = true;
-  
-      if (showConsensus)
-      {
-        alignment.addAnnotation(consensus);
-        if (strucConsensus!=null)
-        {
-          alignment.addAnnotation(strucConsensus);
-        }
-      }
+      /*
+       * No ungapped mapped sequence in middle column - do nothing
+       */
+      return 0;
     }
+    MappingUtils.addSearchResults(sr, sequence,
+            sequence.findPosition(middleColumn), mappings);
+    return seqOffset;
   }
-
 }