JAL-4392 Consensus secondary structure: Display Secondary structure consensus for...
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
index 3702cd0..ccf6379 100644 (file)
  */
 package jalview.viewmodel;
 
+import java.awt.Color;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.Conservation;
+import jalview.analysis.TreeModel;
 import jalview.api.AlignCalcManagerI;
+import jalview.api.AlignExportSettingsI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeaturesDisplayedI;
 import jalview.api.ViewStyleI;
+import jalview.bin.Console;
 import jalview.commands.CommandI;
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentExportData;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.Annotation;
-import jalview.datamodel.CigarArray;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.ContactListI;
+import jalview.datamodel.ContactMatrixI;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.HiddenSequences;
 import jalview.datamodel.ProfilesI;
@@ -43,6 +60,8 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
+import jalview.gui.QuitHandler;
+import jalview.project.Jalview2XML;
 import jalview.renderer.ResidueShader;
 import jalview.renderer.ResidueShaderI;
 import jalview.schemes.ColourSchemeI;
@@ -57,19 +76,9 @@ import jalview.viewmodel.styles.ViewStyle;
 import jalview.workers.AlignCalcManager;
 import jalview.workers.ComplementConsensusThread;
 import jalview.workers.ConsensusThread;
+import jalview.workers.SecondaryStructureConsensusThread;
 import jalview.workers.StrucConsensusThread;
 
-import java.awt.Color;
-import java.beans.PropertyChangeSupport;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
 /**
  * base class holding visualization and analysis attributes and common logic for
  * an active alignment view displayed in the GUI
@@ -80,7 +89,7 @@ import java.util.Map;
 public abstract class AlignmentViewport
         implements AlignViewportI, CommandListener, VamsasSource
 {
-  final protected ViewportRanges ranges;
+  protected ViewportRanges ranges;
 
   protected ViewStyleI viewStyle = new ViewStyle();
 
@@ -97,6 +106,11 @@ public abstract class AlignmentViewport
   protected Deque<CommandI> redoList = new ArrayDeque<>();
 
   /**
+   * used to determine if quit should be confirmed
+   */
+  private boolean savedUpToDate = false;
+
+  /**
    * alignment displayed in the viewport. Please use get/setter
    */
   protected AlignmentI alignment;
@@ -661,7 +675,7 @@ public abstract class AlignmentViewport
          * 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,
@@ -684,6 +698,8 @@ public abstract class AlignmentViewport
   }
 
   protected AlignmentAnnotation consensus;
+  
+  protected AlignmentAnnotation secondaryStructureConsensus;
 
   protected AlignmentAnnotation complementConsensus;
 
@@ -696,6 +712,8 @@ public abstract class AlignmentViewport
   protected AlignmentAnnotation quality;
 
   protected AlignmentAnnotation[] groupConsensus;
+  
+  protected AlignmentAnnotation[] groupSSConsensus;
 
   protected AlignmentAnnotation[] groupConservation;
 
@@ -703,17 +721,21 @@ public abstract class AlignmentViewport
    * results of alignment consensus analysis for visible portion of view
    */
   protected ProfilesI hconsensus = null;
+  
+  protected ProfilesI hSSConsensus = null;
+  
+  
 
   /**
    * results of cDNA complement consensus visible portion of view
    */
-  protected Hashtable[] hcomplementConsensus = null;
+  protected Hashtable<String, Object>[] hcomplementConsensus = null;
 
   /**
    * results of secondary structure base pair consensus for visible portion of
    * view
    */
-  protected Hashtable[] hStrucConsensus = null;
+  protected Hashtable<String, Object>[] hStrucConsensus = null;
 
   protected Conservation hconservation = null;
 
@@ -740,9 +762,16 @@ public abstract class AlignmentViewport
   {
     this.hconsensus = hconsensus;
   }
-
+  
   @Override
-  public void setComplementConsensusHash(Hashtable[] hconsensus)
+  public void setSequenceSSConsensusHash(ProfilesI hSSConsensus)
+  {
+    this.hSSConsensus = hSSConsensus;
+  }
+  
+  @Override
+  public void setComplementConsensusHash(
+          Hashtable<String, Object>[] hconsensus)
   {
     this.hcomplementConsensus = hconsensus;
   }
@@ -752,21 +781,28 @@ public abstract class AlignmentViewport
   {
     return hconsensus;
   }
+  
+  @Override
+  public ProfilesI getSequenceSSConsensusHash()
+  {
+    return hSSConsensus;
+  }
 
   @Override
-  public Hashtable[] getComplementConsensusHash()
+  public Hashtable<String, Object>[] getComplementConsensusHash()
   {
     return hcomplementConsensus;
   }
 
   @Override
-  public Hashtable[] getRnaStructureConsensusHash()
+  public Hashtable<String, Object>[] getRnaStructureConsensusHash()
   {
     return hStrucConsensus;
   }
 
   @Override
-  public void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus)
+  public void setRnaStructureConsensusHash(
+          Hashtable<String, Object>[] hStrucConsensus)
   {
     this.hStrucConsensus = hStrucConsensus;
 
@@ -790,6 +826,14 @@ public abstract class AlignmentViewport
     return consensus;
   }
 
+  
+  @Override
+  public AlignmentAnnotation getAlignmentSecondaryStructureConsensusAnnotation()
+  {
+    return secondaryStructureConsensus;
+  }
+  
+
   @Override
   public AlignmentAnnotation getAlignmentGapAnnotation()
   {
@@ -881,6 +925,26 @@ public abstract class AlignmentViewport
       }
     }
   }
+  
+  
+  
+
+  /**
+   * trigger update of consensus annotation
+   */
+  public void updateSecondaryStructureConsensus(final AlignmentViewPanel ap)
+  {
+    // see note in mantis : issue number 8585
+    if (secondaryStructureConsensus == null || !autoCalculateConsensus)
+    {
+      return;
+    }
+    if (calculator
+            .getRegisteredWorkersOfClass(SecondaryStructureConsensusThread.class) == null)
+    {
+      calculator.registerWorker(new SecondaryStructureConsensusThread(this, ap));
+    }
+  }
 
   // --------START Structure Conservation
   public void updateStrucConsensus(final AlignmentViewPanel ap)
@@ -919,7 +983,8 @@ public abstract class AlignmentViewport
     }
     if (calculator.workingInvolvedWith(alignmentAnnotation))
     {
-      // System.err.println("grey out ("+alignmentAnnotation.label+")");
+      // jalview.bin.Console.errPrintln("grey out
+      // ("+alignmentAnnotation.label+")");
       return true;
     }
     return false;
@@ -943,16 +1008,22 @@ public abstract class AlignmentViewport
     consensus = null;
     complementConsensus = null;
     strucConsensus = null;
+    secondaryStructureConsensus = null;
     conservation = null;
     quality = null;
     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);
   }
 
@@ -984,6 +1055,8 @@ public abstract class AlignmentViewport
    * should consensus profile be rendered by default
    */
   protected boolean showSequenceLogo = false;
+  
+  protected boolean showSequenceSSLogo = false;
 
   /**
    * should consensus profile be rendered normalised to row height
@@ -994,6 +1067,13 @@ public abstract class AlignmentViewport
    * should consensus histograms be rendered by default
    */
   protected boolean showConsensusHistogram = true;
+  
+  protected boolean showSSConsensusHistogram = true;
+
+  public void setShowSSConsensusHistogram(boolean showSSConsensusHistogram)
+  {
+    this.showSSConsensusHistogram = showSSConsensusHistogram;
+  }
 
   /**
    * @return the showConsensusProfile
@@ -1003,6 +1083,12 @@ public abstract class AlignmentViewport
   {
     return showSequenceLogo;
   }
+  
+  @Override
+  public boolean isShowSequenceSSLogo()
+  {
+    return showSequenceSSLogo;
+  }
 
   /**
    * @param showSequenceLogo
@@ -1021,6 +1107,18 @@ public abstract class AlignmentViewport
     }
     this.showSequenceLogo = showSequenceLogo;
   }
+  
+  public void setShowSequenceSSLogo(boolean showSequenceSSLogo)
+  {
+    if (showSequenceSSLogo != this.showSequenceSSLogo)
+    {
+      // TODO: decouple settings setting from calculation when refactoring
+      // annotation update method from alignframe to viewport
+      this.showSequenceSSLogo = showSequenceSSLogo;
+      calculator.updateAnnotationFor(SecondaryStructureConsensusThread.class);
+    }
+    this.showSequenceSSLogo = showSequenceSSLogo;
+  }
 
   /**
    * @param showConsensusHistogram
@@ -1075,6 +1173,12 @@ public abstract class AlignmentViewport
   {
     return this.showConsensusHistogram;
   }
+  
+  @Override
+  public boolean isShowSSConsensusHistogram()
+  {
+    return this.showSSConsensusHistogram;
+  }
 
   /**
    * when set, updateAlignment will always ensure sequences are of equal length
@@ -1187,7 +1291,7 @@ public abstract class AlignmentViewport
   {
     if (sequenceSetID != null)
     {
-      System.err.println(
+      jalview.bin.Console.errPrintln(
               "Warning - overwriting a sequenceSetId for a viewport!");
     }
     sequenceSetID = new String(newid);
@@ -1226,6 +1330,7 @@ public abstract class AlignmentViewport
     if (ap != null)
     {
       updateConsensus(ap);
+      updateSecondaryStructureConsensus(ap);
       if (residueShading != null)
       {
         residueShading.setThreshold(residueShading.getThreshold(),
@@ -1334,7 +1439,10 @@ public abstract class AlignmentViewport
   public void removePropertyChangeListener(
           java.beans.PropertyChangeListener listener)
   {
-    changeSupport.removePropertyChangeListener(listener);
+    if (changeSupport != null)
+    {
+      changeSupport.removePropertyChangeListener(listener);
+    }
   }
 
   /**
@@ -1628,6 +1736,7 @@ public abstract class AlignmentViewport
   public void invertColumnSelection()
   {
     colSel.invertColumnSelection(0, alignment.getWidth(), alignment);
+    isColSelChanged(true);
   }
 
   @Override
@@ -1673,13 +1782,6 @@ public abstract class AlignmentViewport
   }
 
   @Override
-  public CigarArray getViewAsCigars(boolean selectedRegionOnly)
-  {
-    return new CigarArray(alignment, alignment.getHiddenColumns(),
-            (selectedRegionOnly ? selectionGroup : null));
-  }
-
-  @Override
   public jalview.datamodel.AlignmentView getAlignmentView(
           boolean selectedOnly)
   {
@@ -1740,8 +1842,12 @@ public abstract class AlignmentViewport
     if (alignment.getHiddenColumns() != null
             && alignment.getHiddenColumns().hasHiddenColumns())
     {
-      selection = alignment.getHiddenColumns()
-              .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
     {
@@ -1768,10 +1874,10 @@ public abstract class AlignmentViewport
       {
         if (start == 0)
         {
-          start = hidden.adjustForHiddenColumns(start);
+          start = hidden.visibleToAbsoluteColumn(start);
         }
 
-        end = hidden.getHiddenBoundaryRight(start);
+        end = hidden.getNextHiddenBoundary(false, start);
         if (start == end)
         {
           end = max;
@@ -1786,12 +1892,12 @@ public abstract class AlignmentViewport
 
       if (hidden != null && hidden.hasHiddenColumns())
       {
-        start = hidden.adjustForHiddenColumns(end);
-        start = hidden.getHiddenBoundaryLeft(start) + 1;
+        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;
   }
@@ -1809,13 +1915,12 @@ public abstract class AlignmentViewport
         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
         if (selectedOnly && selectionGroup != null)
         {
-          alignment.getHiddenColumns().makeVisibleAnnotation(
-                  selectionGroup.getStartRes(), selectionGroup.getEndRes(),
-                  clone);
+          clone.makeVisibleAnnotation(selectionGroup.getStartRes(),
+                  selectionGroup.getEndRes(), alignment.getHiddenColumns());
         }
         else
         {
-          alignment.getHiddenColumns().makeVisibleAnnotation(clone);
+          clone.makeVisibleAnnotation(alignment.getHiddenColumns());
         }
         ala.add(clone);
       }
@@ -1851,6 +1956,7 @@ public abstract class AlignmentViewport
     if (autoCalculateConsensus)
     {
       updateConsensus(ap);
+      updateSecondaryStructureConsensus(ap);
     }
     if (hconsensus != null && autoCalculateConsensus)
     {
@@ -1934,13 +2040,19 @@ public abstract class AlignmentViewport
       consensus = new AlignmentAnnotation("Consensus",
               MessageManager.getString("label.consensus_descr"),
               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
+      
+      secondaryStructureConsensus = new AlignmentAnnotation("SecondaryStructureConsensus",
+              MessageManager.getString("label.ssconsensus_descr"),
+              new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
+      
       initConsensus(consensus);
+      initSSConsensus(secondaryStructureConsensus);
       initGapCounts();
-
       initComplementConsensus();
     }
   }
 
+
   /**
    * If this is a protein alignment and there are mappings to cDNA, adds the
    * cDNA consensus annotation and returns true, else returns false.
@@ -1991,6 +2103,17 @@ public abstract class AlignmentViewport
       alignment.addAnnotation(aa);
     }
   }
+  
+  private void initSSConsensus(AlignmentAnnotation aa)
+  {
+    aa.hasText = true;
+    aa.autoCalculated = true;
+
+    if (showConsensus)
+    {
+      alignment.addAnnotation(aa);
+    }
+  }
 
   // these should be extracted from the view model - style and settings for
   // derived annotation
@@ -2080,7 +2203,7 @@ public abstract class AlignmentViewport
       {
         if (aa == null)
         {
-          System.err.println("Null annotation row: ignoring.");
+          jalview.bin.Console.errPrintln("Null annotation row: ignoring.");
           continue;
         }
         if (!aa.visible)
@@ -2112,7 +2235,7 @@ public abstract class AlignmentViewport
 
         if (aa.graph > 0)
         {
-          aa.height += aa.graphHeight;
+          aa.height += aa.graphHeight + 20;
         }
 
         if (aa.height == 0)
@@ -2139,14 +2262,16 @@ public abstract class AlignmentViewport
     boolean conv = isShowGroupConservation();
     boolean cons = isShowGroupConsensus();
     boolean showprf = isShowSequenceLogo();
+    boolean showSSprf = isShowSequenceSSLogo();
     boolean showConsHist = isShowConsensusHistogram();
+    boolean showSSConsHist = isShowSSConsensusHistogram();
     boolean normLogo = isNormaliseSequenceLogo();
 
     /**
      * TODO reorder the annotation rows according to group/sequence ordering on
      * alignment
      */
-    boolean sortg = true;
+    // boolean sortg = true;
 
     // remove old automatic annotation
     // add any new annotation
@@ -2176,7 +2301,9 @@ public abstract class AlignmentViewport
         {
           // set defaults for this group's conservation/consensus
           sg.setshowSequenceLogo(showprf);
+          sg.setshowSequenceSSLogo(showSSprf);
           sg.setShowConsensusHistogram(showConsHist);
+          sg.setShowSSConsensusHistogram(showSSConsHist);
           sg.setNormaliseSequenceLogo(normLogo);
         }
         if (conv)
@@ -2188,6 +2315,7 @@ public abstract class AlignmentViewport
         {
           updateCalcs = true;
           alignment.addAnnotation(sg.getConsensus(), 0);
+          alignment.addAnnotation(sg.getSSConsensus(), 0);
         }
         // refresh the annotation rows
         if (updateCalcs)
@@ -2256,7 +2384,7 @@ public abstract class AlignmentViewport
   public void clearSequenceColours()
   {
     sequenceColours.clear();
-  };
+  }
 
   @Override
   public AlignViewportI getCodingComplement()
@@ -2273,7 +2401,8 @@ public abstract class AlignmentViewport
   {
     if (this == av)
     {
-      System.err.println("Ignoring recursive setCodingComplement request");
+      jalview.bin.Console
+              .errPrintln("Ignoring recursive setCodingComplement request");
     }
     else
     {
@@ -2604,6 +2733,8 @@ public abstract class AlignmentViewport
     {
       this.historyList.push(command);
       broadcastCommand(command, false);
+      setSavedUpToDate(false);
+      Jalview2XML.setStateSavedUpToDate(false);
     }
   }
 
@@ -2709,6 +2840,30 @@ public abstract class AlignmentViewport
     viewStyle.setProteinFontAsCdna(b);
   }
 
+  @Override
+  public void setShowComplementFeatures(boolean b)
+  {
+    viewStyle.setShowComplementFeatures(b);
+  }
+
+  @Override
+  public boolean isShowComplementFeatures()
+  {
+    return viewStyle.isShowComplementFeatures();
+  }
+
+  @Override
+  public void setShowComplementFeaturesOnTop(boolean b)
+  {
+    viewStyle.setShowComplementFeaturesOnTop(b);
+  }
+
+  @Override
+  public boolean isShowComplementFeaturesOnTop()
+  {
+    return viewStyle.isShowComplementFeaturesOnTop();
+  }
+
   /**
    * @return true if view should scroll to show the highlighted region of a
    *         sequence
@@ -2781,7 +2936,7 @@ public abstract class AlignmentViewport
     int lastSeq = alignment.getHeight() - 1;
     List<AlignedCodonFrame> seqMappings = null;
     for (int seqNo = ranges
-            .getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++)
+            .getStartSeq(); seqNo <= lastSeq; seqNo++, seqOffset++)
     {
       sequence = getAlignment().getSequenceAt(seqNo);
       if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
@@ -2877,6 +3032,8 @@ public abstract class AlignmentViewport
    */
   private SearchResultsI searchResults = null;
 
+  protected TreeModel currentTree = null;
+
   @Override
   public boolean hasSearchResults()
   {
@@ -2895,6 +3052,19 @@ public abstract class AlignmentViewport
     return searchResults;
   }
 
+  @Override
+  public ContactListI getContactList(AlignmentAnnotation _aa, int column)
+  {
+    return alignment.getContactListFor(_aa, column);
+  }
+
+  @Override
+  public ContactMatrixI getContactMatrix(
+          AlignmentAnnotation alignmentAnnotation)
+  {
+    return alignment.getContactMatrixFor(alignmentAnnotation);
+  }
+
   /**
    * get the consensus sequence as displayed under the PID consensus annotation
    * row.
@@ -2935,4 +3105,184 @@ public abstract class AlignmentViewport
             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
     return sq;
   }
+  
+  public SequenceI getSSConsensusSeq()
+  {
+    if (secondaryStructureConsensus == null)
+    {
+      updateSecondaryStructureConsensus(null);
+    }
+    if (secondaryStructureConsensus == null)
+    {
+      return null;
+    }
+    StringBuffer seqs = new StringBuffer();
+    for (int i = 0; i < secondaryStructureConsensus.annotations.length; i++)
+    {
+      Annotation annotation = secondaryStructureConsensus.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("Sec Str Consensus", seqs.toString());
+    sq.setDescription("Percentage Identity Sec Str Consensus "
+            + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
+    return sq;
+  }
+
+  @Override
+  public void setCurrentTree(TreeModel tree)
+  {
+    currentTree = tree;
+  }
+
+  @Override
+  public TreeModel getCurrentTree()
+  {
+    return currentTree;
+  }
+
+  @Override
+  public AlignmentExportData getAlignExportData(
+          AlignExportSettingsI options)
+  {
+    AlignmentI alignmentToExport = null;
+    String[] omitHidden = null;
+    alignmentToExport = null;
+
+    if (hasHiddenColumns() && !options.isExportHiddenColumns())
+    {
+      omitHidden = getViewAsString(false,
+              options.isExportHiddenSequences());
+    }
+
+    int[] alignmentStartEnd = new int[2];
+    if (hasHiddenRows() && options.isExportHiddenSequences())
+    {
+      alignmentToExport = getAlignment().getHiddenSequences()
+              .getFullAlignment();
+    }
+    else
+    {
+      alignmentToExport = getAlignment();
+    }
+    alignmentStartEnd = getAlignment().getHiddenColumns()
+            .getVisibleStartAndEndIndex(alignmentToExport.getWidth());
+    AlignmentExportData ed = new AlignmentExportData(alignmentToExport,
+            omitHidden, alignmentStartEnd);
+    return ed;
+  }
+
+  /**
+   * 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);
+    }
+  }
+
+  @Override
+  public Iterator<int[]> getViewAsVisibleContigs(boolean selectedRegionOnly)
+  {
+    int start = 0;
+    int end = 0;
+    if (selectedRegionOnly && selectionGroup != null)
+    {
+      start = selectionGroup.getStartRes();
+      end = selectionGroup.getEndRes() + 1;
+    }
+    else
+    {
+      end = alignment.getWidth();
+    }
+    return (alignment.getHiddenColumns().getVisContigsIterator(start, end,
+            false));
+  }
+
+  public void setSavedUpToDate(boolean s)
+  {
+    setSavedUpToDate(s, QuitHandler.Message.UNSAVED_CHANGES);
+  }
+
+  public void setSavedUpToDate(boolean s, QuitHandler.Message m)
+  {
+    Console.debug(
+            "Setting " + this.getViewId() + " setSavedUpToDate to " + s);
+    savedUpToDate = s;
+    QuitHandler.setMessage(m);
+  }
+
+  public boolean savedUpToDate()
+  {
+    Console.debug("Returning " + this.getViewId() + " savedUpToDate value: "
+            + savedUpToDate);
+    return savedUpToDate;
+  }
 }