merge develop
authortcofoegbu <tcnofoegbu@dundee.ac.uk>
Mon, 1 May 2017 14:02:17 +0000 (15:02 +0100)
committertcofoegbu <tcnofoegbu@dundee.ac.uk>
Mon, 1 May 2017 14:02:17 +0000 (15:02 +0100)
18 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
src/jalview/ws/sifts/SiftsClient.java
test/jalview/structures/models/AAStructureBindingModelTest.java
test/jalview/ws/sifts/SiftsClientTest.java

@@@ -3,9 -3,6 +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
@@@ -71,6 -68,7 +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
@@@ -671,7 -669,8 +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
@@@ -1296,4 -1295,10 +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,7 -28,6 +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;
@@@ -159,7 -158,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,7 -28,6 +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;
@@@ -153,7 -152,8 +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,7 -75,6 +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;
@@@ -421,8 -420,6 +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,6 -24,7 +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;
  
@@@ -54,7 -55,21 +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,6 -26,7 +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;
@@@ -66,6 -67,13 +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;
@@@ -164,7 -165,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;
@@@ -55,12 -53,9 +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,7 -20,9 +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;
@@@ -172,6 -174,13 +172,6 @@@ public abstract class JalviewChimeraBin
        if (getSsm() != null)
        {
          getSsm().addStructureViewerListener(this);
 -        // ssm.addSelectionListener(this);
 -        FeatureRenderer fr = getFeatureRenderer(null);
 -        if (fr != null)
 -        {
 -          fr.featuresAdded();
 -        }
 -        refreshGUI();
        }
        return true;
      } catch (Exception q)
     */
    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,7 -86,6 +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;
@@@ -161,8 -160,6 +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,6 -40,8 +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,7 -20,6 +20,7 @@@
   */
  package jalview.gui;
  
 +import jalview.api.FeatureRenderer;
  import jalview.bin.Cache;
  import jalview.datamodel.AlignmentI;
  import jalview.datamodel.PDBEntry;
@@@ -484,7 -483,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,7 -29,6 +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;
@@@ -78,6 -77,7 +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,7 -87,6 +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;
@@@ -756,7 -755,6 +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,7 -20,9 +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;
@@@ -532,7 -534,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);
  }
@@@ -29,6 -29,8 +29,8 @@@ import jalview.datamodel.SequenceI
  import jalview.io.StructureFile;
  import jalview.schemes.ResidueProperties;
  import jalview.structure.StructureMapping;
+ import jalview.structure.StructureMappingClient;
+ import jalview.structures.models.MappingOutputModel;
  import jalview.util.Comparison;
  import jalview.util.DBRefUtils;
  import jalview.util.Format;
@@@ -53,7 -55,6 +55,6 @@@ import java.nio.file.Path
  import java.nio.file.attribute.BasicFileAttributes;
  import java.util.ArrayList;
  import java.util.Arrays;
- import java.util.Collection;
  import java.util.Collections;
  import java.util.Date;
  import java.util.HashMap;
@@@ -69,10 -70,8 +70,8 @@@ import javax.xml.bind.Unmarshaller
  import javax.xml.stream.XMLInputFactory;
  import javax.xml.stream.XMLStreamReader;
  
- import MCview.Atom;
- import MCview.PDBChain;
- public class SiftsClient implements SiftsClientI
+ public class SiftsClient extends StructureMappingClient implements
+         SiftsClientI
  {
    /*
     * for use in mocking out file fetch for tests only
@@@ -82,8 -81,6 +81,6 @@@
  
    private Entry siftsEntry;
  
-   private StructureFile pdb;
    private String pdbId;
  
    private String structId;
@@@ -96,8 -93,6 +93,6 @@@
  
    private static final int PDB_RES_POS = 0;
  
-   private static final int PDB_ATOM_POS = 1;
    private static final String NOT_OBSERVED = "Not_Observed";
  
    private static final String SIFTS_FTP_BASE_URL = "http://ftp.ebi.ac.uk/pub/databases/msd/sifts/xml/";
     * @param pdbId
     * @throws SiftsException
     */
-   public SiftsClient(StructureFile pdb) throws SiftsException
+   public SiftsClient(StructureFile structureFile) throws SiftsException
    {
-     this.pdb = pdb;
-     this.pdbId = pdb.getId();
+     this.structureFile = structureFile;
+     this.pdbId = structureFile.getId();
      File siftsFile = getSiftsFile(pdbId);
      siftsEntry = parseSIFTs(siftsFile);
    }
    }
  
    @Override
-   public StructureMapping getSiftsStructureMapping(SequenceI seq,
-           String pdbFile, String chain) throws SiftsException
+   public StructureMapping getStructureMapping(SequenceI seq,
+           String pdbFile, String chain) throws Exception,
+           StructureMappingException
    {
      structId = (chain == null) ? pdbId : pdbId + "|" + chain;
      System.out.println("Getting SIFTS mapping for " + structId + ": seq "
  
    @Override
    public HashMap<Integer, int[]> getGreedyMapping(String entityId,
-           SequenceI seq, java.io.PrintStream os) throws SiftsException
+           SequenceI seq, java.io.PrintStream os) throws SiftsException,
+           StructureMappingException
    {
      List<Integer> omitNonObserved = new ArrayList<Integer>();
      int nonObservedShiftIndex = 0;
  
      if (os != null)
      {
-       MappingOutputPojo mop = new MappingOutputPojo();
+       MappingOutputModel mop = new MappingOutputModel();
        mop.setSeqStart(seqStart);
        mop.setSeqEnd(seqEnd);
        mop.setSeqName(seq.getName());
                      .getDbResNum());
            } catch (NumberFormatException nfe)
            {
 -            resNum = (pdbRefDb == null) ? Integer.valueOf(residue
 -                    .getDbResNum()) : Integer.valueOf(pdbRefDb
 +            if (pdbRefDb == null || pdbRefDb.getDbResNum().equals("null"))
 +            {
 +              resNum = UNASSIGNED;
 +              continue;
 +            }
 +            resNum = Integer.valueOf(pdbRefDb
                      .getDbResNum().split("[a-zA-Z]")[0]);
              continue;
            }
      }
    }
  
-   /**
-    * 
-    * @param chainId
-    *          Target chain to populate mapping of its atom positions.
-    * @param mapping
-    *          Two dimension array of residue index versus atom position
-    * @throws IllegalArgumentException
-    *           Thrown if chainId or mapping is null
-    * @throws SiftsException
-    */
-   void populateAtomPositions(String chainId, Map<Integer, int[]> mapping)
-           throws IllegalArgumentException, SiftsException
-   {
-     try
-     {
-       PDBChain chain = pdb.findChain(chainId);
-       if (chain == null || mapping == null)
-       {
-         throw new IllegalArgumentException(
-                 "Chain id or mapping must not be null.");
-       }
-       for (int[] map : mapping.values())
-       {
-         if (map[PDB_RES_POS] != UNASSIGNED)
-         {
-           map[PDB_ATOM_POS] = getAtomIndex(map[PDB_RES_POS], chain.atoms);
-         }
-       }
-     } catch (NullPointerException e)
-     {
-       throw new SiftsException(e.getMessage());
-     } catch (Exception e)
-     {
-       throw new SiftsException(e.getMessage());
-     }
-   }
-   /**
-    * 
-    * @param residueIndex
-    *          The residue index used for the search
-    * @param atoms
-    *          A collection of Atom to search
-    * @return atom position for the given residue index
-    */
-   int getAtomIndex(int residueIndex, Collection<Atom> atoms)
-   {
-     if (atoms == null)
-     {
-       throw new IllegalArgumentException(
-               "atoms collection must not be null!");
-     }
-     for (Atom atom : atoms)
-     {
-       if (atom.resNumber == residueIndex)
-       {
-         return atom.atomIndex;
-       }
-     }
-     return UNASSIGNED;
-   }
  
    /**
     * Checks if the residue instance is marked 'Not_observed' or not
    }
  
    @Override
-   public StringBuffer getMappingOutput(MappingOutputPojo mp)
-           throws SiftsException
+   public StringBuffer getMappingOutput(MappingOutputModel mp)
+           throws StructureMappingException
    {
      String seqRes = mp.getSeqResidue();
      String seqName = mp.getSeqName();
      float pid = (float) matchedSeqCount / seqRes.length() * 100;
      if (pid < SiftsSettings.getFailSafePIDThreshold())
      {
-       throw new SiftsException(">>> Low PID detected for SIFTs mapping...");
+       throw new StructureMappingException(
+               ">>> Low PID detected for SIFTs mapping...");
      }
      output.append("Length of alignment = " + seqRes.length()).append(
              NEWLINE);
@@@ -24,6 -24,7 +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;
@@@ -129,16 -130,17 +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,16 @@@ 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,19 -282,16 +284,32 @@@ public class SiftsClientTes
                "A", testSeq, null);
        Assert.assertEquals(testSeq.getStart(), 1);
        Assert.assertEquals(testSeq.getEnd(), 147);
++<<<<<<< HEAD
 +      // Can't do Assert.assertEquals(actualMapping, expectedMapping);
 +      // because this fails in our version of TestNG
 +      Assert.assertEquals(actualMapping.size(), expectedMapping.size());
 +      Iterator<Map.Entry<Integer, int[]>> it = expectedMapping.entrySet()
 +              .iterator();
 +      while (it.hasNext())
 +      {
 +        Map.Entry<Integer, int[]> pair = it.next();
 +        Assert.assertTrue(actualMapping.containsKey(pair.getKey()));
 +        Assert.assertEquals(actualMapping.get(pair.getKey()),
 +                pair.getValue());
 +      }
 +
++=======
+       // Assert.assertEquals(actualMapping, expectedMapping);
+       Assert.assertEquals(actualMapping.size(), expectedMapping.size());
+       // Test key set
+       Assert.assertEquals(actualMapping.keySet(), expectedMapping.keySet());
+       // Test entry set
+       for (int key : expectedMapping.keySet())
+       {
+         Assert.assertTrue(Arrays.equals(expectedMapping.get(key),
+                 actualMapping.get(key)));
+       }
++>>>>>>> f80180a53bf16dc72ecdd4ace0f70c83cb0d274a
      } 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);
    }
@@@ -392,10 -389,11 +407,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);
  
++<<<<<<< HEAD
 +    // Can't do Assert.assertEquals(strucMapping.getMapping(), expectedMapping);
 +    // because this fails in our version of TestNG
 +    Assert.assertEquals(strucMapping.getMapping().size(),
 +            expectedMapping.size());
 +    Iterator<Map.Entry<Integer, int[]>> it = expectedMapping.entrySet()
 +            .iterator();
 +    while (it.hasNext())
 +    {
 +      Map.Entry<Integer, int[]> pair = it.next();
 +      Assert.assertTrue(strucMapping.getMapping()
 +              .containsKey(pair.getKey()));
 +      Assert.assertEquals(strucMapping.getMapping().get(pair.getKey()),
 +              pair.getValue());
++=======
+     // Assert.assertEquals(strucMapping.getMapping(), expectedMapping);
+     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())
+     {
+       Assert.assertTrue(Arrays.equals(expectedMapping.get(key),
+               strucMapping.getMapping().get(key)));
++>>>>>>> f80180a53bf16dc72ecdd4ace0f70c83cb0d274a
      }
    }