JAL-3597 additional references nulled, bug in test corrected
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
index b62c61c..063d4ab 100644 (file)
@@ -22,6 +22,7 @@ package jalview.viewmodel;
 
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.Conservation;
+import jalview.analysis.TreeModel;
 import jalview.api.AlignCalcManagerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
@@ -33,8 +34,8 @@ 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.HiddenColumns;
 import jalview.datamodel.HiddenSequences;
 import jalview.datamodel.ProfilesI;
 import jalview.datamodel.SearchResultsI;
@@ -51,6 +52,7 @@ import jalview.structure.VamsasSource;
 import jalview.util.Comparison;
 import jalview.util.MapList;
 import jalview.util.MappingUtils;
+import jalview.util.MessageManager;
 import jalview.viewmodel.styles.ViewStyle;
 import jalview.workers.AlignCalcManager;
 import jalview.workers.ComplementConsensusThread;
@@ -65,6 +67,7 @@ import java.util.BitSet;
 import java.util.Deque;
 import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -75,10 +78,10 @@ import java.util.Map;
  * @author jimp
  * 
  */
-public abstract class AlignmentViewport implements AlignViewportI,
-        CommandListener, VamsasSource
+public abstract class AlignmentViewport
+        implements AlignViewportI, CommandListener, VamsasSource
 {
-  protected ViewportPositionProps posProps;
+  protected ViewportRanges ranges;
 
   protected ViewStyleI viewStyle = new ViewStyle();
 
@@ -90,9 +93,20 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   FeaturesDisplayedI featuresDisplayed = null;
 
-  protected Deque<CommandI> historyList = new ArrayDeque<CommandI>();
+  protected Deque<CommandI> historyList = new ArrayDeque<>();
 
-  protected Deque<CommandI> redoList = new ArrayDeque<CommandI>();
+  protected Deque<CommandI> redoList = new ArrayDeque<>();
+
+  /**
+   * alignment displayed in the viewport. Please use get/setter
+   */
+  protected AlignmentI alignment;
+
+  public AlignmentViewport(AlignmentI al)
+  {
+    setAlignment(al);
+    ranges = new ViewportRanges(al);
+  }
 
   /**
    * @param name
@@ -392,6 +406,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void setWrapAlignment(boolean state)
   {
     viewStyle.setWrapAlignment(state);
+    ranges.setWrappedMode(state);
   }
 
   /**
@@ -554,11 +569,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
     viewStyle.setSeqNameItalics(default1);
   }
 
-  /**
-   * alignment displayed in the viewport. Please use get/setter
-   */
-  protected AlignmentI alignment;
-
   @Override
   public AlignmentI getAlignment()
   {
@@ -589,9 +599,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return isDataset;
   }
 
+  private Map<SequenceI, SequenceCollectionI> hiddenRepSequences;
 
-
-  // protected ColumnSelection colSel = new ColumnSelection();
+  protected ColumnSelection colSel = new ColumnSelection();
 
   public boolean autoCalculateConsensus = true;
 
@@ -599,7 +609,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   protected boolean ignoreGapsInConsensusCalculation = false;
 
-  protected ResidueShaderI residueShading;
+  protected ResidueShaderI residueShading = new ResidueShader();
 
   @Override
   public void setGlobalColourScheme(ColourSchemeI cs)
@@ -632,7 +642,12 @@ public abstract class AlignmentViewport implements AlignViewportI,
       {
         residueShading.setConservation(hconservation);
       }
-      residueShading.alignmentChanged(alignment, getHiddenRepSequences());
+      /*
+       * reset conservation flag in case just set to false if
+       * Conservation was null (calculation still in progress)
+       */
+      residueShading.setConservationApplied(getConservationSelected());
+      residueShading.alignmentChanged(alignment, hiddenRepSequences);
     }
 
     /*
@@ -647,12 +662,12 @@ public abstract class AlignmentViewport implements AlignViewportI,
          * retain any colour thresholds per group while
          * changing choice of colour scheme (JAL-2386)
          */
-        sg.setColourScheme(cs);
+        sg.setColourScheme(
+                cs == null ? null : cs.getInstance(this, sg));
         if (cs != null)
         {
-          sg.getGroupColourScheme()
-.alignmentChanged(sg,
-                  getHiddenRepSequences());
+          sg.getGroupColourScheme().alignmentChanged(sg,
+                  hiddenRepSequences);
         }
       }
     }
@@ -661,8 +676,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public ColourSchemeI getGlobalColourScheme()
   {
-    return residueShading == null ? null : residueShading
-            .getColourScheme();
+    return residueShading == null ? null : residueShading.getColourScheme();
   }
 
   @Override
@@ -675,6 +689,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   protected AlignmentAnnotation complementConsensus;
 
+  protected AlignmentAnnotation gapcounts;
+
   protected AlignmentAnnotation strucConsensus;
 
   protected AlignmentAnnotation conservation;
@@ -777,6 +793,12 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
+  public AlignmentAnnotation getAlignmentGapAnnotation()
+  {
+    return gapcounts;
+  }
+
+  @Override
   public AlignmentAnnotation getComplementConsensusAnnotation()
   {
     return complementConsensus;
@@ -802,11 +824,11 @@ public abstract class AlignmentViewport implements AlignViewportI,
     {
       return;
     }
-    if (calculator
-            .getRegisteredWorkersOfClass(jalview.workers.ConservationThread.class) == null)
+    if (calculator.getRegisteredWorkersOfClass(
+            jalview.workers.ConservationThread.class) == null)
     {
-      calculator.registerWorker(new jalview.workers.ConservationThread(
-              this, ap));
+      calculator.registerWorker(
+              new jalview.workers.ConservationThread(this, ap));
     }
   }
 
@@ -820,7 +842,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
     {
       return;
     }
-    if (calculator.getRegisteredWorkersOfClass(ConsensusThread.class) == null)
+    if (calculator
+            .getRegisteredWorkersOfClass(ConsensusThread.class) == null)
     {
       calculator.registerWorker(new ConsensusThread(this, ap));
     }
@@ -851,8 +874,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
       }
       if (doConsensus)
       {
-        if (calculator
-                .getRegisteredWorkersOfClass(ComplementConsensusThread.class) == null)
+        if (calculator.getRegisteredWorkersOfClass(
+                ComplementConsensusThread.class) == null)
         {
           calculator
                   .registerWorker(new ComplementConsensusThread(this, ap));
@@ -876,7 +899,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
     {
       return;
     }
-    if (calculator.getRegisteredWorkersOfClass(StrucConsensusThread.class) == null)
+    if (calculator.getRegisteredWorkersOfClass(
+            StrucConsensusThread.class) == null)
     {
       calculator.registerWorker(new StrucConsensusThread(this, ap));
     }
@@ -926,11 +950,16 @@ public abstract class AlignmentViewport implements AlignViewportI,
     groupConsensus = null;
     groupConservation = null;
     hconsensus = null;
+    hconservation = null;
     hcomplementConsensus = null;
-    // colour scheme may hold reference to consensus
-    residueShading = null;
-    // TODO remove listeners from changeSupport?
+    gapcounts = null;
+    calculator = null;
+    residueShading = null; // may hold a reference to Consensus
     changeSupport = null;
+    ranges = null;
+    currentTree = null;
+    selectionGroup = null;
+    colSel = null;
     setAlignment(null);
   }
 
@@ -1093,29 +1122,26 @@ public abstract class AlignmentViewport implements AlignViewportI,
     }
   }
 
-  public void setHiddenColumns(ColumnSelection colsel)
+  public void setHiddenColumns(HiddenColumns hidden)
   {
-    posProps.setHiddenColumns(colsel);
-    // this.colSel = colsel;
+    this.alignment.setHiddenColumns(hidden);
   }
 
   @Override
   public ColumnSelection getColumnSelection()
   {
-    return posProps.getColumnSelection();
-    // return colSel;
+    return colSel;
   }
 
   @Override
   public void setColumnSelection(ColumnSelection colSel)
   {
-    posProps.setColumnSelection(colSel);
-    /*this.colSel = colSel;
+    this.colSel = colSel;
     if (colSel != null)
     {
       updateHiddenColumns();
     }
-    isColSelChanged(true);*/
+    isColSelChanged(true);
   }
 
   /**
@@ -1125,14 +1151,14 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public Map<SequenceI, SequenceCollectionI> getHiddenRepSequences()
   {
-    return posProps.getHiddenRepSequences();
+    return hiddenRepSequences;
   }
 
   @Override
   public void setHiddenRepSequences(
           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
   {
-    posProps.getHiddenRepSequences();
+    this.hiddenRepSequences = hiddenRepSequences;
   }
 
   @Override
@@ -1145,8 +1171,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public boolean hasHiddenColumns()
   {
-    return posProps.hasHiddenColumns();
-    // return colSel != null && colSel.hasHiddenColumns();
+    return alignment.getHiddenColumns() != null
+            && alignment.getHiddenColumns().hasHiddenColumns();
   }
 
   public void updateHiddenColumns()
@@ -1168,8 +1194,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
   {
     if (sequenceSetID != null)
     {
-      System.err
-              .println("Warning - overwriting a sequenceSetId for a viewport!");
+      System.err.println(
+              "Warning - overwriting a sequenceSetId for a viewport!");
     }
     sequenceSetID = new String(newid);
   }
@@ -1216,7 +1242,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   }
 
-  private long sgrouphash = -1;
+  private long sgrouphash = -1, colselhash = -1;
 
   /**
    * checks current SelectionGroup against record of last hash value, and
@@ -1252,8 +1278,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   public boolean isColSelChanged(boolean b)
   {
-    return posProps.isColSelChanged(b);
-    /*int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
+    int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
     if (hc != -1 && hc != colselhash)
     {
       if (b)
@@ -1262,7 +1287,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
       }
       return true;
     }
-    return false;*/
+    return false;
   }
 
   @Override
@@ -1282,7 +1307,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   protected boolean showConsensus = true;
 
-  private Map<SequenceI, Color> sequenceColours = new HashMap<SequenceI, Color>();
+  protected boolean showOccupancy = true;
+
+  private Map<SequenceI, Color> sequenceColours = new HashMap<>();
 
   protected SequenceAnnotationOrder sortAnnotationsBy = null;
 
@@ -1293,14 +1320,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   private boolean followHighlight = true;
 
-  /*private int startRes;
-
-  private int endRes;
-
-  private int startSeq;
-
-  private int endSeq;*/
-
   /**
    * Property change listener for changes in alignment
    * 
@@ -1322,7 +1341,10 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public void removePropertyChangeListener(
           java.beans.PropertyChangeListener listener)
   {
-    changeSupport.removePropertyChangeListener(listener);
+    if (changeSupport != null)
+    {
+      changeSupport.removePropertyChangeListener(listener);
+    }
   }
 
   /**
@@ -1343,21 +1365,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   // common hide/show column stuff
 
-  public void hideColumns(int start, int end)
-  {
-    posProps.hideColumns(start, end);
-  }
-
-  public void showColumn(int col)
-  {
-    posProps.showColumn(col);
-  }
-
-  public void showAllHiddenColumns()
-  {
-    posProps.showAllHiddenColumns();
-  }
-
   public void hideSelectedColumns()
   {
     if (colSel.isEmpty())
@@ -1365,47 +1372,75 @@ public abstract class AlignmentViewport implements AlignViewportI,
       return;
     }
 
-    colSel.hideSelectedColumns();
+    colSel.hideSelectedColumns(alignment);
     setSelectionGroup(null);
     isColSelChanged(true);
   }
 
+  public void hideColumns(int start, int end)
+  {
+    if (start == end)
+    {
+      colSel.hideSelectedColumns(start, alignment.getHiddenColumns());
+    }
+    else
+    {
+      alignment.getHiddenColumns().hideColumns(start, end);
+    }
+    isColSelChanged(true);
+  }
 
+  public void showColumn(int col)
+  {
+    alignment.getHiddenColumns().revealHiddenColumns(col, colSel);
+    isColSelChanged(true);
+  }
+
+  public void showAllHiddenColumns()
+  {
+    alignment.getHiddenColumns().revealAllHiddenColumns(colSel);
+    isColSelChanged(true);
+  }
 
   // common hide/show seq stuff
   public void showAllHiddenSeqs()
   {
-    selectionGroup = posProps.showAllHiddenSeqs(selectionGroup);
+    int startSeq = ranges.getStartSeq();
+    int endSeq = ranges.getEndSeq();
 
-    /*    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);
-          }
+    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;
 
-          hiddenRepSequences = null;
+      ranges.setStartEndSeq(startSeq, endSeq + tmp.size());
 
-          firePropertyChange("alignment", null, alignment.getSequences());
-          // used to set hasHiddenRows/hiddenRepSequences here, after the property
-          // changed event
-          sendSelection();
-        }*/
+      firePropertyChange("alignment", null, alignment.getSequences());
+      // used to set hasHiddenRows/hiddenRepSequences here, after the property
+      // changed event
+      sendSelection();
+    }
   }
 
   public void showSequence(int index)
   {
-    posProps.showSequence(index, selectionGroup);
-    /*List<SequenceI> tmp = alignment.getHiddenSequences().showSequence(
-            index, hiddenRepSequences);
+    int startSeq = ranges.getStartSeq();
+    int endSeq = ranges.getEndSeq();
+
+    List<SequenceI> tmp = alignment.getHiddenSequences().showSequence(index,
+            hiddenRepSequences);
     if (tmp.size() > 0)
     {
       if (selectionGroup == null)
@@ -1419,15 +1454,17 @@ public abstract class AlignmentViewport implements AlignViewportI,
         selectionGroup.addSequence(seq, false);
         setSequenceAnnotationsVisible(seq, true);
       }
+
+      ranges.setStartEndSeq(startSeq, endSeq + tmp.size());
+
       firePropertyChange("alignment", null, alignment.getSequences());
       sendSelection();
-    }*/
+    }
   }
 
   public void hideAllSelectedSeqs()
   {
-    posProps.hideAllSelectedSeqs(selectionGroup);
-    /*if (selectionGroup == null || selectionGroup.getSize() < 1)
+    if (selectionGroup == null || selectionGroup.getSize() < 1)
     {
       return;
     }
@@ -1436,21 +1473,26 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
     hideSequence(seqs);
 
-    setSelectionGroup(null);*/
+    setSelectionGroup(null);
   }
 
   public void hideSequence(SequenceI[] seq)
   {
-    posProps.hideSequence(seq);
-    /*if (seq != null)
+    /*
+     * cache offset to first visible sequence
+     */
+    int startSeq = ranges.getStartSeq();
+
+    if (seq != null)
     {
       for (int i = 0; i < seq.length; i++)
       {
         alignment.getHiddenSequences().hideSequence(seq[i]);
         setSequenceAnnotationsVisible(seq[i], false);
       }
+      ranges.setStartSeq(startSeq);
       firePropertyChange("alignment", null, alignment.getSequences());
-    }*/
+    }
   }
 
   /**
@@ -1464,8 +1506,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   public void hideSequences(SequenceI sequence, boolean representGroup)
   {
-    posProps.hideSequences(sequence, representGroup, selectionGroup);
-    /*if (selectionGroup == null || selectionGroup.getSize() < 1)
+    if (selectionGroup == null || selectionGroup.getSize() < 1)
     {
       hideSequence(new SequenceI[] { sequence });
       return;
@@ -1479,19 +1520,38 @@ public abstract class AlignmentViewport implements AlignViewportI,
     }
 
     int gsize = selectionGroup.getSize();
-    SequenceI[] hseqs = selectionGroup.getSequences().toArray(
-            new SequenceI[gsize]);
+    SequenceI[] hseqs = selectionGroup.getSequences()
+            .toArray(new SequenceI[gsize]);
 
     hideSequence(hseqs);
     setSelectionGroup(null);
-    sendSelection();*/
+    sendSelection();
   }
 
+  /**
+   * Set visibility for any annotations for the given sequence.
+   * 
+   * @param sequenceI
+   */
+  protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
+          boolean visible)
+  {
+    AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
+    if (anns != null)
+    {
+      for (AlignmentAnnotation ann : anns)
+      {
+        if (ann.sequenceRef == sequenceI)
+        {
+          ann.visible = visible;
+        }
+      }
+    }
+  }
 
   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
   {
-    posProps.hideRepSequences(repSequence, sg);
-    /*int sSize = sg.getSize();
+    int sSize = sg.getSize();
     if (sSize < 2)
     {
       return;
@@ -1499,7 +1559,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
     if (hiddenRepSequences == null)
     {
-      hiddenRepSequences = new Hashtable<SequenceI, SequenceCollectionI>();
+      hiddenRepSequences = new Hashtable<>();
     }
 
     hiddenRepSequences.put(repSequence, sg);
@@ -1521,7 +1581,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     }
     sg.setSeqrep(repSequence); // note: not done in 2.7applet
     sg.setHidereps(true); // note: not done in 2.7applet
-    hideSequence(seqs);*/
+    hideSequence(seqs);
 
   }
 
@@ -1551,9 +1611,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   public boolean isHiddenRepSequence(SequenceI seq)
   {
-    return posProps.isHiddenRepSequence(seq);
-    // return (hiddenRepSequences != null && hiddenRepSequences
-    // .containsKey(seq));
+    return (hiddenRepSequences != null
+            && hiddenRepSequences.containsKey(seq));
   }
 
   /**
@@ -1564,24 +1623,22 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   public SequenceGroup getRepresentedSequences(SequenceI seq)
   {
-    return posProps.getRepresentedSequences(seq);
-    // return (SequenceGroup) (hiddenRepSequences == null ? null
-    // : hiddenRepSequences.get(seq));
+    return (SequenceGroup) (hiddenRepSequences == null ? null
+            : hiddenRepSequences.get(seq));
   }
 
   @Override
   public int adjustForHiddenSeqs(int alignmentIndex)
   {
-    return posProps.adjustForHiddenSeqs(alignmentIndex);
-    // return alignment.getHiddenSequences().adjustForHiddenSeqs(
-    // alignmentIndex);
+    return alignment.getHiddenSequences()
+            .adjustForHiddenSeqs(alignmentIndex);
   }
 
   @Override
   public void invertColumnSelection()
   {
-    posProps.invertColumnSelection();
-    // colSel.invertColumnSelection(0, alignment.getWidth());
+    colSel.invertColumnSelection(0, alignment.getWidth(), alignment);
+    isColSelChanged(true);
   }
 
   @Override
@@ -1627,13 +1684,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
-  public CigarArray getViewAsCigars(boolean selectedRegionOnly)
-  {
-    return new CigarArray(alignment, posProps.getColumnSelection(),
-            (selectedRegionOnly ? selectionGroup : null));
-  }
-
-  @Override
   public jalview.datamodel.AlignmentView getAlignmentView(
           boolean selectedOnly)
   {
@@ -1644,9 +1694,11 @@ public abstract class AlignmentViewport implements AlignViewportI,
   public jalview.datamodel.AlignmentView getAlignmentView(
           boolean selectedOnly, boolean markGroups)
   {
-    return new AlignmentView(alignment, posProps.getColumnSelection(),
-            selectionGroup, posProps.hasHiddenColumns(), selectedOnly,
-            markGroups);
+    return new AlignmentView(alignment, alignment.getHiddenColumns(),
+            selectionGroup,
+            alignment.getHiddenColumns() != null
+                    && alignment.getHiddenColumns().hasHiddenColumns(),
+            selectedOnly, markGroups);
   }
 
   @Override
@@ -1689,9 +1741,15 @@ public abstract class AlignmentViewport implements AlignViewportI,
     }
 
     selection = new String[iSize];
-    if (posProps.hasHiddenColumns())
+    if (alignment.getHiddenColumns() != null
+            && alignment.getHiddenColumns().hasHiddenColumns())
     {
-      selection = posProps.getVisibleSequenceStrings(start, end, seqs);
+      for (i = 0; i < iSize; i++)
+      {
+        Iterator<int[]> blocks = alignment.getHiddenColumns()
+                .getVisContigsIterator(start, end + 1, false);
+        selection[i] = seqs[i].getSequenceStringFromIterator(blocks);
+      }
     }
     else
     {
@@ -1707,52 +1765,50 @@ public abstract class AlignmentViewport implements AlignViewportI,
   @Override
   public List<int[]> getVisibleRegionBoundaries(int min, int max)
   {
-    return posProps.getVisibleRegionBoundaries(min, max);
-    /*    ArrayList<int[]> regions = new ArrayList<int[]>();
-        int start = min;
-        int end = max;
+    ArrayList<int[]> regions = new ArrayList<>();
+    int start = min;
+    int end = max;
 
-        do
+    do
+    {
+      HiddenColumns hidden = alignment.getHiddenColumns();
+      if (hidden != null && hidden.hasHiddenColumns())
+      {
+        if (start == 0)
         {
-          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;
-            }
-          }
+          start = hidden.visibleToAbsoluteColumn(start);
+        }
+
+        end = hidden.getNextHiddenBoundary(false, start);
+        if (start == end)
+        {
+          end = max;
+        }
+        if (end > max)
+        {
+          end = max;
+        }
+      }
 
-          regions.add(new int[] { start, end });
+      regions.add(new int[] { start, end });
 
-          if (posProps.hasHiddenColumns())
-          {
-            start = colSel.adjustForHiddenColumns(end);
-            start = colSel.getHiddenBoundaryLeft(start) + 1;
-          }
-        } while (end < max);
+      if (hidden != null && hidden.hasHiddenColumns())
+      {
+        start = hidden.visibleToAbsoluteColumn(end);
+        start = hidden.getNextHiddenBoundary(true, start) + 1;
+      }
+    } while (end < max);
 
-        int[][] startEnd = new int[regions.size()][2];
+    int[][] startEnd = new int[regions.size()][2];
 
-        return regions;*/
+    return regions;
   }
 
   @Override
   public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(
           boolean selectedOnly)
   {
-    return posProps.getVisibleAlignmentAnnotation(selectedOnly,
-            selectionGroup);
-    /*ArrayList<AlignmentAnnotation> ala = new ArrayList<AlignmentAnnotation>();
+    ArrayList<AlignmentAnnotation> ala = new ArrayList<>();
     AlignmentAnnotation[] aa;
     if ((aa = alignment.getAlignmentAnnotation()) != null)
     {
@@ -1761,17 +1817,18 @@ public abstract class AlignmentViewport implements AlignViewportI,
         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
         if (selectedOnly && selectionGroup != null)
         {
-          colSel.makeVisibleAnnotation(selectionGroup.getStartRes(),
-                  selectionGroup.getEndRes(), clone);
+          clone.makeVisibleAnnotation(
+                  selectionGroup.getStartRes(), selectionGroup.getEndRes(),
+                  alignment.getHiddenColumns());
         }
         else
         {
-          colSel.makeVisibleAnnotation(clone);
+          clone.makeVisibleAnnotation(alignment.getHiddenColumns());
         }
         ala.add(clone);
       }
     }
-    return ala;*/
+    return ala;
   }
 
   @Override
@@ -1844,7 +1901,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     ResidueShaderI rs = residueShading;
     if (rs != null)
     {
-      rs.alignmentChanged(alignment, posProps.getHiddenRepSequences());
+      rs.alignmentChanged(alignment, hiddenRepSequences);
 
       rs.setConsensus(hconsensus);
       if (rs.conservationApplied())
@@ -1859,7 +1916,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     {
       if (sg.cs != null)
       {
-        sg.cs.alignmentChanged(sg, posProps.getHiddenRepSequences());
+        sg.cs.alignmentChanged(sg, hiddenRepSequences);
       }
       sg.recalcConservation();
     }
@@ -1882,19 +1939,21 @@ public abstract class AlignmentViewport implements AlignViewportI,
       {
         initRNAStructure();
       }
-      consensus = new AlignmentAnnotation("Consensus", "PID",
+      consensus = new AlignmentAnnotation("Consensus",
+              MessageManager.getString("label.consensus_descr"),
               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
       initConsensus(consensus);
+      initGapCounts();
 
       initComplementConsensus();
     }
   }
 
   /**
-   * If this is a protein alignment and there are mappings to cDNA, add the cDNA
-   * consensus annotation.
+   * If this is a protein alignment and there are mappings to cDNA, adds the
+   * cDNA consensus annotation and returns true, else returns false.
    */
-  public void initComplementConsensus()
+  public boolean initComplementConsensus()
   {
     if (!alignment.isNucleotide())
     {
@@ -1918,12 +1977,16 @@ public abstract class AlignmentViewport implements AlignViewportI,
         if (doConsensus)
         {
           complementConsensus = new AlignmentAnnotation("cDNA Consensus",
-                  "PID for cDNA", new Annotation[1], 0f, 100f,
+                  MessageManager
+                          .getString("label.complement_consensus_descr"),
+                  new Annotation[1], 0f, 100f,
                   AlignmentAnnotation.BAR_GRAPH);
           initConsensus(complementConsensus);
+          return true;
         }
       }
     }
+    return false;
   }
 
   private void initConsensus(AlignmentAnnotation aa)
@@ -1937,6 +2000,25 @@ public abstract class AlignmentViewport implements AlignViewportI,
     }
   }
 
+  // these should be extracted from the view model - style and settings for
+  // derived annotation
+  private void initGapCounts()
+  {
+    if (showOccupancy)
+    {
+      gapcounts = new AlignmentAnnotation("Occupancy",
+              MessageManager.getString("label.occupancy_descr"),
+              new Annotation[1], 0f, alignment.getHeight(),
+              AlignmentAnnotation.BAR_GRAPH);
+      gapcounts.hasText = true;
+      gapcounts.autoCalculated = true;
+      gapcounts.scaleColLabel = true;
+      gapcounts.graph = AlignmentAnnotation.BAR_GRAPH;
+
+      alignment.addAnnotation(gapcounts);
+    }
+  }
+
   private void initConservation()
   {
     if (showConservation)
@@ -1944,9 +2026,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
       if (conservation == null)
       {
         conservation = new AlignmentAnnotation("Conservation",
-                "Conservation of total alignment less than "
-                        + getConsPercGaps() + "% gaps", new Annotation[1],
-                0f, 11f, AlignmentAnnotation.BAR_GRAPH);
+                MessageManager.formatMessage("label.conservation_descr",
+                        getConsPercGaps()),
+                new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
         conservation.hasText = true;
         conservation.autoCalculated = true;
         alignment.addAnnotation(conservation);
@@ -1961,7 +2043,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
       if (quality == null)
       {
         quality = new AlignmentAnnotation("Quality",
-                "Alignment Quality based on Blosum62 scores",
+                MessageManager.getString("label.quality_descr"),
                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
         quality.hasText = true;
         quality.autoCalculated = true;
@@ -1974,7 +2056,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
   {
     if (alignment.hasRNAStructure() && strucConsensus == null)
     {
-      strucConsensus = new AlignmentAnnotation("StrucConsensus", "PID",
+      strucConsensus = new AlignmentAnnotation("StrucConsensus",
+              MessageManager.getString("label.strucconsensus_descr"),
               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
       strucConsensus.hasText = true;
       strucConsensus.autoCalculated = true;
@@ -2079,7 +2162,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     // intersect alignment annotation with alignment groups
 
     AlignmentAnnotation[] aan = alignment.getAlignmentAnnotation();
-    List<SequenceGroup> oldrfs = new ArrayList<SequenceGroup>();
+    List<SequenceGroup> oldrfs = new ArrayList<>();
     if (aan != null)
     {
       for (int an = 0; an < aan.length; an++)
@@ -2425,8 +2508,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
     viewStyle = new ViewStyle(settingsForView);
     if (residueShading != null)
     {
-      residueShading.setConservationApplied(settingsForView
-              .isConservationColourSelected());
+      residueShading.setConservationApplied(
+              settingsForView.isConservationColourSelected());
     }
   }
 
@@ -2594,7 +2677,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
     return sortAnnotationsBy;
   }
 
-  public void setSortAnnotationsBy(SequenceAnnotationOrder sortAnnotationsBy)
+  public void setSortAnnotationsBy(
+          SequenceAnnotationOrder sortAnnotationsBy)
   {
     this.sortAnnotationsBy = sortAnnotationsBy;
   }
@@ -2621,94 +2705,63 @@ public abstract class AlignmentViewport implements AlignViewportI,
     viewStyle.setScaleProteinAsCdna(b);
   }
 
-  /**
-   * @return true if view should scroll to show the highlighted region of a
-   *         sequence
-   * @return
-   */
   @Override
-  public final boolean isFollowHighlight()
+  public boolean isProteinFontAsCdna()
   {
-    return followHighlight;
+    return viewStyle.isProteinFontAsCdna();
   }
 
   @Override
-  public final void setFollowHighlight(boolean b)
+  public void setProteinFontAsCdna(boolean b)
   {
-    this.followHighlight = b;
+    viewStyle.setProteinFontAsCdna(b);
   }
 
   @Override
-  public ViewportPositionProps getPosProps()
+  public void setShowComplementFeatures(boolean b)
   {
-    return posProps;
+    viewStyle.setShowComplementFeatures(b);
   }
 
   @Override
-  public int getStartRes()
+  public boolean isShowComplementFeatures()
   {
-    return posProps.getStartRes();
+    return viewStyle.isShowComplementFeatures();
   }
 
   @Override
-  public int getEndRes()
+  public void setShowComplementFeaturesOnTop(boolean b)
   {
-    return posProps.getEndRes();
+    viewStyle.setShowComplementFeaturesOnTop(b);
   }
 
   @Override
-  public int getStartSeq()
-  {
-    return posProps.getStartSeq();
-  }
-
-  public void setStartRes(int res)
-  {
-    posProps.setStartRes(res);
-    // this.startRes = res;
-  }
-
-  public void setStartSeq(int seq)
+  public boolean isShowComplementFeaturesOnTop()
   {
-    posProps.setStartSeq(seq);
-    // this.startSeq = seq;
+    return viewStyle.isShowComplementFeaturesOnTop();
   }
 
-  public void setEndRes(int res)
+  /**
+   * @return true if view should scroll to show the highlighted region of a
+   *         sequence
+   * @return
+   */
+  @Override
+  public final boolean isFollowHighlight()
   {
-    posProps.setEndRes(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;*/
+    return followHighlight;
   }
 
-  public void setEndSeq(int seq)
+  @Override
+  public final void setFollowHighlight(boolean b)
   {
-    posProps.setEndSeq(seq);
-    /*if (seq > alignment.getHeight())
-    {
-      seq = alignment.getHeight();
-    }
-    if (seq < 0)
-    {
-      seq = 0;
-    }
-    this.endSeq = seq;*/
+    this.followHighlight = b;
   }
 
   @Override
-  public int getEndSeq()
+  public ViewportRanges getRanges()
   {
-    return posProps.getEndSeq();
-    // return endSeq;
+    return ranges;
   }
 
   /**
@@ -2727,8 +2780,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
       return 0;
     }
     boolean iAmProtein = !getAlignment().isNucleotide();
-    AlignmentI proteinAlignment = iAmProtein ? getAlignment() : complement
-            .getAlignment();
+    AlignmentI proteinAlignment = iAmProtein ? getAlignment()
+            : complement.getAlignment();
     if (proteinAlignment == null)
     {
       return 0;
@@ -2748,7 +2801,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
      * locate 'middle' column (true middle if an odd number visible, left of
      * middle if an even number visible)
      */
-    int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2;
+    int middleColumn = ranges.getStartRes()
+            + (ranges.getEndRes() - ranges.getStartRes()) / 2;
     final HiddenSequences hiddenSequences = getAlignment()
             .getHiddenSequences();
 
@@ -2758,7 +2812,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
      */
     int lastSeq = alignment.getHeight() - 1;
     List<AlignedCodonFrame> seqMappings = null;
-    for (int seqNo = getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++)
+    for (int seqNo = ranges
+            .getStartSeq(); seqNo <= lastSeq; seqNo++, seqOffset++)
     {
       sequence = getAlignment().getSequenceAt(seqNo);
       if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
@@ -2769,9 +2824,9 @@ public abstract class AlignmentViewport implements AlignViewportI,
       {
         continue;
       }
-      seqMappings = MappingUtils
-              .findMappingsForSequenceAndOthers(sequence, mappings,
-                      getCodingComplement().getAlignment().getSequences());
+      seqMappings = MappingUtils.findMappingsForSequenceAndOthers(sequence,
+              mappings,
+              getCodingComplement().getAlignment().getSequences());
       if (!seqMappings.isEmpty())
       {
         break;
@@ -2800,11 +2855,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
   {
-    if (!this.hasSelectedColumns())
-    {
-      posProps.expandColSelection(sg, wholewidth);
-    }
-    /*int sgs, sge;
+    int sgs, sge;
     if (sg != null && (sgs = sg.getStartRes()) >= 0
             && sg.getStartRes() <= (sge = sg.getEndRes())
             && !this.hasSelectedColumns())
@@ -2822,7 +2873,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
       {
         colSel.addElement(cspos);
       }
-    }*/
+    }
   }
 
   /**
@@ -2830,7 +2881,6 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   private boolean selectionIsDefinedGroup = false;
 
-
   @Override
   public boolean isSelectionDefinedGroup()
   {
@@ -2851,8 +2901,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
         selectionIsDefinedGroup = gps.contains(selectionGroup);
       }
     }
-    return selectionGroup.getContext() == alignment
-            || selectionIsDefinedGroup;
+    return selectionGroup.isDefined() || selectionIsDefinedGroup;
   }
 
   /**
@@ -2860,6 +2909,8 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   private SearchResultsI searchResults = null;
 
+  protected TreeModel currentTree = null;
+
   @Override
   public boolean hasSearchResults()
   {
@@ -2877,4 +2928,121 @@ public abstract class AlignmentViewport implements AlignViewportI,
   {
     return searchResults;
   }
+
+  /**
+   * get the consensus sequence as displayed under the PID consensus annotation
+   * row.
+   * 
+   * @return consensus sequence as a new sequence object
+   */
+  public SequenceI getConsensusSeq()
+  {
+    if (consensus == null)
+    {
+      updateConsensus(null);
+    }
+    if (consensus == null)
+    {
+      return null;
+    }
+    StringBuffer seqs = new StringBuffer();
+    for (int i = 0; i < consensus.annotations.length; i++)
+    {
+      Annotation annotation = consensus.annotations[i];
+      if (annotation != null)
+      {
+        String description = annotation.description;
+        if (description != null && description.startsWith("["))
+        {
+          // consensus is a tie - just pick the first one
+          seqs.append(description.charAt(1));
+        }
+        else
+        {
+          seqs.append(annotation.displayCharacter);
+        }
+      }
+    }
+
+    SequenceI sq = new Sequence("Consensus", seqs.toString());
+    sq.setDescription("Percentage Identity Consensus "
+            + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
+    return sq;
+  }
+
+  @Override
+  public void setCurrentTree(TreeModel tree)
+  {
+    currentTree = tree;
+  }
+
+  @Override
+  public TreeModel getCurrentTree()
+  {
+    return currentTree;
+  }
+
+  /**
+   * flag set to indicate if structure views might be out of sync with sequences
+   * in the alignment
+   */
+
+  private boolean needToUpdateStructureViews = false;
+
+  @Override
+  public boolean isUpdateStructures()
+  {
+    return needToUpdateStructureViews;
+  }
+
+  @Override
+  public void setUpdateStructures(boolean update)
+  {
+    needToUpdateStructureViews = update;
+  }
+
+  @Override
+  public boolean needToUpdateStructureViews()
+  {
+    boolean update = needToUpdateStructureViews;
+    needToUpdateStructureViews = false;
+    return update;
+  }
+
+  @Override
+  public void addSequenceGroup(SequenceGroup sequenceGroup)
+  {
+    alignment.addGroup(sequenceGroup);
+
+    Color col = sequenceGroup.idColour;
+    if (col != null)
+    {
+      col = col.brighter();
+
+      for (SequenceI sq : sequenceGroup.getSequences())
+      {
+        setSequenceColour(sq, col);
+      }
+    }
+
+    if (codingComplement != null)
+    {
+      SequenceGroup mappedGroup = MappingUtils
+              .mapSequenceGroup(sequenceGroup, this, codingComplement);
+      if (mappedGroup.getSequences().size() > 0)
+      {
+        codingComplement.getAlignment().addGroup(mappedGroup);
+
+        if (col != null)
+        {
+          for (SequenceI seq : mappedGroup.getSequences())
+          {
+            codingComplement.setSequenceColour(seq, col);
+          }
+        }
+      }
+      // propagate the structure view update flag according to our own setting
+      codingComplement.setUpdateStructures(needToUpdateStructureViews);
+    }
+  }
 }