JAL-2136 merged develop into branch to fix build failure
authortcofoegbu <tcnofoegbu@dundee.ac.uk>
Tue, 18 Apr 2017 10:49:54 +0000 (11:49 +0100)
committertcofoegbu <tcnofoegbu@dundee.ac.uk>
Tue, 18 Apr 2017 10:49:54 +0000 (11:49 +0100)
17 files changed:
1  2 
resources/lang/Messages.properties
src/MCview/AppletPDBCanvas.java
src/MCview/PDBCanvas.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AppletJmolBinding.java
src/jalview/appletgui/ExtJmol.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/jmol/JmolCommands.java
src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AppJmolBinding.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/Jalview2XML.java
src/jalview/javascript/MouseOverStructureListener.java
src/jalview/structures/models/AAStructureBindingModel.java
test/jalview/structures/models/AAStructureBindingModelTest.java
test/jalview/ws/sifts/SiftsClientTest.java

@@@ -3,6 -3,9 +3,9 @@@ action.reset_services = Reset Service
  action.merge_results = Merge Results
  action.load_scheme = Load scheme
  action.save_scheme = Save scheme
+ label.scheme_changed = Changes to scheme {0} have not been saved.<br><br>Save changes, or continue without saving to make a new colour scheme.
+ label.save_changes = Save Changes
+ label.dont_save_changes = Don't Save
  action.save_image = Save Image
  action.paste = Paste
  action.show_html_source = Show HTML Source
@@@ -68,7 -71,6 +71,6 @@@ action.show_gaps = Show Gap
  action.show_hidden_markers = Show Hidden Markers
  action.find = Find
  action.undefine_groups = Undefine Groups
- action.create_groups = Create Groups
  action.make_groups_selection = Make Groups For Selection
  action.copy = Copy
  action.cut = Cut
@@@ -669,8 -671,7 +671,7 @@@ label.2d_rna_sequence_name = 2D RNA - {
  label.edit_name_and_description_current_group = Edit name and description of current group
  label.from_file = From File
  label.enter_pdb_id = Enter PDB Id (or pdbid:chaincode)
- label.text_colour = Text Colour
- action.set_text_colour = Text Colour...
+ label.text_colour = Text Colour...
  label.structure = Structure
  label.show_pdbstruct_dialog = 3D Structure Data...
  label.view_rna_structure = VARNA 2D Structure
@@@ -1295,10 -1296,4 +1296,10 @@@ label.edit_sequence_url_link = Edit seq
  warn.name_cannot_be_duplicate = User-defined URL names must be unique and cannot be MIRIAM ids
  label.invalid_name = Invalid Name !
  label.output_seq_details = Output Sequence Details to list all database references
 +<<<<<<< HEAD
 +label.phyre2_model_prediction = 3D Protein Model prediction with Phyre2
 +label.run_phyre2_prediction = Run Phyre2 Prediction
 +status.obtaining_mapping_with_phyre2_template_alignment = Obtaining mapping with Phyre2 Template alignment 
 +=======
  label.urllinks = Links
 +>>>>>>> develop
@@@ -28,6 -28,7 +28,7 @@@ import jalview.datamodel.PDBEntry
  import jalview.datamodel.SequenceI;
  import jalview.io.DataSourceType;
  import jalview.io.StructureFile;
+ import jalview.renderer.seqfeatures.FeatureColourFinder;
  import jalview.structure.AtomSpec;
  import jalview.structure.StructureListener;
  import jalview.structure.StructureMapping;
@@@ -158,7 -159,7 +159,7 @@@ public class AppletPDBCanvas extends Pa
  
      try
      {
 -      pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol);
 +      pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol, null);
  
        if (protocol == DataSourceType.PASTE)
        {
        showFeatures = true;
      }
  
+     FeatureColourFinder finder = new FeatureColourFinder(fr);
      PDBChain chain;
      if (bysequence && pdb != null)
      {
                  if (pos > 0)
                  {
                    pos = sequence[s].findIndex(pos);
-                   tmp.startCol = sr.getResidueBoxColour(sequence[s], pos);
-                   if (showFeatures)
-                   {
-                     tmp.startCol = fr.findFeatureColour(tmp.startCol,
-                             sequence[s], pos);
-                   }
+                   tmp.startCol = sr.getResidueColour(sequence[s], pos,
+                           finder);
                  }
                  pos = mapping[m].getSeqPos(tmp.at2.resNumber) - 1;
                  if (pos > 0)
                  {
                    pos = sequence[s].findIndex(pos);
-                   tmp.endCol = sr.getResidueBoxColour(sequence[s], pos);
-                   if (showFeatures)
-                   {
-                     tmp.endCol = fr.findFeatureColour(tmp.endCol,
-                             sequence[s], pos);
-                   }
+                   tmp.endCol = sr
+                           .getResidueColour(sequence[s], pos, finder);
                  }
                }
              }
            }
    // ////////////////////////////////
    // /StructureListener
    @Override
 -  public String[] getPdbFile()
 +  public String[] getStructureFiles()
    {
      return new String[] { pdbentry.getFile() };
    }
@@@ -28,6 -28,7 +28,7 @@@ import jalview.gui.FeatureRenderer
  import jalview.gui.SequenceRenderer;
  import jalview.io.DataSourceType;
  import jalview.io.StructureFile;
+ import jalview.renderer.seqfeatures.FeatureColourFinder;
  import jalview.structure.AtomSpec;
  import jalview.structure.StructureListener;
  import jalview.structure.StructureMapping;
@@@ -152,8 -153,7 +153,8 @@@ public class PDBCanvas extends JPanel i
  
      try
      {
 -      pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol);
 +      pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol,
 +              ap.alignFrame);
  
        if (protocol.equals(jalview.io.DataSourceType.PASTE))
        {
        showFeatures = true;
      }
  
+     FeatureColourFinder finder = new FeatureColourFinder(fr);
      PDBChain chain;
      if (bysequence && pdb != null)
      {
                  if (pos > 0)
                  {
                    pos = sequence[s].findIndex(pos);
-                   tmp.startCol = sr.getResidueBoxColour(sequence[s], pos);
-                   if (showFeatures)
-                   {
-                     tmp.startCol = fr.findFeatureColour(tmp.startCol,
-                             sequence[s], pos);
-                   }
+                   tmp.startCol = sr.getResidueColour(sequence[s], pos,
+                           finder);
                  }
                  pos = mapping[m].getSeqPos(tmp.at2.resNumber) - 1;
                  if (pos > 0)
                  {
                    pos = sequence[s].findIndex(pos);
-                   tmp.endCol = sr.getResidueBoxColour(sequence[s], pos);
-                   if (showFeatures)
-                   {
-                     tmp.endCol = fr.findFeatureColour(tmp.endCol,
-                             sequence[s], pos);
-                   }
+                   tmp.endCol = sr
+                           .getResidueColour(sequence[s], pos, finder);
                  }
  
                }
    // ////////////////////////////////
    // /StructureListener
    @Override
 -  public String[] getPdbFile()
 +  public String[] getStructureFiles()
    {
      return new String[] { pdbentry.getFile() };
    }
@@@ -75,6 -75,7 +75,7 @@@ import jalview.structures.models.AAStru
  import jalview.util.MappingUtils;
  import jalview.util.MessageManager;
  import jalview.viewmodel.AlignmentViewport;
+ import jalview.viewmodel.ViewportRanges;
  
  import java.awt.BorderLayout;
  import java.awt.Canvas;
@@@ -420,6 -421,8 +421,8 @@@ public class AlignFrame extends Embmenu
    @Override
    public void keyPressed(KeyEvent evt)
    {
+     ViewportRanges ranges = viewport.getRanges();
      if (viewport.cursorMode
              && ((evt.getKeyCode() >= KeyEvent.VK_0 && evt.getKeyCode() <= KeyEvent.VK_9) || (evt
                      .getKeyCode() >= KeyEvent.VK_NUMPAD0 && evt
                new String[] { (viewport.cursorMode ? "on" : "off") }));
        if (viewport.cursorMode)
        {
-         alignPanel.seqPanel.seqCanvas.cursorX = viewport.startRes;
-         alignPanel.seqPanel.seqCanvas.cursorY = viewport.startSeq;
+         alignPanel.seqPanel.seqCanvas.cursorX = ranges.getStartRes();
+         alignPanel.seqPanel.seqCanvas.cursorY = ranges.getStartSeq();
        }
        break;
  
        }
        else
        {
-         alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
-                 - viewport.endSeq + viewport.startSeq);
+         alignPanel.setScrollValues(ranges.getStartRes(),
+                 2 * ranges.getStartSeq() - ranges.getEndSeq());
        }
        break;
  
        }
        else
        {
-         alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
-                 + viewport.endSeq - viewport.startSeq);
+         alignPanel
+                 .setScrollValues(ranges.getStartRes(), ranges.getEndSeq());
        }
        break;
  
      {
        delete_actionPerformed();
      }
+     else if (source == createGroup)
+     {
+       createGroup_actionPerformed();
+     }
+     else if (source == unGroup)
+     {
+       unGroup_actionPerformed();
+     }
      else if (source == grpsFromSelection)
      {
        makeGrpsFromSelection_actionPerformed();
              seqs, 0, viewport.getAlignment().getWidth(),
              viewport.getAlignment()));
  
-     viewport.setEndSeq(viewport.getAlignment().getHeight());
+     viewport.getRanges().setEndSeq(viewport.getAlignment().getHeight());
      viewport.getAlignment().getWidth();
      viewport.firePropertyChange("alignment", null, viewport.getAlignment()
              .getSequences());
  
    void trimAlignment(boolean trimLeft)
    {
+     AlignmentI al = viewport.getAlignment();
+     ViewportRanges ranges = viewport.getRanges();
      ColumnSelection colSel = viewport.getColumnSelection();
      int column;
  
        }
        else
        {
-         seqs = viewport.getAlignment().getSequencesArray();
+         seqs = al.getSequencesArray();
        }
  
        TrimRegionCommand trimRegion;
        if (trimLeft)
        {
          trimRegion = new TrimRegionCommand("Remove Left", true, seqs,
-                 column, viewport.getAlignment());
-         viewport.setStartRes(0);
+                 column, al);
+         ranges.setStartRes(0);
        }
        else
        {
          trimRegion = new TrimRegionCommand("Remove Right", false, seqs,
-                 column, viewport.getAlignment());
+                 column, al);
        }
  
        statusBar.setText(MessageManager.formatMessage(
                        .toString() }));
        addHistoryItem(trimRegion);
  
-       for (SequenceGroup sg : viewport.getAlignment().getGroups())
+       for (SequenceGroup sg : al.getGroups())
        {
          if ((trimLeft && !sg.adjustForRemoveLeft(column))
                  || (!trimLeft && !sg.adjustForRemoveRight(column)))
          {
-           viewport.getAlignment().deleteGroup(sg);
+           al.deleteGroup(sg);
          }
        }
  
-       viewport.firePropertyChange("alignment", null, viewport
-               .getAlignment().getSequences());
+       viewport.firePropertyChange("alignment", null, al.getSequences());
      }
    }
  
    public void removeGappedColumnMenuItem_actionPerformed()
    {
-     int start = 0, end = viewport.getAlignment().getWidth() - 1;
+     AlignmentI al = viewport.getAlignment();
+     ViewportRanges ranges = viewport.getRanges();
+     int start = 0;
+     int end = ranges.getAbsoluteAlignmentWidth() - 1;
  
      SequenceI[] seqs;
      if (viewport.getSelectionGroup() != null)
  
      // This is to maintain viewport position on first residue
      // of first sequence
-     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
-     int startRes = seq.findPosition(viewport.startRes);
+     SequenceI seq = al.getSequenceAt(0);
+     int startRes = seq.findPosition(ranges.getStartRes());
      // ShiftList shifts;
      // viewport.getAlignment().removeGaps(shifts=new ShiftList());
      // edit.alColumnChanges=shifts.getInverse();
      // if (viewport.hasHiddenColumns)
      // viewport.getColumnSelection().compensateForEdits(shifts);
-     viewport.setStartRes(seq.findIndex(startRes) - 1);
-     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
-             .getSequences());
+     ranges.setStartRes(seq.findIndex(startRes) - 1);
+     viewport.firePropertyChange("alignment", null, al.getSequences());
  
    }
  
    public void removeAllGapsMenuItem_actionPerformed()
    {
-     int start = 0, end = viewport.getAlignment().getWidth() - 1;
+     AlignmentI al = viewport.getAlignment();
+     ViewportRanges ranges = viewport.getRanges();
+     int start = 0;
+     int end = ranges.getAbsoluteAlignmentWidth() - 1;
  
      SequenceI[] seqs;
      if (viewport.getSelectionGroup() != null)
  
      // This is to maintain viewport position on first residue
      // of first sequence
-     SequenceI seq = viewport.getAlignment().getSequenceAt(0);
-     int startRes = seq.findPosition(viewport.startRes);
+     SequenceI seq = al.getSequenceAt(0);
+     int startRes = seq.findPosition(ranges.getStartRes());
  
      addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
-             viewport.getAlignment()));
+             al));
  
-     viewport.setStartRes(seq.findIndex(startRes) - 1);
+     ranges.setStartRes(seq.findIndex(startRes) - 1);
  
-     viewport.firePropertyChange("alignment", null, viewport.getAlignment()
-             .getSequences());
+     viewport.firePropertyChange("alignment", null, al.getSequences());
  
    }
  
      boolean selected = conservationMenuItem.getState();
      modifyConservation.setEnabled(selected);
      viewport.setConservationSelected(selected);
-     // viewport.setAbovePIDThreshold(false);
-     // abovePIDThreshold.setState(false);
+     viewport.getResidueShading().setConservationApplied(selected);
  
      changeColour(viewport.getGlobalColourScheme());
  
      boolean selected = abovePIDThreshold.getState();
      modifyPID.setEnabled(selected);
      viewport.setAbovePIDThreshold(selected);
-     // conservationMenuItem.setState(false);
-     // viewport.setConservationSelected(false);
+     if (!selected)
+     {
+       viewport.getResidueShading().setThreshold(0,
+               viewport.isIgnoreGapsConsensus());
+     }
  
      changeColour(viewport.getGlobalColourScheme());
  
              .getString("action.make_groups_selection"));
      grpsFromSelection.addActionListener(this);
      createGroup.setLabel(MessageManager.getString("action.create_group"));
+     createGroup.addActionListener(this);
      unGroup.setLabel(MessageManager.getString("action.remove_group"));
+     unGroup.addActionListener(this);
      annotationColumnSelection.setLabel(MessageManager
              .getString("action.select_by_annotation"));
      annotationColumnSelection.addActionListener(this);
      {
        // register the association(s) and quit, don't create any windows.
        if (StructureSelectionManager.getStructureSelectionManager(applet)
 -              .setMapping(seqs, chains, pdb.getFile(), protocol) == null)
 +              .setMapping(seqs, chains, pdb.getFile(), protocol, null) == null)
        {
          System.err.println("Failed to map " + pdb.getFile() + " ("
                  + protocol + ") to any sequences");
@@@ -24,7 -24,6 +24,7 @@@ import jalview.api.AlignmentViewPanel
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SequenceI;
  import jalview.ext.jmol.JalviewJmolBinding;
 +import jalview.gui.IProgressIndicator;
  import jalview.io.DataSourceType;
  import jalview.structure.StructureSelectionManager;
  
@@@ -55,21 -54,7 +55,7 @@@ class AppletJmolBinding extends Jalview
    public jalview.api.FeatureRenderer getFeatureRenderer(
            AlignmentViewPanel alignment)
    {
-     AlignmentPanel ap = (AlignmentPanel) alignment;
-     if (appletJmolBinding.ap.av.isShowSequenceFeatures())
-     {
-       if (appletJmolBinding.fr == null)
-       {
-         appletJmolBinding.fr = new jalview.appletgui.FeatureRenderer(
-                 appletJmolBinding.ap.av);
-       }
-       appletJmolBinding.fr
-               .transferSettings(appletJmolBinding.ap.seqPanel.seqCanvas
-                       .getFeatureRenderer());
-     }
-     return appletJmolBinding.fr;
+     return appletJmolBinding.ap.getFeatureRenderer();
    }
  
    @Override
      // TODO Auto-generated method stub
      return null;
    }
 +
 +  @Override
 +  protected IProgressIndicator getIProgressIndicator()
 +  {
 +    // no progress indicators on the applet
 +    return null;
 +  }
  }
@@@ -26,7 -26,6 +26,7 @@@ import jalview.api.SequenceRenderer
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SequenceI;
  import jalview.ext.jmol.JalviewJmolBinding;
 +import jalview.gui.IProgressIndicator;
  import jalview.io.DataSourceType;
  
  import java.awt.Container;
@@@ -67,13 -66,6 +67,13 @@@ public class ExtJmol extends JalviewJmo
    }
  
    @Override
 +  protected IProgressIndicator getIProgressIndicator()
 +  {
 +    // no progress indicators on applet (could access javascript for this)
 +    return null;
 +  }
 +
 +  @Override
    public void updateColours(Object source)
    {
  
    @Override
    public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
    {
-     AlignmentPanel ap = (AlignmentPanel) alignment;
-     if (ap.av.isShowSequenceFeatures())
+     AlignmentPanel alignPanel = (AlignmentPanel) alignment;
+     if (alignPanel.av.isShowSequenceFeatures())
      {
-       return ap.getFeatureRenderer();
+       return alignPanel.getFeatureRenderer();
      }
      else
      {
      }
    }
  
 +
    @Override
    public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment)
    {
   */
  package jalview.ext.jmol;
  
- import jalview.api.AlignViewportI;
+ import jalview.api.AlignmentViewPanel;
  import jalview.api.FeatureRenderer;
  import jalview.api.SequenceRenderer;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.SequenceI;
 +import jalview.gui.IProgressIndicator;
  import jalview.io.DataSourceType;
  import jalview.io.StructureFile;
  import jalview.schemes.ColourSchemeI;
@@@ -165,7 -164,7 +165,7 @@@ public abstract class JalviewJmolBindin
    public void closeViewer()
    {
      // remove listeners for all structures in viewer
 -    getSsm().removeStructureViewerListener(this, this.getPdbFile());
 +    getSsm().removeStructureViewerListener(this, this.getStructureFiles());
      viewer.dispose();
      lastCommand = null;
      viewer = null;
       * get the distinct structure files modelled
       * (a file with multiple chains may map to multiple sequences)
       */
 -    String[] files = getPdbFile();
 +    String[] files = getStructureFiles();
      if (!waitForFileLoad(files))
      {
        return null;
    /**
     * @param files
     * @param sr
-    * @param fr
-    * @param viewport
+    * @param viewPanel
     * @return
     */
    @Override
    protected StructureMappingcommandSet[] getColourBySequenceCommands(
-           String[] files, SequenceRenderer sr, FeatureRenderer fr,
-           AlignViewportI viewport)
+           String[] files, SequenceRenderer sr, AlignmentViewPanel viewPanel)
    {
      return JmolCommands.getColourBySequenceCommand(getSsm(), files,
-             getSequence(), sr, fr, viewport);
+             getSequence(), sr, viewPanel);
    }
  
    /**
  
    private int getModelNum(String modelFileName)
    {
 -    String[] mfn = getPdbFile();
 +    String[] mfn = getStructureFiles();
      if (mfn == null)
      {
        return -1;
  
    // ////////////////////////////////
    // /StructureListener
 -  @Override
 -  public synchronized String[] getPdbFile()
 +  // @Override
 +  public synchronized String[] getPdbFilex()
    {
      if (viewer == null)
      {
      return modelFileNames;
    }
  
 +  @Override
 +  public synchronized String[] getStructureFiles()
 +  {
 +    List<String> mset = new ArrayList<String>();
 +    if (viewer == null)
 +    {
 +      return new String[0];
 +    }
 +
 +    if (modelFileNames == null)
 +    {
 +      int modelCount = viewer.ms.mc;
 +      String filePath = null;
 +      for (int i = 0; i < modelCount; ++i)
 +      {
 +        filePath = viewer.ms.getModelFileName(i);
 +        if (!mset.contains(filePath))
 +        {
 +          mset.add(filePath);
 +        }
 +      }
 +      modelFileNames = mset.toArray(new String[mset.size()]);
 +    }
 +
 +    return modelFileNames;
 +  }
    /**
     * map from string to applet
     */
      chainNames = new ArrayList<String>();
      chainFile = new Hashtable<String, String>();
      boolean notifyLoaded = false;
 -    String[] modelfilenames = getPdbFile();
 +    String[] modelfilenames = getStructureFiles();
      // first check if we've lost any structures
      if (oldmodels != null && oldmodels.length > 0)
      {
            // see JAL-623 - need method of matching pasted data up
            {
              pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
 -                    pdbfile, DataSourceType.PASTE);
 +                    pdbfile, DataSourceType.PASTE, getIProgressIndicator());
              getPdbEntry(modelnum).setFile("INLINE" + pdb.getId());
              matches = true;
              foundEntry = true;
              }
              // Explicitly map to the filename used by Jmol ;
              pdb = getSsm().setMapping(getSequence()[pe], getChains()[pe],
 -                    fileName, protocol);
 +                    fileName, protocol, getIProgressIndicator());
              // pdbentry[pe].getFile(), protocol);
  
            }
      return chainNames;
    }
  
 +  protected abstract IProgressIndicator getIProgressIndicator();
 +
    public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
    {
      notifyAtomPicked(iatom, strMeasure, null);
  package jalview.ext.jmol;
  
  import jalview.api.AlignViewportI;
+ import jalview.api.AlignmentViewPanel;
  import jalview.api.FeatureRenderer;
  import jalview.api.SequenceRenderer;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.ColumnSelection;
  import jalview.datamodel.SequenceI;
+ import jalview.renderer.seqfeatures.FeatureColourFinder;
  import jalview.structure.StructureMapping;
  import jalview.structure.StructureMappingcommandSet;
  import jalview.structure.StructureSelectionManager;
@@@ -53,9 -55,12 +55,12 @@@ public class JmolCommand
     */
    public static StructureMappingcommandSet[] getColourBySequenceCommand(
            StructureSelectionManager ssm, String[] files,
-           SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
-           AlignViewportI viewport)
+           SequenceI[][] sequence, SequenceRenderer sr,
+           AlignmentViewPanel viewPanel)
    {
+     FeatureRenderer fr = viewPanel.getFeatureRenderer();
+     FeatureColourFinder finder = new FeatureColourFinder(fr);
+     AlignViewportI viewport = viewPanel.getAlignViewport();
      ColumnSelection cs = viewport.getColumnSelection();
      AlignmentI al = viewport.getAlignment();
      List<StructureMappingcommandSet> cset = new ArrayList<StructureMappingcommandSet>();
  
                lastPos = pos;
  
-               Color col = sr.getResidueBoxColour(sequence[pdbfnum][s], r);
-               if (fr != null)
-               {
-                 col = fr.findFeatureColour(col, sequence[pdbfnum][s], r);
-               }
+               Color col = sr.getResidueColour(sequence[pdbfnum][s], r,
+                       finder);
  
                /*
                 * shade hidden regions darker
                 */
                if (!cs.isVisible(r))
                {
                  col = Color.GRAY;
                }
  
 -              String newSelcom = (mapping[m].getChain() != " " ? ":"
 -                      + mapping[m].getChain() : "")
 +              String newSelcom = ":" + mapping[m].getChain()
                        + "/"
                        + (pdbfnum + 1)
                        + ".1"
@@@ -20,9 -20,7 +20,7 @@@
   */
  package jalview.ext.rbvi.chimera;
  
- import jalview.api.AlignViewportI;
  import jalview.api.AlignmentViewPanel;
- import jalview.api.FeatureRenderer;
  import jalview.api.SequenceRenderer;
  import jalview.api.structures.JalviewStructureDisplayI;
  import jalview.bin.Cache;
@@@ -175,11 -173,6 +173,6 @@@ public abstract class JalviewChimeraBin
        {
          getSsm().addStructureViewerListener(this);
          // ssm.addSelectionListener(this);
-         FeatureRenderer fr = getFeatureRenderer(null);
-         if (fr != null)
-         {
-           fr.featuresAdded();
-         }
          refreshGUI();
        }
        return true;
     */
    public void closeViewer(boolean closeChimera)
    {
 -    getSsm().removeStructureViewerListener(this, this.getPdbFile());
 +    getSsm().removeStructureViewerListener(this, this.getStructureFiles());
      if (closeChimera)
      {
        viewer.exitChimera();
            int[] _refStructure, ColumnSelection[] _hiddenCols)
    {
      StringBuilder allComs = new StringBuilder(128);
 -    String[] files = getPdbFile();
 +    String[] files = getStructureFiles();
  
      if (!waitForFileLoad(files))
      {
       * to the Chimera command 'list models type molecule', see
       * ChimeraManager.getModelList().
       */
 -    List<ChimeraModel> maps = chimeraMaps.get(getPdbFile()[pdbfnum]);
 +    List<ChimeraModel> maps = chimeraMaps.get(getStructureFiles()[pdbfnum]);
      boolean hasSubModels = maps != null && maps.size() > 1;
      return "#" + String.valueOf(pdbfnum) + (hasSubModels ? ".1" : "");
    }
    /**
     * @param files
     * @param sr
-    * @param fr
-    * @param viewport
+    * @param viewPanel
     * @return
     */
    @Override
    protected StructureMappingcommandSet[] getColourBySequenceCommands(
-           String[] files, SequenceRenderer sr, FeatureRenderer fr,
-           AlignViewportI viewport)
+           String[] files, SequenceRenderer sr, AlignmentViewPanel viewPanel)
    {
      return ChimeraCommands.getColourBySequenceCommand(getSsm(), files,
-             getSequence(), sr, fr, viewport);
+             getSequence(), sr, viewPanel);
    }
  
    /**
    // ////////////////////////////////
    // /StructureListener
    @Override
 -  public synchronized String[] getPdbFile()
 +  public synchronized String[] getStructureFiles()
    {
      if (viewer == null)
      {
    {
      // TODO refactor as required to pull up to an interface
      AlignmentI alignment = avp.getAlignment();
-     FeatureRenderer fr = getFeatureRenderer(avp);
-     /*
-      * fr is null if feature display is turned off
-      */
-     if (fr == null)
-     {
-       return 0;
-     }
  
 -    String[] files = getPdbFile();
 +    String[] files = getStructureFiles();
      if (files == null)
      {
        return 0;
  
      StructureMappingcommandSet commandSet = ChimeraCommands
              .getSetAttributeCommandsForFeatures(getSsm(), files,
-                     getSequence(), fr, alignment);
+                     getSequence(), avp);
      String[] commands = commandSet.commands;
      if (commands.length > 10)
      {
@@@ -86,6 -86,7 +86,7 @@@ import jalview.schemes.ResiduePropertie
  import jalview.schemes.TCoffeeColourScheme;
  import jalview.util.MessageManager;
  import jalview.viewmodel.AlignmentViewport;
+ import jalview.viewmodel.ViewportRanges;
  import jalview.ws.DBRefFetcher;
  import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
  import jalview.ws.jws1.Discoverer;
@@@ -160,6 -161,8 +161,8 @@@ public class AlignFrame extends GAlignF
  
    AlignViewport viewport;
  
+   ViewportRanges vpRanges;
    public AlignViewControllerI avc;
  
    List<AlignmentPanel> alignPanels = new ArrayList<AlignmentPanel>();
        progressBar = new ProgressBar(this.statusPanel, this.statusBar);
      }
  
+     vpRanges = viewport.getRanges();
      avc = new jalview.controller.AlignViewController(this, viewport,
              alignPanel);
      if (viewport.getAlignmentConservationAnnotation() == null)
                    new String[] { (viewport.cursorMode ? "on" : "off") }));
            if (viewport.cursorMode)
            {
-             alignPanel.getSeqPanel().seqCanvas.cursorX = viewport.startRes;
-             alignPanel.getSeqPanel().seqCanvas.cursorY = viewport.startSeq;
+             alignPanel.getSeqPanel().seqCanvas.cursorX = vpRanges
+                     .getStartRes();
+             alignPanel.getSeqPanel().seqCanvas.cursorY = vpRanges
+                     .getStartSeq();
            }
            alignPanel.getSeqPanel().seqCanvas.repaint();
            break;
            }
            else
            {
-             alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
-                     - viewport.endSeq + viewport.startSeq);
+             alignPanel.setScrollValues(vpRanges.getStartRes(),
+                     2 * vpRanges.getStartSeq() - vpRanges.getEndSeq());
            }
            break;
          case KeyEvent.VK_PAGE_DOWN:
            }
            else
            {
-             alignPanel.setScrollValues(viewport.startRes, viewport.startSeq
-                     + viewport.endSeq - viewport.startSeq);
+             alignPanel.setScrollValues(vpRanges.getStartRes(),
+                     vpRanges.getEndSeq());
            }
            break;
          }
        {
  
          // propagate alignment changed.
-         viewport.setEndSeq(alignment.getHeight());
+         vpRanges.setEndSeq(alignment.getHeight());
          if (annotationAdded)
          {
            // Duplicate sequence annotation in all views.
        {
          trimRegion = new TrimRegionCommand("Remove Left", true, seqs,
                  column, viewport.getAlignment());
-         viewport.setStartRes(0);
+         vpRanges.setStartRes(0);
        }
        else
        {
      // This is to maintain viewport position on first residue
      // of first sequence
      SequenceI seq = viewport.getAlignment().getSequenceAt(0);
-     int startRes = seq.findPosition(viewport.startRes);
+     int startRes = seq.findPosition(vpRanges.getStartRes());
      // ShiftList shifts;
      // viewport.getAlignment().removeGaps(shifts=new ShiftList());
      // edit.alColumnChanges=shifts.getInverse();
      // if (viewport.hasHiddenColumns)
      // viewport.getColumnSelection().compensateForEdits(shifts);
-     viewport.setStartRes(seq.findIndex(startRes) - 1);
+     vpRanges.setStartRes(seq.findIndex(startRes) - 1);
      viewport.firePropertyChange("alignment", null, viewport.getAlignment()
              .getSequences());
  
      // This is to maintain viewport position on first residue
      // of first sequence
      SequenceI seq = viewport.getAlignment().getSequenceAt(0);
-     int startRes = seq.findPosition(viewport.startRes);
+     int startRes = seq.findPosition(vpRanges.getStartRes());
  
      addHistoryItem(new RemoveGapsCommand("Remove Gaps", seqs, start, end,
              viewport.getAlignment()));
  
-     viewport.setStartRes(seq.findIndex(startRes) - 1);
+     vpRanges.setStartRes(seq.findIndex(startRes) - 1);
  
      viewport.firePropertyChange("alignment", null, viewport.getAlignment()
              .getSequences());
       */
      newap.av.replaceMappings(viewport.getAlignment());
  
+     /*
+      * start up cDNA consensus (if applicable) now mappings are in place
+      */
+     if (newap.av.initComplementConsensus())
+     {
+       newap.refresh(true); // adjust layout of annotations
+     }
      newap.av.viewName = getNewViewName(viewTitle);
  
      addAlignmentPanel(newap, true);
       */
      if (ResidueColourScheme.USER_DEFINED.equals(name))
      {
-       new UserDefinedColours(alignPanel, null);
+       new UserDefinedColours(alignPanel);
        return;
      }
  
    public void changeColour(ColourSchemeI cs)
    {
      // TODO: pull up to controller method
-     if (cs != null)
-     {
-       ColourMenuHelper.setColourSelected(colourMenu, cs.getSchemeName());
-     }
+     ColourMenuHelper.setColourSelected(colourMenu, cs);
  
      viewport.setGlobalColourScheme(cs);
  
                // associating PDB files which have no IDs.
                for (SequenceI toassoc : (SequenceI[]) fm[2])
                {
 -                PDBEntry pe = new AssociatePdbFileWithSeq()
 -                        .associatePdbWithSeq((String) fm[0],
 +                PDBEntry pe = new AssociateStructureFileWithSeq()
 +                        .associateStructureWithSeq((String) fm[0],
                                  (DataSourceType) fm[1], toassoc, false,
                                  Desktop.instance);
                  if (pe != null)
      colourMenu.add(annotationColour);
  
      ColourSchemeI colourScheme = viewport.getGlobalColourScheme();
-     String schemeName = colourScheme == null ? null : colourScheme
-             .getSchemeName();
-     ColourMenuHelper.setColourSelected(colourMenu, schemeName);
+     ColourMenuHelper.setColourSelected(colourMenu, colourScheme);
    }
  }
  
@@@ -40,8 -40,6 +40,6 @@@ public class AppJmolBinding extends Jal
  {
    private AppJmol appJmolWindow;
  
-   private FeatureRenderer fr = null;
    public AppJmolBinding(AppJmol appJmol, StructureSelectionManager sSm,
            PDBEntry[] pdbentry, SequenceI[][] sequenceIs, DataSourceType protocol)
    {
    }
  
    @Override
 +  protected IProgressIndicator getIProgressIndicator()
 +  {
 +    return appJmolWindow.progressBar;
 +  }
-   @Override
-   public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
-   {
-     AlignmentPanel ap = (alignment == null) ? appJmolWindow
-             .getAlignmentPanel() : (AlignmentPanel) alignment;
-     if (ap.av.isShowSequenceFeatures())
-     {
-       if (fr == null)
-       {
-         fr = (jalview.gui.FeatureRenderer) ap.cloneFeatureRenderer();
-       }
-       else
-       {
-         ap.updateFeatureRenderer(fr);
-       }
-     }
-     return fr;
-   }
 +
 +  @Override
    public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment)
    {
      return new SequenceRenderer(((AlignmentPanel) alignment).av);
    {
      return appJmolWindow;
    }
+   @Override
+   public jalview.api.FeatureRenderer getFeatureRenderer(
+           AlignmentViewPanel alignment)
+   {
+     AlignmentPanel ap = (alignment == null) ? appJmolWindow
+             .getAlignmentPanel() : (AlignmentPanel) alignment;
+     if (ap.av.isShowSequenceFeatures())
+     {
+       return ap.av.getAlignPanel().getSeqPanel().seqCanvas.fr;
+     }
+     return null;
+   }
  }
@@@ -20,6 -20,7 +20,7 @@@
   */
  package jalview.gui;
  
+ import jalview.api.FeatureRenderer;
  import jalview.bin.Cache;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.PDBEntry;
@@@ -483,7 -484,7 +484,7 @@@ public class ChimeraViewFrame extends S
      StructureFile pdb = null;
      try
      {
 -      String[] curfiles = jmb.getPdbFile(); // files currently in viewer
 +      String[] curfiles = jmb.getStructureFiles(); // files currently in viewer
        // TODO: replace with reference fetching/transfer code (validate PDBentry
        // as a DBRef?)
        for (int pi = 0; pi < jmb.getPdbCount(); pi++)
              }
              // Explicitly map to the filename used by Chimera ;
              pdb = jmb.getSsm().setMapping(jmb.getSequence()[pos],
 -                    jmb.getChains()[pos], pe.getFile(), protocol);
 +                    jmb.getChains()[pos], pe.getFile(), protocol,
 +                    progressBar);
              stashFoundChains(pdb, pe.getFile());
            } catch (OutOfMemoryError oomerror)
            {
        jmb.setFinishedInit(true);
        jmb.setLoadingFromArchive(false);
  
+       /*
+        * ensure that any newly discovered features (e.g. RESNUM)
+        * are added to any open feature settings dialog
+        */
+       FeatureRenderer fr = getBinding().getFeatureRenderer(null);
+       if (fr != null)
+       {
+         fr.featuresAdded();
+       }
        // refresh the sequence colours for the new structure(s)
        for (AlignmentPanel ap : _colourwith)
        {
  
    /**
     * Fetch PDB data and save to a local file. Returns the full path to the file,
 -   * or null if fetch fails.
 +   * or null if fetch fails. TODO: refactor to common with Jmol ? duplication
     * 
     * @param processingEntry
     * @return
      }
      return reply;
    }
 +
 +  @Override
 +  protected IProgressIndicator getIProgressIndicator()
 +  {
 +    return progressBar;
 +  }
 +
 +  @Override
 +  protected AAStructureBindingModel getBindingModel()
 +  {
 +    return jmb;
 +  }
  }
@@@ -29,6 -29,7 +29,7 @@@ import jalview.datamodel.AlignedCodonFr
  import jalview.datamodel.Alignment;
  import jalview.datamodel.AlignmentAnnotation;
  import jalview.datamodel.AlignmentI;
+ import jalview.datamodel.GraphLine;
  import jalview.datamodel.PDBEntry;
  import jalview.datamodel.RnaViewerModel;
  import jalview.datamodel.SequenceGroup;
@@@ -77,7 -78,6 +78,6 @@@ import jalview.schemes.AnnotationColour
  import jalview.schemes.ColourSchemeI;
  import jalview.schemes.ColourSchemeProperty;
  import jalview.schemes.FeatureColour;
- import jalview.schemes.ResidueColourScheme;
  import jalview.schemes.ResidueProperties;
  import jalview.schemes.UserColourScheme;
  import jalview.structure.StructureSelectionManager;
@@@ -87,6 -87,7 +87,7 @@@ import jalview.util.Platform
  import jalview.util.StringUtils;
  import jalview.util.jarInputStreamProvider;
  import jalview.viewmodel.AlignmentViewport;
+ import jalview.viewmodel.ViewportRanges;
  import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
  import jalview.viewmodel.seqfeatures.FeaturesDisplayed;
  import jalview.ws.jws2.Jws2Discoverer;
@@@ -755,6 -756,7 +756,7 @@@ public class Jalview2XM
      List<UserColourScheme> userColours = new ArrayList<UserColourScheme>();
  
      AlignViewport av = ap.av;
+     ViewportRanges vpRanges = av.getRanges();
  
      JalviewModel object = new JalviewModel();
      object.setVamsasModel(new jalview.schemabinding.version2.VamsasModel());
        view.setWidth(size.width);
        view.setHeight(size.height);
  
-       view.setStartRes(av.startRes);
-       view.setStartSeq(av.startSeq);
+       view.setStartRes(vpRanges.getStartRes());
+       view.setStartSeq(vpRanges.getStartSeq());
  
        if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
        {
      return matchedFile;
    }
  
+   /**
+    * Populates the AnnotationColours xml for save. This captures the settings of
+    * the options in the 'Colour by Annotation' dialog.
+    * 
+    * @param acg
+    * @param userColours
+    * @param jms
+    * @return
+    */
    private AnnotationColours constructAnnotationColours(
            AnnotationColourGradient acg, List<UserColourScheme> userColours,
            JalviewModelSequence jms)
      AnnotationColours ac = new AnnotationColours();
      ac.setAboveThreshold(acg.getAboveThreshold());
      ac.setThreshold(acg.getAnnotationThreshold());
-     ac.setAnnotation(acg.getAnnotation());
-     if (acg.getBaseColour() instanceof jalview.schemes.UserColourScheme)
+     // 2.10.2 save annotationId (unique) not annotation label
+     ac.setAnnotation(acg.getAnnotation().annotationId);
+     if (acg.getBaseColour() instanceof UserColourScheme)
      {
        ac.setColourScheme(setUserColourScheme(acg.getBaseColour(),
                userColours, jms));
            @Override
            public void run()
            {
-             JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-                     finalErrorMessage, "Error "
-                             + (saving ? "saving" : "loading")
-                             + " Jalview file", JvOptionPane.WARNING_MESSAGE);
+             JvOptionPane
+                     .showInternalMessageDialog(Desktop.desktop,
+                             finalErrorMessage, "Error "
+                                     + (saving ? "saving" : "loading")
+                                     + " Jalview file",
+                             JvOptionPane.WARNING_MESSAGE);
            }
          });
        }
        StructureData filedat = oldFiles.get(id);
        String pdbFile = filedat.getFilePath();
        SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]);
 -      binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE);
 +      binding.getSsm().setMapping(seq, null, pdbFile, DataSourceType.FILE,
 +              null);
        binding.addSequenceForStructFile(pdbFile, seq);
      }
      // and add the AlignmentPanel's reference to the view panel
      af.viewport.setThresholdTextColour(view.getTextColThreshold());
      af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
              .isShowUnconserved() : false);
-     af.viewport.setStartRes(view.getStartRes());
-     af.viewport.setStartSeq(view.getStartSeq());
+     af.viewport.getRanges().setStartRes(view.getStartRes());
+     af.viewport.getRanges().setStartSeq(view.getStartSeq());
      af.alignPanel.updateLayout();
      ColourSchemeI cs = null;
      // apply colourschemes
      return af;
    }
  
+   /**
+    * Reads saved data to restore Colour by Annotation settings
+    * 
+    * @param viewAnnColour
+    * @param af
+    * @param al
+    * @param jms
+    * @param checkGroupAnnColour
+    * @return
+    */
    private ColourSchemeI constructAnnotationColour(
            AnnotationColours viewAnnColour, AlignFrame af, AlignmentI al,
            JalviewModelSequence jms, boolean checkGroupAnnColour)
    {
      boolean propagateAnnColour = false;
-     ColourSchemeI cs = null;
      AlignmentI annAlignment = af != null ? af.viewport.getAlignment() : al;
      if (checkGroupAnnColour && al.getGroups() != null
              && al.getGroups().size() > 0)
        // pre 2.8.1 behaviour
        // check to see if we should transfer annotation colours
        propagateAnnColour = true;
-       for (jalview.datamodel.SequenceGroup sg : al.getGroups())
+       for (SequenceGroup sg : al.getGroups())
        {
          if (sg.getColourScheme() instanceof AnnotationColourGradient)
          {
          }
        }
      }
-     // int find annotation
-     if (annAlignment.getAlignmentAnnotation() != null)
+     /*
+      * 2.10.2- : saved annotationId is AlignmentAnnotation.annotationId
+      */
+     String annotationId = viewAnnColour.getAnnotation();
+     AlignmentAnnotation matchedAnnotation = annotationIds.get(annotationId);
+     /*
+      * pre 2.10.2: saved annotationId is AlignmentAnnotation.label
+      */
+     if (matchedAnnotation == null && annAlignment.getAlignmentAnnotation() != null)
      {
        for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
        {
-         if (annAlignment.getAlignmentAnnotation()[i].label
-                 .equals(viewAnnColour.getAnnotation()))
+         if (annotationId
+                 .equals(annAlignment.getAlignmentAnnotation()[i].label))
          {
-           if (annAlignment.getAlignmentAnnotation()[i].getThreshold() == null)
-           {
-             annAlignment.getAlignmentAnnotation()[i]
-                     .setThreshold(new jalview.datamodel.GraphLine(
-                             viewAnnColour.getThreshold(), "Threshold",
-                             java.awt.Color.black)
-                     );
-           }
-           if (viewAnnColour.getColourScheme().equals(
-                   ResidueColourScheme.NONE))
-           {
-             cs = new AnnotationColourGradient(
-                     annAlignment.getAlignmentAnnotation()[i],
-                     new java.awt.Color(viewAnnColour.getMinColour()),
-                     new java.awt.Color(viewAnnColour.getMaxColour()),
-                     viewAnnColour.getAboveThreshold());
-           }
-           else if (viewAnnColour.getColourScheme().startsWith("ucs"))
-           {
-             cs = new AnnotationColourGradient(
-                     annAlignment.getAlignmentAnnotation()[i],
-                     getUserColourScheme(jms,
-                             viewAnnColour.getColourScheme()),
-                     viewAnnColour.getAboveThreshold());
-           }
-           else
-           {
-             cs = new AnnotationColourGradient(
-                     annAlignment.getAlignmentAnnotation()[i],
-                     ColourSchemeProperty.getColourScheme(al,
-                             viewAnnColour.getColourScheme()),
-                     viewAnnColour.getAboveThreshold());
-           }
-           if (viewAnnColour.hasPerSequence())
-           {
-             ((AnnotationColourGradient) cs).setSeqAssociated(viewAnnColour
-                     .isPerSequence());
-           }
-           if (viewAnnColour.hasPredefinedColours())
-           {
-             ((AnnotationColourGradient) cs)
-                     .setPredefinedColours(viewAnnColour
-                             .isPredefinedColours());
-           }
-           if (propagateAnnColour && al.getGroups() != null)
-           {
-             // Also use these settings for all the groups
-             for (int g = 0; g < al.getGroups().size(); g++)
-             {
-               jalview.datamodel.SequenceGroup sg = al.getGroups().get(g);
-               if (sg.cs == null)
-               {
-                 continue;
-               }
+           matchedAnnotation = annAlignment.getAlignmentAnnotation()[i];
+           break;
+         }
+       }
+     }
+     if (matchedAnnotation == null)
+     {
+       System.err.println("Failed to match annotation colour scheme for "
+               + annotationId);
+       return null;
+     }
+     if (matchedAnnotation.getThreshold() == null)
+     {
+       matchedAnnotation.setThreshold(new GraphLine(viewAnnColour.getThreshold(),
+               "Threshold", Color.black));
+     }
  
-               /*
-                * if (viewAnnColour.getColourScheme().equals(ResidueColourScheme.NONE)) { sg.cs =
-                * new AnnotationColourGradient(
-                * annAlignment.getAlignmentAnnotation()[i], new
-                * java.awt.Color(viewAnnColour. getMinColour()), new
-                * java.awt.Color(viewAnnColour. getMaxColour()),
-                * viewAnnColour.getAboveThreshold()); } else
-                */
-               {
-                 sg.setColourScheme(new AnnotationColourGradient(
-                         annAlignment.getAlignmentAnnotation()[i], sg
-                                 .getColourScheme(), viewAnnColour
-                                 .getAboveThreshold()));
-                 if (cs instanceof AnnotationColourGradient)
-                 {
-                   if (viewAnnColour.hasPerSequence())
-                   {
-                     ((AnnotationColourGradient) cs)
-                             .setSeqAssociated(viewAnnColour.isPerSequence());
-                   }
-                   if (viewAnnColour.hasPredefinedColours())
-                   {
-                     ((AnnotationColourGradient) cs)
-                             .setPredefinedColours(viewAnnColour
-                                     .isPredefinedColours());
-                   }
-                 }
-               }
+     AnnotationColourGradient cs = null;
+     if (viewAnnColour.getColourScheme().equals("None"))
+     {
+       cs = new AnnotationColourGradient(matchedAnnotation, new Color(
+               viewAnnColour.getMinColour()), new Color(
+               viewAnnColour.getMaxColour()),
+               viewAnnColour.getAboveThreshold());
+     }
+     else if (viewAnnColour.getColourScheme().startsWith("ucs"))
+     {
+       cs = new AnnotationColourGradient(matchedAnnotation, getUserColourScheme(
+               jms, viewAnnColour.getColourScheme()),
+               viewAnnColour.getAboveThreshold());
+     }
+     else
+     {
+       cs = new AnnotationColourGradient(matchedAnnotation,
+               ColourSchemeProperty.getColourScheme(al,
+                       viewAnnColour.getColourScheme()),
+               viewAnnColour.getAboveThreshold());
+     }
  
-             }
-           }
+     boolean perSequenceOnly = viewAnnColour.isPerSequence();
+     boolean useOriginalColours = viewAnnColour.isPredefinedColours();
+     cs.setSeqAssociated(perSequenceOnly);
+     cs.setPredefinedColours(useOriginalColours);
  
-           break;
+     if (propagateAnnColour && al.getGroups() != null)
+     {
+       // Also use these settings for all the groups
+       for (int g = 0; g < al.getGroups().size(); g++)
+       {
+         SequenceGroup sg = al.getGroups().get(g);
+         if (sg.getGroupColourScheme() == null)
+         {
+           continue;
          }
  
+         AnnotationColourGradient groupScheme = new AnnotationColourGradient(
+                 matchedAnnotation, sg.getColourScheme(),
+                 viewAnnColour.getAboveThreshold());
+         sg.setColourScheme(groupScheme);
+         groupScheme.setSeqAssociated(perSequenceOnly);
+         groupScheme.setPredefinedColours(useOriginalColours);
        }
      }
      return cs;
@@@ -126,7 -126,7 +126,7 @@@ public class MouseOverStructureListene
    }
  
    @Override
 -  public String[] getPdbFile()
 +  public String[] getStructureFiles()
    {
      return modelSet;
    }
        ArrayList<String[]> ccomands = new ArrayList<String[]>();
        ArrayList<String> pdbfn = new ArrayList<String>();
        StructureMappingcommandSet[] colcommands = JmolCommands
-               .getColourBySequenceCommand(ssm, modelSet, sequence, sr, fr,
-                       ((AlignmentViewPanel) source).getAlignViewport());
+               .getColourBySequenceCommand(ssm, modelSet, sequence, sr,
+                       (AlignmentViewPanel) source);
        if (colcommands == null)
        {
          return;
@@@ -20,9 -20,7 +20,7 @@@
   */
  package jalview.structures.models;
  
- import jalview.api.AlignViewportI;
  import jalview.api.AlignmentViewPanel;
- import jalview.api.FeatureRenderer;
  import jalview.api.SequenceRenderer;
  import jalview.api.StructureSelectionManagerProvider;
  import jalview.api.structures.JalviewStructureDisplayI;
@@@ -534,7 -532,7 +532,7 @@@ public abstract class AAStructureBindin
            BitSet matched, SuperposeData[] structures)
    {
      int refStructure = -1;
 -    String[] files = getPdbFile();
 +    String[] files = getStructureFiles();
      if (files == null)
      {
        return -1;
    public abstract void setBackgroundColour(Color col);
  
    protected abstract StructureMappingcommandSet[] getColourBySequenceCommands(
-           String[] files, SequenceRenderer sr, FeatureRenderer fr,
-           AlignViewportI alignViewportI);
-   /**
-    * returns the current featureRenderer that should be used to colour the
-    * structures
-    * 
-    * @param alignment
-    * 
-    * @return
-    */
-   public abstract FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment);
+           String[] files, SequenceRenderer sr, AlignmentViewPanel avp);
  
    /**
     * returns the current sequenceRenderer that should be used to colour the
      {
        return;
      }
 -    String[] files = getPdbFile();
 +    String[] files = getStructureFiles();
    
      SequenceRenderer sr = getSequenceRenderer(alignmentv);
    
-     FeatureRenderer fr = null;
-     boolean showFeatures = alignmentv.getAlignViewport()
-             .isShowSequenceFeatures();
-     if (showFeatures)
-     {
-       fr = getFeatureRenderer(alignmentv);
-     }
-   
      StructureMappingcommandSet[] colourBySequenceCommands = getColourBySequenceCommands(
-             files, sr, fr, alignmentv.getAlignViewport());
+             files, sr, alignmentv);
      colourBySequence(colourBySequenceCommands);
    }
  
    {
      return fileLoadingError != null && fileLoadingError.length() > 0;
    }
+   public abstract jalview.api.FeatureRenderer getFeatureRenderer(
+           AlignmentViewPanel alignment);
  }
@@@ -24,7 -24,6 +24,6 @@@ import static org.testng.AssertJUnit.as
  import static org.testng.AssertJUnit.assertFalse;
  import static org.testng.AssertJUnit.assertTrue;
  
- import jalview.api.AlignViewportI;
  import jalview.api.AlignmentViewPanel;
  import jalview.api.FeatureRenderer;
  import jalview.api.SequenceRenderer;
@@@ -130,17 -129,16 +129,17 @@@ public class AAStructureBindingModelTes
      StructureSelectionManager ssm = new StructureSelectionManager();
  
      ssm.setMapping(new SequenceI[] { seq1a, seq1b }, null, PDB_1,
 -            DataSourceType.PASTE);
 +            DataSourceType.PASTE, null);
      ssm.setMapping(new SequenceI[] { seq2 }, null, PDB_2,
 -            DataSourceType.PASTE);
 +            DataSourceType.PASTE, null);
      ssm.setMapping(new SequenceI[] { seq3 }, null, PDB_3,
 -            DataSourceType.PASTE);
 +            DataSourceType.PASTE, null);
 +
  
      testee = new AAStructureBindingModel(ssm, pdbFiles, seqs, null)
      {
        @Override
 -      public String[] getPdbFile()
 +      public String[] getStructureFiles()
        {
          return new String[] { "INLINE1YCS", "INLINE3A6S", "INLINE1OOT" };
        }
  
        @Override
        protected StructureMappingcommandSet[] getColourBySequenceCommands(
-               String[] files, SequenceRenderer sr, FeatureRenderer fr,
-               AlignViewportI viewport)
-       {
-         return null;
-       }
-       @Override
-       public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
+               String[] files, SequenceRenderer sr, AlignmentViewPanel avp)
        {
          return null;
        }
        public void colourByCharge()
        {
        }
+       @Override
+       public FeatureRenderer getFeatureRenderer(
+               AlignmentViewPanel alignment)
+       {
+         return null;
+       }
      };
    }
  
      /*
       * create a data bean to hold data per structure file
       */
 -    SuperposeData[] structs = new SuperposeData[testee.getPdbFile().length];
 +    SuperposeData[] structs = new SuperposeData[testee.getStructureFiles().length];
      for (int i = 0; i < structs.length; i++)
      {
        structs[i] = testee.new SuperposeData(al.getWidth());
@@@ -32,14 -32,14 +32,15 @@@ import jalview.datamodel.SequenceI
  import jalview.gui.JvOptionPane;
  import jalview.io.DataSourceType;
  import jalview.structure.StructureMapping;
 +import jalview.structure.StructureMappingClient.StructureMappingException;
  import jalview.xml.binding.sifts.Entry.Entity;
  
  import java.io.File;
  import java.io.IOException;
  import java.util.ArrayList;
- import java.util.Arrays;
  import java.util.HashMap;
+ import java.util.Iterator;
+ import java.util.Map;
  
  import org.testng.Assert;
  import org.testng.FileAssert;
@@@ -282,16 -282,19 +283,18 @@@ public class SiftsClientTes
                "A", testSeq, null);
        Assert.assertEquals(testSeq.getStart(), 1);
        Assert.assertEquals(testSeq.getEnd(), 147);
-       // Assert.assertEquals(actualMapping, expectedMapping);
+       // Can't do Assert.assertEquals(actualMapping, expectedMapping);
+       // because this fails in our version of TestNG
        Assert.assertEquals(actualMapping.size(), expectedMapping.size());
-       // Test key set
-       Assert.assertEquals(actualMapping.keySet(), expectedMapping.keySet());
-       // Test entry set
-       for (int key : expectedMapping.keySet())
+       Iterator<Map.Entry<Integer, int[]>> it = expectedMapping.entrySet()
+               .iterator();
+       while (it.hasNext())
        {
-         Assert.assertTrue(Arrays.equals(expectedMapping.get(key),
-                 actualMapping.get(key)));
+         Map.Entry<Integer, int[]> pair = it.next();
+         Assert.assertTrue(actualMapping.containsKey(pair.getKey()));
+         Assert.assertEquals(actualMapping.get(pair.getKey()),
+                 pair.getValue());
        }
 -
      } catch (Exception e)
      {
        e.printStackTrace();
  
    @Test(
  groups = { "Network" },
 -    expectedExceptions = SiftsException.class)
 +    expectedExceptions = StructureMappingException.class)
    private void populateAtomPositionsNullTest1()
 -          throws IllegalArgumentException, SiftsException
 +          throws IllegalArgumentException, StructureMappingException
    {
      siftsClient.populateAtomPositions(null, null);
    }
  
    @Test(
  groups = { "Network" },
 -    expectedExceptions = SiftsException.class)
 +    expectedExceptions = StructureMappingException.class)
    private void populateAtomPositionsNullTest2()
 -          throws IllegalArgumentException, SiftsException
 +          throws IllegalArgumentException, StructureMappingException
    {
      siftsClient.populateAtomPositions("A", null);
    }
@@@ -389,11 -392,10 +392,11 @@@ groups = { "Network" }
    }
  
    @Test(groups = { "Network" })
 -  public void getSiftsStructureMappingTest() throws SiftsException
 +  public void getSiftsStructureMappingTest()
 +          throws StructureMappingException, Exception
    {
      Assert.assertTrue(SiftsSettings.isMapWithSifts());
 -    StructureMapping strucMapping = siftsClient.getSiftsStructureMapping(
 +    StructureMapping strucMapping = siftsClient.getStructureMapping(
              testSeq, testPDBId, "A");
      String expectedMappingOutput = "\nSequence ⟷ Structure mapping details\n"
              + "Method: SIFTS\n\n"
      Assert.assertEquals(strucMapping.getMappingDetailsOutput(),
              expectedMappingOutput);
  
-     // Assert.assertEquals(strucMapping.getMapping(), expectedMapping);
+     // Can't do Assert.assertEquals(strucMapping.getMapping(), expectedMapping);
+     // because this fails in our version of TestNG
      Assert.assertEquals(strucMapping.getMapping().size(),
              expectedMapping.size());
-     // Test key set
-     Assert.assertEquals(strucMapping.getMapping().keySet(),
-             expectedMapping.keySet());
-     // Test entry set
-     for (int key : expectedMapping.keySet())
+     Iterator<Map.Entry<Integer, int[]>> it = expectedMapping.entrySet()
+             .iterator();
+     while (it.hasNext())
      {
-       Assert.assertTrue(Arrays.equals(expectedMapping.get(key),
-               strucMapping.getMapping().get(key)));
+       Map.Entry<Integer, int[]> pair = it.next();
+       Assert.assertTrue(strucMapping.getMapping()
+               .containsKey(pair.getKey()));
+       Assert.assertEquals(strucMapping.getMapping().get(pair.getKey()),
+               pair.getValue());
      }
    }